From be4eb9b4bbcc2ea932d927b84bf2fcb8cbf32663 Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 01:56:08 -0700 Subject: [PATCH 01/21] translate: README/docs README.md to English, preserve README.cn.md --- README.cn.md | 259 +++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 200 +++++++++++++++++++-------------------- 2 files changed, 357 insertions(+), 102 deletions(-) create mode 100644 README.cn.md diff --git a/README.cn.md b/README.cn.md new file mode 100644 index 000000000..8ad16175b --- /dev/null +++ b/README.cn.md @@ -0,0 +1,259 @@ +NeuCharFramework + + + +# NcfPackageSources + +[![Build Status](https://mysenparc.visualstudio.com/NCF-and-PackageResources/_apis/build/status/NeuCharFramework.NcfPackageSources?branchName=master)](https://mysenparc.visualstudio.com/NCF-and-PackageResources/_build/latest?definitionId=48&branchName=master) + +## 目录 / Table of Contents +- [项目介绍 / Introduction](#项目介绍--introduction) +- [开发环境 / Development Environment](#开发环境--development-environment) +- [快速开始 / Quick Start](#快速开始--quick-start) +- [项目结构 / Project Structure](#项目结构--project-structure) +- [模块加载顺序 / Module Loading Order](#模块加载顺序--module-loading-order) +- [可用的 Nuget 包 / Available NuGet Packages](#可用的-nuget-包--available-nuget-packages) +- [贡献指南 / Contributing](#贡献指南--contributing) +- [许可证 / License](#许可证--license) + +## 项目介绍 / Introduction + +本项目为 [NCF(NeuCharFramework)](https://github.com/NeuCharFramework/NCF) 模板官方包的核心基础库源码。 + +当您使用 [NCF](https://github.com/NeuCharFramework/NCF) 模板开发项目时,核心基础库将为您提供一系列基础能力的支撑,通常情况下您无需关心这些库的具体实现,只需要引用即可。 + +当您需要了解、修改或调试相关基础库时,您可以通过本项目获取源码。 + +[NCF](https://github.com/NeuCharFramework/NCF) 文档地址:[https://doc.ncf.pub/](https://doc.ncf.pub/)。 + +## 开发环境 / Development Environment + +- Visual Studio 2019+ 或 VS Code 最新版本 +- [.NET 8.0](https://dotnet.microsoft.com/download/dotnet/8.0) +- 支持的数据库: + - SQLite + - MySQL + - SQL Server (2012+) + - PostgreSQL + - Oracle + - DM(达梦) + +## 快速开始 / Quick Start + + 1. 克隆仓库 + +```bash +git clone https://github.com/NeuCharFramework/NcfPackageSources.git +``` + +2. 打开解决方案 + +```bash +cd NcfPackageSources +start NcfPackageSources.sln # Windows +open NcfPackageSources.sln # macOS +``` + +3. 还原包 + +```bash +dotnet restore +``` + +4. 还原工作负载(可选) + +```bash +dotnet workload restore +``` + +5. 编译运行 + +```bash +dotnet build +dotnet run +``` + +## 项目结构 / Project Structure + +| 文件夹 / Folder | 说明 / Description | +|--------------|-----------------| +| src/Basic | 必须安装的基础官方库,以 `Separc.Ncf.` 开头
Required basic official libraries, prefixed with `Separc.Ncf.` +| src/Extensions | 可选的扩展包,以 `Senparc.Xncf.` 开头
Optional extension packages, prefixed with `Senparc.Xncf.` +| src/Extensions/System | 系统模块
System modules + +## 模块加载顺序 / Module Loading Order + +使用 `[XncfOrder(x)]` 特性指定模块的加载顺序,为降序排列,数字越大越在前: + +The `[XncfOrder(x)]` attribute specifies the loading order of modules in descending order, larger numbers load first: + +- `0`:默认值,可以不用设置 / Default value, no need to set +- `1` ~ `5000`:需要预加载的重要模块 / Important modules that need preloading +- `5000+`:系统及基础模块 / System and basic modules +- `58xx`:AI 相关基础模块 / AI-related basic modules +- `59xx`:系统底层基础模块 / System underlying basic modules + +## 可用的 Nuget 包 / Available NuGet Packages + +### 基础库 / Basic Libraries + +| 包名 / Package Name | 描述 / Description | 版本 / Version | +|---------------------|-------------------|----------------| +| Senparc.Ncf.XncfBase | XNCF 模块基础库 | [![Senparc.Ncf.XncfBase][badge-Senparc.Ncf.XncfBase]][nuget-Senparc.Ncf.XncfBase] | +| Senparc.Ncf.Core | 核心基础库 | [![Senparc.Ncf.Core][badge-Senparc.Ncf.Core]][nuget-Senparc.Ncf.Core] | +| Senparc.Ncf.DatabasePlant | 数据库组装厂 | [![Senparc.Ncf.DatabasePlant][badge-Senparc.Ncf.DatabasePlant]][nuget-Senparc.Ncf.DatabasePlant] | +| Senparc.Ncf.Log | 日志模块 | [![Senparc.Ncf.Log][badge-Senparc.Ncf.Log]][nuget-Senparc.Ncf.Log] | +| Senparc.Ncf.AreaBase | Area 基础模块 | [![Senparc.Ncf.AreaBase][badge-Senparc.Ncf.AreaBase]][nuget-Senparc.Ncf.AreaBase] | +| Senparc.Ncf.UnitTestExtension | 单元测试扩展 | [![Senparc.Ncf.UnitTestExtension][badge-Senparc.Ncf.UnitTestExtension]][nuget-Senparc.Ncf.UnitTestExtension] | +| Senparc.Ncf.Mvc.UI | MVC UI 组件 | [![Senparc.Ncf.Mvc.UI][badge-Senparc.Ncf.Mvc.UI]][nuget-Senparc.Ncf.Mvc.UI] | +| Senparc.Ncf.Database | 数据库基础库 | [![Senparc.Ncf.Database][badge-Senparc.Ncf.Database]][nuget-Senparc.Ncf.Database] | +| Senparc.Ncf.Database.MySql.Backup | MySQL 数据库备份 | [![Senparc.Ncf.Database.MySql.Backup][badge-Senparc.Ncf.Database.MySql.Backup]][nuget-Senparc.Ncf.Database.MySql.Backup] | +| Senparc.Ncf.Database.MySql | MySQL 数据库支持 | [![Senparc.Ncf.Database.MySql][badge-Senparc.Ncf.Database.MySql]][nuget-Senparc.Ncf.Database.MySql] | +| Senparc.Ncf.Database.Oracle | Oracle 数据库支持 | [![Senparc.Ncf.Database.Oracle][badge-Senparc.Ncf.Database.Oracle]][nuget-Senparc.Ncf.Database.Oracle] | +| Senparc.Ncf.Database.PostgreSQL | PostgreSQL 数据库支持 | [![Senparc.Ncf.Database.PostgreSQL][badge-Senparc.Ncf.Database.PostgreSQL]][nuget-Senparc.Ncf.Database.PostgreSQL] | +| Senparc.Ncf.Database.Sqlite | SQLite 数据库支持 | [![Senparc.Ncf.Database.Sqlite][badge-Senparc.Ncf.Database.Sqlite]][nuget-Senparc.Ncf.Database.Sqlite] | +| Senparc.Ncf.Database.SqlServer | SQL Server 数据库支持 | [![Senparc.Ncf.Database.SqlServer][badge-Senparc.Ncf.Database.SqlServer]][nuget-Senparc.Ncf.Database.SqlServer] | +| Senparc.Ncf.Database.Dm | 达梦(DM)数据库支持 | [![Senparc.Ncf.Database.Dm][badge-Senparc.Ncf.Database.Dm]][nuget-Senparc.Ncf.Database.Dm] | +| Senparc.Ncf.Database.InMemory | 内存数据库支持 | [![Senparc.Ncf.Database.InMemory][badge-Senparc.Ncf.Database.InMemory]][nuget-Senparc.Ncf.Database.InMemory] | +| Senparc.Ncf.Repository | 仓储层 | [![Senparc.Ncf.Repository][badge-Senparc.Ncf.Repository]][nuget-Senparc.Ncf.Repository] | +| Senparc.Ncf.Service | 服务层 | [![Senparc.Ncf.Service][badge-Senparc.Ncf.Service]][nuget-Senparc.Ncf.Service] | +| Senparc.Ncf.SMS | 短信服务 | [![Senparc.Ncf.SMS][badge-Senparc.Ncf.SMS]][nuget-Senparc.Ncf.SMS] | +| Senparc.Ncf.Utility | 工具类库 | [![Senparc.Ncf.Utility][badge-Senparc.Ncf.Utility]][nuget-Senparc.Ncf.Utility] | + +### 扩展 XNCF 模块 / Extension XNCF Modules + +| 包名 / Package Name | 描述 / Description | 版本 / Version | +|---------------------|-------------------|----------------| +| Senparc.Xncf.AgentsManager | 智能体管理器 | [![Senparc.Xncf.AgentsManager][badge-Senparc.Xncf.AgentsManager]][nuget-Senparc.Xncf.AgentsManager] | +| Senparc.Xncf.AIAgentsHub | AI 智能体中心 | [![Senparc.Xncf.AIAgentsHub][badge-Senparc.Xncf.AIAgentsHub]][nuget-Senparc.Xncf.AIAgentsHub] | +| Senparc.Xncf.AIKernel | AI 内核 | [![Senparc.Xncf.AIKernel][badge-Senparc.Xncf.AIKernel]][nuget-Senparc.Xncf.AIKernel] | +| Senparc.Xncf.ChangeNamespace | 命名空间修改工具 | [![Senparc.Xncf.ChangeNamespace][badge-Senparc.Xncf.ChangeNamespace]][nuget-Senparc.Xncf.ChangeNamespace] | +| Senparc.Xncf.Dapr | Dapr 分布式运行时支持 | [![Senparc.Xncf.Dapr][badge-Senparc.Xncf.Dapr]][nuget-Senparc.Xncf.Dapr] | +| Senparc.Xncf.DatabaseToolkit | 数据库工具包模块 | [![Senparc.Xncf.DatabaseToolkit][badge-Senparc.Xncf.DatabaseToolkit]][nuget-Senparc.Xncf.DatabaseToolkit] | +| Senparc.Xncf.DynamicData | 动态数据基础模块 | [![Senparc.Xncf.DynamicData][badge-Senparc.Xncf.DynamicData]][nuget-Senparc.Xncf.DynamicData] | +| Senparc.Xncf.FileManager | 文件管理模块 | [![Senparc.Xncf.FileManager][badge-Senparc.Xncf.FileManager]][nuget-Senparc.Xncf.FileManager] | +| Senparc.Xncf.KnowledgeBase | AI 知识库 | [![Senparc.Xncf.KnowledgeBase][badge-Senparc.Xncf.KnowledgeBase]][nuget-Senparc.Xncf.KnowledgeBase] | +| Senparc.Xncf.MCP | MCF 多租户微服务平台 | [![Senparc.Xncf.MCP][badge-Senparc.Xncf.MCP]][nuget-Senparc.Xncf.MCP] | +| Senparc.Xncf.PromptRange | AI 提示词靶场 | [![Senparc.Xncf.PromptRange][badge-Senparc.Xncf.PromptRange]][nuget-Senparc.Xncf.PromptRange] | +| Senparc.Xncf.SenMapic | SenMapic 爬虫模块 | [![Senparc.Xncf.SenMapic][badge-Senparc.Xncf.SenMapic]][nuget-Senparc.Xncf.SenMapic] | +| Senparc.Xncf.Swagger | Swagger 接口文档 | [![Senparc.Xncf.Swagger][badge-Senparc.Xncf.Swagger]][nuget-Senparc.Xncf.Swagger] | +| Senparc.Xncf.Terminal | 终端命令模块 | [![Senparc.Xncf.Terminal][badge-Senparc.Xncf.Terminal]][nuget-Senparc.Xncf.Terminal] | +| Senparc.Xncf.XncfBuilder | XNCF 模块生成器 | [![Senparc.Xncf.XncfBuilder][badge-Senparc.Xncf.XncfBuilder]][nuget-Senparc.Xncf.XncfBuilder] | +| Senparc.Xncf.AreasBase | 系统区域基础模块 | [![Senparc.Xncf.AreasBase][badge-Senparc.Xncf.AreasBase]][nuget-Senparc.Xncf.AreasBase] | +| Senparc.Xncf.Menu | 菜单管理模块 | [![Senparc.Xncf.Menu][badge-Senparc.Xncf.Menu]][nuget-Senparc.Xncf.Menu] | +| Senparc.Xncf.SystemCore | 系统核心模块 | [![Senparc.Xncf.SystemCore][badge-Senparc.Xncf.SystemCore]][nuget-Senparc.Xncf.SystemCore] | +| Senparc.Xncf.SystemManager | 系统管理模块 | [![Senparc.Xncf.SystemManager][badge-Senparc.Xncf.SystemManager]][nuget-Senparc.Xncf.SystemManager] | +| Senparc.Xncf.SystemPermission | 系统权限模块 | [![Senparc.Xncf.SystemPermission][badge-Senparc.Xncf.SystemPermission]][nuget-Senparc.Xncf.SystemPermission] | +| Senparc.Xncf.Tenant | 多租户管理模块 | [![Senparc.Xncf.Tenant][badge-Senparc.Xncf.Tenant]][nuget-Senparc.Xncf.Tenant] | +| Senparc.Xncf.Tenant.Interface | 多租户接口模块 | [![Senparc.Xncf.Tenant.Interface][badge-Senparc.Xncf.Tenant.Interface]][nuget-Senparc.Xncf.Tenant.Interface] | +| Senparc.Xncf.XncfModuleManager | XNCF 模块管理器 | [![Senparc.Xncf.XncfModuleManager][badge-Senparc.Xncf.XncfModuleManager]][nuget-Senparc.Xncf.XncfModuleManager] | + +## 贡献指南 / Contributing + +我们欢迎开发者为 NCF 贡献代码。如果您想要贡献,请: + +We welcome developers to contribute to NCF. If you want to contribute, please: + +1. Fork 本仓库 / Fork this repository +2. 创建您的特性分支 / Create your feature branch +3. 提交您的改动 / Commit your changes +4. 推送到分支 / Push to the branch +5. 创建 Pull Request / Create a Pull Request + +## 许可证 / License + +Apache License Version 2.0 + +详细请参考 / For details, please refer to: [LICENSE](LICENSE) + +--- + + +[badge-Senparc.Ncf.XncfBase]: https://img.shields.io/nuget/v/Senparc.Ncf.XncfBase.svg +[nuget-Senparc.Ncf.XncfBase]: https://www.nuget.org/packages/Senparc.Ncf.XncfBase +[badge-Senparc.Ncf.Core]: https://img.shields.io/nuget/v/Senparc.Ncf.Core.svg +[nuget-Senparc.Ncf.Core]: https://www.nuget.org/packages/Senparc.Ncf.Core +[badge-Senparc.Ncf.DatabasePlant]: https://img.shields.io/nuget/v/Senparc.Ncf.DatabasePlant.svg +[nuget-Senparc.Ncf.DatabasePlant]: https://www.nuget.org/packages/Senparc.Ncf.DatabasePlant +[badge-Senparc.Ncf.Log]: https://img.shields.io/nuget/v/Senparc.Ncf.Log.svg +[nuget-Senparc.Ncf.Log]: https://www.nuget.org/packages/Senparc.Ncf.Log +[badge-Senparc.Ncf.AreaBase]: https://img.shields.io/nuget/v/Senparc.Ncf.AreaBase.svg +[nuget-Senparc.Ncf.AreaBase]: https://www.nuget.org/packages/Senparc.Ncf.AreaBase +[badge-Senparc.Ncf.UnitTestExtension]: https://img.shields.io/nuget/v/Senparc.Ncf.UnitTestExtension.svg +[nuget-Senparc.Ncf.UnitTestExtension]: https://www.nuget.org/packages/Senparc.Ncf.UnitTestExtension +[badge-Senparc.Ncf.Mvc.UI]: https://img.shields.io/nuget/v/Senparc.Ncf.Mvc.UI.svg +[nuget-Senparc.Ncf.Mvc.UI]: https://www.nuget.org/packages/Senparc.Ncf.Mvc.UI +[badge-Senparc.Ncf.Database]: https://img.shields.io/nuget/v/Senparc.Ncf.Database.svg +[nuget-Senparc.Ncf.Database]: https://www.nuget.org/packages/Senparc.Ncf.Database +[badge-Senparc.Ncf.Database.MySql.Backup]: https://img.shields.io/nuget/v/Senparc.Ncf.Database.MySql.Backup.svg +[nuget-Senparc.Ncf.Database.MySql.Backup]: https://www.nuget.org/packages/Senparc.Ncf.Database.MySql.Backup +[badge-Senparc.Ncf.Database.MySql]: https://img.shields.io/nuget/v/Senparc.Ncf.Database.MySql.svg +[nuget-Senparc.Ncf.Database.MySql]: https://www.nuget.org/packages/Senparc.Ncf.Database.MySql +[badge-Senparc.Ncf.Database.Oracle]: https://img.shields.io/nuget/v/Senparc.Ncf.Database.Oracle.svg +[nuget-Senparc.Ncf.Database.Oracle]: https://www.nuget.org/packages/Senparc.Ncf.Database.Oracle +[badge-Senparc.Ncf.Database.PostgreSQL]: https://img.shields.io/nuget/v/Senparc.Ncf.Database.PostgreSQL.svg +[nuget-Senparc.Ncf.Database.PostgreSQL]: https://www.nuget.org/packages/Senparc.Ncf.Database.PostgreSQL +[badge-Senparc.Ncf.Database.Sqlite]: https://img.shields.io/nuget/v/Senparc.Ncf.Database.Sqlite.svg +[nuget-Senparc.Ncf.Database.Sqlite]: https://www.nuget.org/packages/Senparc.Ncf.Database.Sqlite +[badge-Senparc.Ncf.Database.SqlServer]: https://img.shields.io/nuget/v/Senparc.Ncf.Database.SqlServer.svg +[nuget-Senparc.Ncf.Database.SqlServer]: https://www.nuget.org/packages/Senparc.Ncf.Database.SqlServer +[badge-Senparc.Ncf.Database.Dm]: https://img.shields.io/nuget/v/Senparc.Ncf.Database.Dm.svg +[nuget-Senparc.Ncf.Database.Dm]: https://www.nuget.org/packages/Senparc.Ncf.Database.Dm +[badge-Senparc.Ncf.Database.InMemory]: https://img.shields.io/nuget/v/Senparc.Ncf.Database.InMemory.svg +[nuget-Senparc.Ncf.Database.InMemory]: https://www.nuget.org/packages/Senparc.Ncf.Database.InMemory +[badge-Senparc.Ncf.Repository]: https://img.shields.io/nuget/v/Senparc.Ncf.Repository.svg +[nuget-Senparc.Ncf.Repository]: https://www.nuget.org/packages/Senparc.Ncf.Repository +[badge-Senparc.Ncf.Service]: https://img.shields.io/nuget/v/Senparc.Ncf.Service.svg +[nuget-Senparc.Ncf.Service]: https://www.nuget.org/packages/Senparc.Ncf.Service +[badge-Senparc.Ncf.SMS]: https://img.shields.io/nuget/v/Senparc.Ncf.SMS.svg +[nuget-Senparc.Ncf.SMS]: https://www.nuget.org/packages/Senparc.Ncf.SMS +[badge-Senparc.Ncf.Utility]: https://img.shields.io/nuget/v/Senparc.Ncf.Utility.svg +[nuget-Senparc.Ncf.Utility]: https://www.nuget.org/packages/Senparc.Ncf.Utility + +[badge-Senparc.Xncf.AgentsManager]: https://img.shields.io/nuget/v/Senparc.Xncf.AgentsManager.svg +[nuget-Senparc.Xncf.AgentsManager]: https://www.nuget.org/packages/Senparc.Xncf.AgentsManager +[badge-Senparc.Xncf.AIAgentsHub]: https://img.shields.io/nuget/v/Senparc.Xncf.AIAgentsHub.svg +[nuget-Senparc.Xncf.AIAgentsHub]: https://www.nuget.org/packages/Senparc.Xncf.AIAgentsHub +[badge-Senparc.Xncf.AIKernel]: https://img.shields.io/nuget/v/Senparc.Xncf.AIKernel.svg +[nuget-Senparc.Xncf.AIKernel]: https://www.nuget.org/packages/Senparc.Xncf.AIKernel +[badge-Senparc.Xncf.ChangeNamespace]: https://img.shields.io/nuget/v/Senparc.Xncf.ChangeNamespace.svg +[nuget-Senparc.Xncf.ChangeNamespace]: https://www.nuget.org/packages/Senparc.Xncf.ChangeNamespace +[badge-Senparc.Xncf.Dapr]: https://img.shields.io/nuget/v/Senparc.Xncf.Dapr.svg +[nuget-Senparc.Xncf.Dapr]: https://www.nuget.org/packages/Senparc.Xncf.Dapr +[badge-Senparc.Xncf.DatabaseToolkit]: https://img.shields.io/nuget/v/Senparc.Xncf.DatabaseToolkit.svg +[nuget-Senparc.Xncf.DatabaseToolkit]: https://www.nuget.org/packages/Senparc.Xncf.DatabaseToolkit +[badge-Senparc.Xncf.DynamicData]: https://img.shields.io/nuget/v/Senparc.Xncf.DynamicData.svg +[nuget-Senparc.Xncf.DynamicData]: https://www.nuget.org/packages/Senparc.Xncf.DynamicData +[badge-Senparc.Xncf.FileManager]: https://img.shields.io/nuget/v/Senparc.Xncf.FileManager.svg +[nuget-Senparc.Xncf.FileManager]: https://www.nuget.org/packages/Senparc.Xncf.FileManager +[badge-Senparc.Xncf.KnowledgeBase]: https://img.shields.io/nuget/v/Senparc.Xncf.KnowledgeBase.svg +[nuget-Senparc.Xncf.KnowledgeBase]: https://www.nuget.org/packages/Senparc.Xncf.KnowledgeBase +[badge-Senparc.Xncf.MCP]: https://img.shields.io/nuget/v/Senparc.Xncf.MCP.svg +[nuget-Senparc.Xncf.MCP]: https://www.nuget.org/packages/Senparc.Xncf.MCP +[badge-Senparc.Xncf.PromptRange]: https://img.shields.io/nuget/v/Senparc.Xncf.PromptRange.svg +[nuget-Senparc.Xncf.PromptRange]: https://www.nuget.org/packages/Senparc.Xncf.PromptRange +[badge-Senparc.Xncf.SenMapic]: https://img.shields.io/nuget/v/Senparc.Xncf.SenMapic.svg +[nuget-Senparc.Xncf.SenMapic]: https://www.nuget.org/packages/Senparc.Xncf.SenMapic +[badge-Senparc.Xncf.Swagger]: https://img.shields.io/nuget/v/Senparc.Xncf.Swagger.svg +[nuget-Senparc.Xncf.Swagger]: https://www.nuget.org/packages/Senparc.Xncf.Swagger +[badge-Senparc.Xncf.Terminal]: https://img.shields.io/nuget/v/Senparc.Xncf.Terminal.svg +[nuget-Senparc.Xncf.Terminal]: https://www.nuget.org/packages/Senparc.Xncf.Terminal +[badge-Senparc.Xncf.XncfBuilder]: https://img.shields.io/nuget/v/Senparc.Xncf.XncfBuilder.svg +[nuget-Senparc.Xncf.XncfBuilder]: https://www.nuget.org/packages/Senparc.Xncf.XncfBuilder +[badge-Senparc.Xncf.AreasBase]: https://img.shields.io/nuget/v/Senparc.Xncf.AreasBase.svg +[nuget-Senparc.Xncf.AreasBase]: https://www.nuget.org/packages/Senparc.Xncf.AreasBase +[badge-Senparc.Xncf.Menu]: https://img.shields.io/nuget/v/Senparc.Xncf.Menu.svg +[nuget-Senparc.Xncf.Menu]: https://www.nuget.org/packages/Senparc.Xncf.Menu +[badge-Senparc.Xncf.SystemCore]: https://img.shields.io/nuget/v/Senparc.Xncf.SystemCore.svg +[nuget-Senparc.Xncf.SystemCore]: https://www.nuget.org/packages/Senparc.Xncf.SystemCore +[badge-Senparc.Xncf.SystemManager]: https://img.shields.io/nuget/v/Senparc.Xncf.SystemManager.svg +[nuget-Senparc.Xncf.SystemManager]: https://www.nuget.org/packages/Senparc.Xncf.SystemManager +[badge-Senparc.Xncf.SystemPermission]: https://img.shields.io/nuget/v/Senparc.Xncf.SystemPermission.svg +[nuget-Senparc.Xncf.SystemPermission]: https://www.nuget.org/packages/Senparc.Xncf.SystemPermission +[badge-Senparc.Xncf.Tenant]: https://img.shields.io/nuget/v/Senparc.Xncf.Tenant.svg +[nuget-Senparc.Xncf.Tenant]: https://www.nuget.org/packages/Senparc.Xncf.Tenant +[badge-Senparc.Xncf.Tenant.Interface]: https://img.shields.io/nuget/v/Senparc.Xncf.Tenant.Interface.svg +[nuget-Senparc.Xncf.Tenant.Interface]: https://www.nuget.org/packages/Senparc.Xncf.Tenant.Interface +[badge-Senparc.Xncf.XncfModuleManager]: https://img.shields.io/nuget/v/Senparc.Xncf.XncfModuleManager.svg +[nuget-Senparc.Xncf.XncfModuleManager]: https://www.nuget.org/packages/Senparc.Xncf.XncfModuleManager + diff --git a/README.md b/README.md index 8ad16175b..37b92b8f8 100644 --- a/README.md +++ b/README.md @@ -6,47 +6,47 @@ [![Build Status](https://mysenparc.visualstudio.com/NCF-and-PackageResources/_apis/build/status/NeuCharFramework.NcfPackageSources?branchName=master)](https://mysenparc.visualstudio.com/NCF-and-PackageResources/_build/latest?definitionId=48&branchName=master) -## 目录 / Table of Contents -- [项目介绍 / Introduction](#项目介绍--introduction) -- [开发环境 / Development Environment](#开发环境--development-environment) -- [快速开始 / Quick Start](#快速开始--quick-start) -- [项目结构 / Project Structure](#项目结构--project-structure) -- [模块加载顺序 / Module Loading Order](#模块加载顺序--module-loading-order) -- [可用的 Nuget 包 / Available NuGet Packages](#可用的-nuget-包--available-nuget-packages) -- [贡献指南 / Contributing](#贡献指南--contributing) -- [许可证 / License](#许可证--license) +## Table of Contents +- [Introduction](#introduction) +- [Development Environment](#development-environment) +- [Quick Start](#quick-start) +- [Project Structure](#project-structure) +- [Module Loading Order](#module-loading-order) +- [Available NuGet Packages](#available-nuget-packages) +- [Contributing](#contributing) +- [License](#license) -## 项目介绍 / Introduction +## Introduction -本项目为 [NCF(NeuCharFramework)](https://github.com/NeuCharFramework/NCF) 模板官方包的核心基础库源码。 +This project contains the core library source code for the official packages of the [NCF (NeuCharFramework)](https://github.com/NeuCharFramework/NCF) template. -当您使用 [NCF](https://github.com/NeuCharFramework/NCF) 模板开发项目时,核心基础库将为您提供一系列基础能力的支撑,通常情况下您无需关心这些库的具体实现,只需要引用即可。 +When you develop a project using the [NCF](https://github.com/NeuCharFramework/NCF) template, the core libraries provide a set of foundational capabilities. In most cases, you do not need to care about their internal implementation — just add a reference. -当您需要了解、修改或调试相关基础库时,您可以通过本项目获取源码。 +When you need to understand, modify, or debug these core libraries, you can obtain the source code from this project. -[NCF](https://github.com/NeuCharFramework/NCF) 文档地址:[https://doc.ncf.pub/](https://doc.ncf.pub/)。 +[NCF](https://github.com/NeuCharFramework/NCF) documentation: [https://doc.ncf.pub/](https://doc.ncf.pub/). -## 开发环境 / Development Environment +## Development Environment -- Visual Studio 2019+ 或 VS Code 最新版本 +- Visual Studio 2019+ or the latest version of VS Code - [.NET 8.0](https://dotnet.microsoft.com/download/dotnet/8.0) -- 支持的数据库: +- Supported databases: - SQLite - MySQL - SQL Server (2012+) - PostgreSQL - Oracle - - DM(达梦) + - DM (Dameng) -## 快速开始 / Quick Start +## Quick Start - 1. 克隆仓库 +1. Clone the repository ```bash git clone https://github.com/NeuCharFramework/NcfPackageSources.git ``` -2. 打开解决方案 +2. Open the solution ```bash cd NcfPackageSources @@ -54,117 +54,113 @@ start NcfPackageSources.sln # Windows open NcfPackageSources.sln # macOS ``` -3. 还原包 +3. Restore packages ```bash dotnet restore ``` -4. 还原工作负载(可选) +4. Restore workloads (optional) ```bash dotnet workload restore ``` -5. 编译运行 +5. Build and run ```bash dotnet build dotnet run ``` -## 项目结构 / Project Structure +## Project Structure -| 文件夹 / Folder | 说明 / Description | -|--------------|-----------------| -| src/Basic | 必须安装的基础官方库,以 `Separc.Ncf.` 开头
Required basic official libraries, prefixed with `Separc.Ncf.` -| src/Extensions | 可选的扩展包,以 `Senparc.Xncf.` 开头
Optional extension packages, prefixed with `Senparc.Xncf.` -| src/Extensions/System | 系统模块
System modules +| Folder | Description | +|--------|-------------| +| src/Basic | Required basic official libraries, prefixed with `Senparc.Ncf.` | +| src/Extensions | Optional extension packages, prefixed with `Senparc.Xncf.` | +| src/Extensions/System | System modules | -## 模块加载顺序 / Module Loading Order +## Module Loading Order -使用 `[XncfOrder(x)]` 特性指定模块的加载顺序,为降序排列,数字越大越在前: +Use the `[XncfOrder(x)]` attribute to specify the loading order of modules. The order is descending — higher numbers load first: -The `[XncfOrder(x)]` attribute specifies the loading order of modules in descending order, larger numbers load first: - -- `0`:默认值,可以不用设置 / Default value, no need to set -- `1` ~ `5000`:需要预加载的重要模块 / Important modules that need preloading -- `5000+`:系统及基础模块 / System and basic modules -- `58xx`:AI 相关基础模块 / AI-related basic modules -- `59xx`:系统底层基础模块 / System underlying basic modules +- `0`: Default value, no need to set +- `1` ~ `5000`: Important modules that need preloading +- `5000+`: System and basic modules +- `58xx`: AI-related basic modules +- `59xx`: System underlying basic modules ## 可用的 Nuget 包 / Available NuGet Packages -### 基础库 / Basic Libraries - -| 包名 / Package Name | 描述 / Description | 版本 / Version | -|---------------------|-------------------|----------------| -| Senparc.Ncf.XncfBase | XNCF 模块基础库 | [![Senparc.Ncf.XncfBase][badge-Senparc.Ncf.XncfBase]][nuget-Senparc.Ncf.XncfBase] | -| Senparc.Ncf.Core | 核心基础库 | [![Senparc.Ncf.Core][badge-Senparc.Ncf.Core]][nuget-Senparc.Ncf.Core] | -| Senparc.Ncf.DatabasePlant | 数据库组装厂 | [![Senparc.Ncf.DatabasePlant][badge-Senparc.Ncf.DatabasePlant]][nuget-Senparc.Ncf.DatabasePlant] | -| Senparc.Ncf.Log | 日志模块 | [![Senparc.Ncf.Log][badge-Senparc.Ncf.Log]][nuget-Senparc.Ncf.Log] | -| Senparc.Ncf.AreaBase | Area 基础模块 | [![Senparc.Ncf.AreaBase][badge-Senparc.Ncf.AreaBase]][nuget-Senparc.Ncf.AreaBase] | -| Senparc.Ncf.UnitTestExtension | 单元测试扩展 | [![Senparc.Ncf.UnitTestExtension][badge-Senparc.Ncf.UnitTestExtension]][nuget-Senparc.Ncf.UnitTestExtension] | -| Senparc.Ncf.Mvc.UI | MVC UI 组件 | [![Senparc.Ncf.Mvc.UI][badge-Senparc.Ncf.Mvc.UI]][nuget-Senparc.Ncf.Mvc.UI] | -| Senparc.Ncf.Database | 数据库基础库 | [![Senparc.Ncf.Database][badge-Senparc.Ncf.Database]][nuget-Senparc.Ncf.Database] | -| Senparc.Ncf.Database.MySql.Backup | MySQL 数据库备份 | [![Senparc.Ncf.Database.MySql.Backup][badge-Senparc.Ncf.Database.MySql.Backup]][nuget-Senparc.Ncf.Database.MySql.Backup] | -| Senparc.Ncf.Database.MySql | MySQL 数据库支持 | [![Senparc.Ncf.Database.MySql][badge-Senparc.Ncf.Database.MySql]][nuget-Senparc.Ncf.Database.MySql] | -| Senparc.Ncf.Database.Oracle | Oracle 数据库支持 | [![Senparc.Ncf.Database.Oracle][badge-Senparc.Ncf.Database.Oracle]][nuget-Senparc.Ncf.Database.Oracle] | -| Senparc.Ncf.Database.PostgreSQL | PostgreSQL 数据库支持 | [![Senparc.Ncf.Database.PostgreSQL][badge-Senparc.Ncf.Database.PostgreSQL]][nuget-Senparc.Ncf.Database.PostgreSQL] | -| Senparc.Ncf.Database.Sqlite | SQLite 数据库支持 | [![Senparc.Ncf.Database.Sqlite][badge-Senparc.Ncf.Database.Sqlite]][nuget-Senparc.Ncf.Database.Sqlite] | -| Senparc.Ncf.Database.SqlServer | SQL Server 数据库支持 | [![Senparc.Ncf.Database.SqlServer][badge-Senparc.Ncf.Database.SqlServer]][nuget-Senparc.Ncf.Database.SqlServer] | -| Senparc.Ncf.Database.Dm | 达梦(DM)数据库支持 | [![Senparc.Ncf.Database.Dm][badge-Senparc.Ncf.Database.Dm]][nuget-Senparc.Ncf.Database.Dm] | -| Senparc.Ncf.Database.InMemory | 内存数据库支持 | [![Senparc.Ncf.Database.InMemory][badge-Senparc.Ncf.Database.InMemory]][nuget-Senparc.Ncf.Database.InMemory] | -| Senparc.Ncf.Repository | 仓储层 | [![Senparc.Ncf.Repository][badge-Senparc.Ncf.Repository]][nuget-Senparc.Ncf.Repository] | -| Senparc.Ncf.Service | 服务层 | [![Senparc.Ncf.Service][badge-Senparc.Ncf.Service]][nuget-Senparc.Ncf.Service] | -| Senparc.Ncf.SMS | 短信服务 | [![Senparc.Ncf.SMS][badge-Senparc.Ncf.SMS]][nuget-Senparc.Ncf.SMS] | -| Senparc.Ncf.Utility | 工具类库 | [![Senparc.Ncf.Utility][badge-Senparc.Ncf.Utility]][nuget-Senparc.Ncf.Utility] | - -### 扩展 XNCF 模块 / Extension XNCF Modules - -| 包名 / Package Name | 描述 / Description | 版本 / Version | -|---------------------|-------------------|----------------| -| Senparc.Xncf.AgentsManager | 智能体管理器 | [![Senparc.Xncf.AgentsManager][badge-Senparc.Xncf.AgentsManager]][nuget-Senparc.Xncf.AgentsManager] | -| Senparc.Xncf.AIAgentsHub | AI 智能体中心 | [![Senparc.Xncf.AIAgentsHub][badge-Senparc.Xncf.AIAgentsHub]][nuget-Senparc.Xncf.AIAgentsHub] | -| Senparc.Xncf.AIKernel | AI 内核 | [![Senparc.Xncf.AIKernel][badge-Senparc.Xncf.AIKernel]][nuget-Senparc.Xncf.AIKernel] | -| Senparc.Xncf.ChangeNamespace | 命名空间修改工具 | [![Senparc.Xncf.ChangeNamespace][badge-Senparc.Xncf.ChangeNamespace]][nuget-Senparc.Xncf.ChangeNamespace] | -| Senparc.Xncf.Dapr | Dapr 分布式运行时支持 | [![Senparc.Xncf.Dapr][badge-Senparc.Xncf.Dapr]][nuget-Senparc.Xncf.Dapr] | -| Senparc.Xncf.DatabaseToolkit | 数据库工具包模块 | [![Senparc.Xncf.DatabaseToolkit][badge-Senparc.Xncf.DatabaseToolkit]][nuget-Senparc.Xncf.DatabaseToolkit] | -| Senparc.Xncf.DynamicData | 动态数据基础模块 | [![Senparc.Xncf.DynamicData][badge-Senparc.Xncf.DynamicData]][nuget-Senparc.Xncf.DynamicData] | -| Senparc.Xncf.FileManager | 文件管理模块 | [![Senparc.Xncf.FileManager][badge-Senparc.Xncf.FileManager]][nuget-Senparc.Xncf.FileManager] | -| Senparc.Xncf.KnowledgeBase | AI 知识库 | [![Senparc.Xncf.KnowledgeBase][badge-Senparc.Xncf.KnowledgeBase]][nuget-Senparc.Xncf.KnowledgeBase] | -| Senparc.Xncf.MCP | MCF 多租户微服务平台 | [![Senparc.Xncf.MCP][badge-Senparc.Xncf.MCP]][nuget-Senparc.Xncf.MCP] | -| Senparc.Xncf.PromptRange | AI 提示词靶场 | [![Senparc.Xncf.PromptRange][badge-Senparc.Xncf.PromptRange]][nuget-Senparc.Xncf.PromptRange] | -| Senparc.Xncf.SenMapic | SenMapic 爬虫模块 | [![Senparc.Xncf.SenMapic][badge-Senparc.Xncf.SenMapic]][nuget-Senparc.Xncf.SenMapic] | -| Senparc.Xncf.Swagger | Swagger 接口文档 | [![Senparc.Xncf.Swagger][badge-Senparc.Xncf.Swagger]][nuget-Senparc.Xncf.Swagger] | -| Senparc.Xncf.Terminal | 终端命令模块 | [![Senparc.Xncf.Terminal][badge-Senparc.Xncf.Terminal]][nuget-Senparc.Xncf.Terminal] | -| Senparc.Xncf.XncfBuilder | XNCF 模块生成器 | [![Senparc.Xncf.XncfBuilder][badge-Senparc.Xncf.XncfBuilder]][nuget-Senparc.Xncf.XncfBuilder] | -| Senparc.Xncf.AreasBase | 系统区域基础模块 | [![Senparc.Xncf.AreasBase][badge-Senparc.Xncf.AreasBase]][nuget-Senparc.Xncf.AreasBase] | -| Senparc.Xncf.Menu | 菜单管理模块 | [![Senparc.Xncf.Menu][badge-Senparc.Xncf.Menu]][nuget-Senparc.Xncf.Menu] | -| Senparc.Xncf.SystemCore | 系统核心模块 | [![Senparc.Xncf.SystemCore][badge-Senparc.Xncf.SystemCore]][nuget-Senparc.Xncf.SystemCore] | -| Senparc.Xncf.SystemManager | 系统管理模块 | [![Senparc.Xncf.SystemManager][badge-Senparc.Xncf.SystemManager]][nuget-Senparc.Xncf.SystemManager] | -| Senparc.Xncf.SystemPermission | 系统权限模块 | [![Senparc.Xncf.SystemPermission][badge-Senparc.Xncf.SystemPermission]][nuget-Senparc.Xncf.SystemPermission] | -| Senparc.Xncf.Tenant | 多租户管理模块 | [![Senparc.Xncf.Tenant][badge-Senparc.Xncf.Tenant]][nuget-Senparc.Xncf.Tenant] | -| Senparc.Xncf.Tenant.Interface | 多租户接口模块 | [![Senparc.Xncf.Tenant.Interface][badge-Senparc.Xncf.Tenant.Interface]][nuget-Senparc.Xncf.Tenant.Interface] | -| Senparc.Xncf.XncfModuleManager | XNCF 模块管理器 | [![Senparc.Xncf.XncfModuleManager][badge-Senparc.Xncf.XncfModuleManager]][nuget-Senparc.Xncf.XncfModuleManager] | - -## 贡献指南 / Contributing - -我们欢迎开发者为 NCF 贡献代码。如果您想要贡献,请: +### Basic Libraries + +| Package Name | Description | Version | +|---|---|---| +| Senparc.Ncf.XncfBase | XNCF Module Base Library | [![Senparc.Ncf.XncfBase][badge-Senparc.Ncf.XncfBase]][nuget-Senparc.Ncf.XncfBase] | +| Senparc.Ncf.Core | Core Base Library | [![Senparc.Ncf.Core][badge-Senparc.Ncf.Core]][nuget-Senparc.Ncf.Core] | +| Senparc.Ncf.DatabasePlant | Database Assembly Plant | [![Senparc.Ncf.DatabasePlant][badge-Senparc.Ncf.DatabasePlant]][nuget-Senparc.Ncf.DatabasePlant] | +| Senparc.Ncf.Log | Logging Module | [![Senparc.Ncf.Log][badge-Senparc.Ncf.Log]][nuget-Senparc.Ncf.Log] | +| Senparc.Ncf.AreaBase | Area Base Module | [![Senparc.Ncf.AreaBase][badge-Senparc.Ncf.AreaBase]][nuget-Senparc.Ncf.AreaBase] | +| Senparc.Ncf.UnitTestExtension | Unit Test Extensions | [![Senparc.Ncf.UnitTestExtension][badge-Senparc.Ncf.UnitTestExtension]][nuget-Senparc.Ncf.UnitTestExtension] | +| Senparc.Ncf.Mvc.UI | MVC UI Components | [![Senparc.Ncf.Mvc.UI][badge-Senparc.Ncf.Mvc.UI]][nuget-Senparc.Ncf.Mvc.UI] | +| Senparc.Ncf.Database | Database Base Library | [![Senparc.Ncf.Database][badge-Senparc.Ncf.Database]][nuget-Senparc.Ncf.Database] | +| Senparc.Ncf.Database.MySql.Backup | MySQL Database Backup | [![Senparc.Ncf.Database.MySql.Backup][badge-Senparc.Ncf.Database.MySql.Backup]][nuget-Senparc.Ncf.Database.MySql.Backup] | +| Senparc.Ncf.Database.MySql | MySQL Database Support | [![Senparc.Ncf.Database.MySql][badge-Senparc.Ncf.Database.MySql]][nuget-Senparc.Ncf.Database.MySql] | +| Senparc.Ncf.Database.Oracle | Oracle Database Support | [![Senparc.Ncf.Database.Oracle][badge-Senparc.Ncf.Database.Oracle]][nuget-Senparc.Ncf.Database.Oracle] | +| Senparc.Ncf.Database.PostgreSQL | PostgreSQL Database Support | [![Senparc.Ncf.Database.PostgreSQL][badge-Senparc.Ncf.Database.PostgreSQL]][nuget-Senparc.Ncf.Database.PostgreSQL] | +| Senparc.Ncf.Database.Sqlite | SQLite Database Support | [![Senparc.Ncf.Database.Sqlite][badge-Senparc.Ncf.Database.Sqlite]][nuget-Senparc.Ncf.Database.Sqlite] | +| Senparc.Ncf.Database.SqlServer | SQL Server Database Support | [![Senparc.Ncf.Database.SqlServer][badge-Senparc.Ncf.Database.SqlServer]][nuget-Senparc.Ncf.Database.SqlServer] | +| Senparc.Ncf.Database.Dm | DM (Dameng) Database Support | [![Senparc.Ncf.Database.Dm][badge-Senparc.Ncf.Database.Dm]][nuget-Senparc.Ncf.Database.Dm] | +| Senparc.Ncf.Database.InMemory | In-Memory Database Support | [![Senparc.Ncf.Database.InMemory][badge-Senparc.Ncf.Database.InMemory]][nuget-Senparc.Ncf.Database.InMemory] | +| Senparc.Ncf.Repository | Repository Layer | [![Senparc.Ncf.Repository][badge-Senparc.Ncf.Repository]][nuget-Senparc.Ncf.Repository] | +| Senparc.Ncf.Service | Service Layer | [![Senparc.Ncf.Service][badge-Senparc.Ncf.Service]][nuget-Senparc.Ncf.Service] | +| Senparc.Ncf.SMS | SMS Service | [![Senparc.Ncf.SMS][badge-Senparc.Ncf.SMS]][nuget-Senparc.Ncf.SMS] | +| Senparc.Ncf.Utility | Utility Library | [![Senparc.Ncf.Utility][badge-Senparc.Ncf.Utility]][nuget-Senparc.Ncf.Utility] | + +### Extension XNCF Modules + +| Package Name | Description | Version | +|---|---|---| +| Senparc.Xncf.AgentsManager | AI Agents Manager | [![Senparc.Xncf.AgentsManager][badge-Senparc.Xncf.AgentsManager]][nuget-Senparc.Xncf.AgentsManager] | +| Senparc.Xncf.AIAgentsHub | AI Agents Hub | [![Senparc.Xncf.AIAgentsHub][badge-Senparc.Xncf.AIAgentsHub]][nuget-Senparc.Xncf.AIAgentsHub] | +| Senparc.Xncf.AIKernel | AI Kernel | [![Senparc.Xncf.AIKernel][badge-Senparc.Xncf.AIKernel]][nuget-Senparc.Xncf.AIKernel] | +| Senparc.Xncf.ChangeNamespace | Namespace Rename Tool | [![Senparc.Xncf.ChangeNamespace][badge-Senparc.Xncf.ChangeNamespace]][nuget-Senparc.Xncf.ChangeNamespace] | +| Senparc.Xncf.Dapr | Dapr Distributed Runtime Support | [![Senparc.Xncf.Dapr][badge-Senparc.Xncf.Dapr]][nuget-Senparc.Xncf.Dapr] | +| Senparc.Xncf.DatabaseToolkit | Database Toolkit Module | [![Senparc.Xncf.DatabaseToolkit][badge-Senparc.Xncf.DatabaseToolkit]][nuget-Senparc.Xncf.DatabaseToolkit] | +| Senparc.Xncf.DynamicData | Dynamic Data Base Module | [![Senparc.Xncf.DynamicData][badge-Senparc.Xncf.DynamicData]][nuget-Senparc.Xncf.DynamicData] | +| Senparc.Xncf.FileManager | File Management Module | [![Senparc.Xncf.FileManager][badge-Senparc.Xncf.FileManager]][nuget-Senparc.Xncf.FileManager] | +| Senparc.Xncf.KnowledgeBase | AI Knowledge Base | [![Senparc.Xncf.KnowledgeBase][badge-Senparc.Xncf.KnowledgeBase]][nuget-Senparc.Xncf.KnowledgeBase] | +| Senparc.Xncf.MCP | MCP Multi-tenant Microservice Platform | [![Senparc.Xncf.MCP][badge-Senparc.Xncf.MCP]][nuget-Senparc.Xncf.MCP] | +| Senparc.Xncf.PromptRange | AI Prompt Testing Range | [![Senparc.Xncf.PromptRange][badge-Senparc.Xncf.PromptRange]][nuget-Senparc.Xncf.PromptRange] | +| Senparc.Xncf.SenMapic | SenMapic Web Crawler Module | [![Senparc.Xncf.SenMapic][badge-Senparc.Xncf.SenMapic]][nuget-Senparc.Xncf.SenMapic] | +| Senparc.Xncf.Swagger | Swagger API Documentation | [![Senparc.Xncf.Swagger][badge-Senparc.Xncf.Swagger]][nuget-Senparc.Xncf.Swagger] | +| Senparc.Xncf.Terminal | Terminal Command Module | [![Senparc.Xncf.Terminal][badge-Senparc.Xncf.Terminal]][nuget-Senparc.Xncf.Terminal] | +| Senparc.Xncf.XncfBuilder | XNCF Module Generator | [![Senparc.Xncf.XncfBuilder][badge-Senparc.Xncf.XncfBuilder]][nuget-Senparc.Xncf.XncfBuilder] | +| Senparc.Xncf.AreasBase | System Areas Base Module | [![Senparc.Xncf.AreasBase][badge-Senparc.Xncf.AreasBase]][nuget-Senparc.Xncf.AreasBase] | +| Senparc.Xncf.Menu | Menu Management Module | [![Senparc.Xncf.Menu][badge-Senparc.Xncf.Menu]][nuget-Senparc.Xncf.Menu] | +| Senparc.Xncf.SystemCore | System Core Module | [![Senparc.Xncf.SystemCore][badge-Senparc.Xncf.SystemCore]][nuget-Senparc.Xncf.SystemCore] | +| Senparc.Xncf.SystemManager | System Management Module | [![Senparc.Xncf.SystemManager][badge-Senparc.Xncf.SystemManager]][nuget-Senparc.Xncf.SystemManager] | +| Senparc.Xncf.SystemPermission | System Permission Module | [![Senparc.Xncf.SystemPermission][badge-Senparc.Xncf.SystemPermission]][nuget-Senparc.Xncf.SystemPermission] | +| Senparc.Xncf.Tenant | Multi-tenant Management Module | [![Senparc.Xncf.Tenant][badge-Senparc.Xncf.Tenant]][nuget-Senparc.Xncf.Tenant] | +| Senparc.Xncf.Tenant.Interface | Multi-tenant Interface Module | [![Senparc.Xncf.Tenant.Interface][badge-Senparc.Xncf.Tenant.Interface]][nuget-Senparc.Xncf.Tenant.Interface] | +| Senparc.Xncf.XncfModuleManager | XNCF Module Manager | [![Senparc.Xncf.XncfModuleManager][badge-Senparc.Xncf.XncfModuleManager]][nuget-Senparc.Xncf.XncfModuleManager] | + +## Contributing We welcome developers to contribute to NCF. If you want to contribute, please: -1. Fork 本仓库 / Fork this repository -2. 创建您的特性分支 / Create your feature branch -3. 提交您的改动 / Commit your changes -4. 推送到分支 / Push to the branch -5. 创建 Pull Request / Create a Pull Request +1. Fork this repository +2. Create your feature branch +3. Commit your changes +4. Push to the branch +5. Create a Pull Request -## 许可证 / License +## License Apache License Version 2.0 -详细请参考 / For details, please refer to: [LICENSE](LICENSE) +For details, please refer to: [LICENSE](LICENSE) --- From c9b1f569a5c81cfc1dec086e0c8449b26d4ad29e Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 02:20:40 -0700 Subject: [PATCH 02/21] translate: README/docs Admin-Chat-Migration-Guide to English, preserve .cn.md --- docs/Admin-Chat-Migration-Guide.cn.md | 238 +++++++++++++++++++++++ docs/Admin-Chat-Migration-Guide.md | 268 +++++++++++++------------- 2 files changed, 372 insertions(+), 134 deletions(-) create mode 100644 docs/Admin-Chat-Migration-Guide.cn.md diff --git a/docs/Admin-Chat-Migration-Guide.cn.md b/docs/Admin-Chat-Migration-Guide.cn.md new file mode 100644 index 000000000..25e6d0f7a --- /dev/null +++ b/docs/Admin-Chat-Migration-Guide.cn.md @@ -0,0 +1,238 @@ +# 管理后台 AI 对话功能 - Migration 指南 + +## 📋 执行清单 + +### ✅ 已完成的工作 + +1. **数据模型创建** + - ✅ 3 个实体类(AdminChatSession, AdminChatMessage, AdminChatSessionModule) + - ✅ 3 个 DTO 类(继承 DtoBase) + - ✅ 3 个 Repository 接口和实现类 + +2. **服务层实现** + - ✅ 3 个 Domain Service 类 + - ✅ 1 个 AppService(9 个 API 接口) + - ✅ 所有服务已在 Register.cs 中注册 + +3. **前端页面开发** + - ✅ 首页 AI 对话入口(Index.cshtml + Index.js) + - ✅ 对话任务页面(Chat.cshtml + Chat.js + Chat.css) + - ✅ 模块拖拽功能 + +4. **代码质量** + - ✅ 编译成功(0 个错误) + - ✅ 应用程序可正常启动 + - ✅ 所有依赖已正确注册 + +--- + +## 🚀 待执行操作(醒来后操作) + +### 步骤 1: 创建数据库 Migration + +```bash +cd /Volumes/DevelopAndData/SenparcProjects/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web + +# 创建 Migration +dotnet ef migrations add AddAdminChatTables \ + --project ../Senparc.Areas.Admin/Senparc.Areas.Admin.csproj \ + --context AdminSenparcEntities +``` + +**预期结果**:在 `Senparc.Areas.Admin/Migrations/` 文件夹中生成新的 Migration 文件。 + +--- + +### 步骤 2: 检查 Migration 文件 + +打开生成的 Migration 文件(如 `20260325_AddAdminChatTables.cs`),确认包含以下 3 张表: + +1. **ADMIN_AdminChatSession** + - 字段:Id, Title, UserId, Status, LastMessageTime, AddTime, LastUpdateTime, TenantId, Flag + +2. **ADMIN_AdminChatMessage** + - 字段:Id, SessionId, RoleType, Content, Sequence, UserFeedback, ModelIdentifier, AddTime, LastUpdateTime, TenantId, Flag + +3. **ADMIN_AdminChatSessionModule** + - 字段:Id, SessionId, XncfModuleUid, ModuleName, ModuleVersion, AddedTime, AddTime, LastUpdateTime, TenantId, Flag + +--- + +### 步骤 3: 执行 Migration + +```bash +cd /Volumes/DevelopAndData/SenparcProjects/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web + +# 应用 Migration 到数据库 +dotnet ef database update \ + --project ../Senparc.Areas.Admin/Senparc.Areas.Admin.csproj \ + --context AdminSenparcEntities +``` + +**预期结果**:数据库中创建 3 张新表,并显示 "Done." 消息。 + +--- + +### 步骤 4: 验证数据库表 + +使用数据库管理工具(如 Navicat, DBeaver)连接数据库,检查: + +```sql +-- 检查表是否存在 +SELECT * FROM ADMIN_AdminChatSession LIMIT 0; +SELECT * FROM ADMIN_AdminChatMessage LIMIT 0; +SELECT * FROM ADMIN_AdminChatSessionModule LIMIT 0; + +-- 查看表结构 +DESCRIBE ADMIN_AdminChatSession; +DESCRIBE ADMIN_AdminChatMessage; +DESCRIBE ADMIN_AdminChatSessionModule; +``` + +--- + +### 步骤 5: 启动应用程序 + +```bash +cd /Volumes/DevelopAndData/SenparcProjects/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web + +# 启动应用 +dotnet run +``` + +**预期结果**: +- 应用程序成功启动 +- 显示 "Now listening on: http://localhost:5000" +- 没有数据库相关错误 + +--- + +### 步骤 6: 功能测试 + +#### 6.1 首页测试 + +1. 访问 `http://localhost:5000/Admin` +2. 登录管理后台 +3. 检查首页改版效果: + - ✅ 顶部统计区域正常显示 + - ✅ AI 对话入口提示框显示(醒目、有 margin) + - ✅ "开始对话"按钮可点击 + - ✅ 模块卡片保留 + +#### 6.2 模块拖拽测试 + +1. 在首页,拖拽任一模块卡片 +2. 拖拽到 AI 对话入口提示框上方 +3. 检查效果: + - ✅ 拖拽时提示框高亮/变色 + - ✅ 释放后模块名称显示在提示框下方 + - ✅ 可以删除已选模块(点击 x 按钮) + +#### 6.3 对话页面测试 + +1. 在对话入口输入文字,点击"开始对话" +2. 跳转到对话页面 `/Admin/AdminChat/Chat` +3. 检查页面布局: + - ✅ 左侧会话列表显示 + - ✅ 右侧对话窗口显示 + - ✅ 初始消息已发送 + - ✅ AI 回复显示(占位文本) + +#### 6.4 对话交互测试 + +1. 在右侧对话窗口输入消息,按 Enter 发送 +2. 检查效果: + - ✅ 用户消息显示在右侧(蓝色头像) + - ✅ AI 回复显示在左侧(机器人头像) + - ✅ 消息时间戳正确 + - ✅ 滚动到最新消息 + +3. 测试点赞/点踩功能: + - ✅ 点击 AI 消息的 👍 或 👎 图标 + - ✅ 状态切换正常(高亮/取消高亮) + +4. 测试会话切换: + - ✅ 点击左侧不同会话 + - ✅ 右侧内容切换正确 + - ✅ 消息历史加载正确 + +5. 测试新建对话: + - ✅ 点击"新建对话"按钮 + - ✅ 创建新会话,切换到新对话 + +--- + +## ⚠️ 已知限制和待扩展功能 + +### 当前限制 + +1. **AI 回复为占位文本** + - `AdminChatAppService.GenerateAIResponseAsync()` 方法返回固定文本 + - **原因**: 需要配置真实的 AI 模型接口 + - **扩展方向**: 集成 AIKernel 模块,调用配置的 AI 模型 + +2. **模块上下文暂未传递给 AI** + - 选中的模块信息会保存到数据库,但暂未在 AI 生成时使用 + - **扩展方向**: 在 `GenerateAIResponseAsync` 中读取关联模块,加入 prompt + +3. **会话管理功能简化** + - 暂不支持会话归档、删除、重命名等操作 + - **扩展方向**: 在会话列表添加更多操作按钮(归档、删除、导出等) + +### 未来扩展方向 + +1. **文件上传支持** + - 支持上传文档、图片到对话中 + - 使用 FileManager 模块存储文件 + +2. **语音对话支持** + - 集成语音识别和 TTS + - 支持语音消息 + +3. **对话导出功能** + - 导出对话为 Markdown / PDF + - 分享对话链接 + +4. **多人协作对话** + - 支持多个管理员在同一会话中对话 + - 实时消息推送(WebSocket) + +5. **智能建议和快捷指令** + - 根据对话历史提供智能建议 + - 支持 `/` 快捷指令(如 `/help`, `/clear` 等) + +--- + +## 📊 功能清单总结 + +| 功能模块 | 状态 | 说明 | +|---------|------|------| +| 首页统计区域保留 | ✅ 已完成 | 原有功能完全保留 | +| AI 对话入口提示框 | ✅ 已完成 | 醒目设计,支持输入和拖拽 | +| 模块拖拽功能 | ✅ 已完成 | HTML5 Drag API,无需库 | +| 对话任务页面 | ✅ 已完成 | 左右布局,现代化设计 | +| 会话历史管理 | ✅ 已完成 | 列表显示,切换,新建 | +| 消息发送接收 | ✅ 已完成 | 用户/AI 消息区分 | +| 消息反馈功能 | ✅ 已完成 | 点赞/点踩 | +| 模块上下文关联 | ✅ 已完成 | 保存到数据库 | +| API 接口完整 | ✅ 已完成 | 9 个接口,CRUD 完整 | +| 编译通过 | ✅ 已完成 | 0 个错误 | +| AI 真实回复 | ⏳ 待扩展 | 需要集成 AI 模型 | + +--- + +## 🎯 技术亮点 + +1. **零新依赖**: 完全使用系统现有的 Vue.js、Element UI、axios,无新增库 +2. **DDD 架构**: 严格遵循 Domain 实体、Service、AppService 分层 +3. **Repository 模式**: 为每个实体创建对应的 Repository 接口和实现 +4. **DTO 规范化**: 所有 DTO 继承 `DtoBase`,统一基类属性管理 +5. **拖拽交互**: 原生 HTML5 Drag API,无需第三方库 +6. **现代化 UI**: 符合系统风格,响应式设计,流畅动画 +7. **API 安全**: 使用 `[BackendJwtAuthorize]` 保护所有接口 + +--- + +**文档创建日期**: 2026-03-25 +**预计执行时间**: 10-15 分钟 +**难度等级**: ⭐⭐ (中等) diff --git a/docs/Admin-Chat-Migration-Guide.md b/docs/Admin-Chat-Migration-Guide.md index 25e6d0f7a..251fb8fdd 100644 --- a/docs/Admin-Chat-Migration-Guide.md +++ b/docs/Admin-Chat-Migration-Guide.md @@ -1,89 +1,89 @@ -# 管理后台 AI 对话功能 - Migration 指南 +# Admin Console AI Chat Feature - Migration Guide -## 📋 执行清单 +## 📋 Execution Checklist -### ✅ 已完成的工作 +### ✅ Completed Work -1. **数据模型创建** - - ✅ 3 个实体类(AdminChatSession, AdminChatMessage, AdminChatSessionModule) - - ✅ 3 个 DTO 类(继承 DtoBase) - - ✅ 3 个 Repository 接口和实现类 +1. **Data model creation** + - ✅ 3 entity classes (AdminChatSession, AdminChatMessage, AdminChatSessionModule) + - ✅ 3 DTO classes (inherit from DtoBase) + - ✅ 3 Repository interfaces and implementations -2. **服务层实现** - - ✅ 3 个 Domain Service 类 - - ✅ 1 个 AppService(9 个 API 接口) - - ✅ 所有服务已在 Register.cs 中注册 +2. **Service layer implementation** + - ✅ 3 Domain Service classes + - ✅ 1 AppService (9 API endpoints) + - ✅ All services registered in Register.cs -3. **前端页面开发** - - ✅ 首页 AI 对话入口(Index.cshtml + Index.js) - - ✅ 对话任务页面(Chat.cshtml + Chat.js + Chat.css) - - ✅ 模块拖拽功能 +3. **Frontend page development** + - ✅ Home page AI chat entry (Index.cshtml + Index.js) + - ✅ Chat task page (Chat.cshtml + Chat.js + Chat.css) + - ✅ Module drag-and-drop feature -4. **代码质量** - - ✅ 编译成功(0 个错误) - - ✅ 应用程序可正常启动 - - ✅ 所有依赖已正确注册 +4. **Code quality** + - ✅ Build succeeded (0 errors) + - ✅ Application starts normally + - ✅ All dependencies registered correctly --- -## 🚀 待执行操作(醒来后操作) +## 🚀 Pending Actions (Run after restart) -### 步骤 1: 创建数据库 Migration +### Step 1: Create database migration ```bash cd /Volumes/DevelopAndData/SenparcProjects/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web -# 创建 Migration +# Create migration dotnet ef migrations add AddAdminChatTables \ --project ../Senparc.Areas.Admin/Senparc.Areas.Admin.csproj \ --context AdminSenparcEntities ``` -**预期结果**:在 `Senparc.Areas.Admin/Migrations/` 文件夹中生成新的 Migration 文件。 +**Expected result**: A new migration file is generated in the Senparc.Areas.Admin/Migrations/ folder. --- -### 步骤 2: 检查 Migration 文件 +### Step 2: Verify migration file -打开生成的 Migration 文件(如 `20260325_AddAdminChatTables.cs`),确认包含以下 3 张表: +Open the generated migration file (for example, 20260325_AddAdminChatTables.cs) and confirm it contains the following 3 tables: 1. **ADMIN_AdminChatSession** - - 字段:Id, Title, UserId, Status, LastMessageTime, AddTime, LastUpdateTime, TenantId, Flag + - Fields: Id, Title, UserId, Status, LastMessageTime, AddTime, LastUpdateTime, TenantId, Flag 2. **ADMIN_AdminChatMessage** - - 字段:Id, SessionId, RoleType, Content, Sequence, UserFeedback, ModelIdentifier, AddTime, LastUpdateTime, TenantId, Flag + - Fields: Id, SessionId, RoleType, Content, Sequence, UserFeedback, ModelIdentifier, AddTime, LastUpdateTime, TenantId, Flag 3. **ADMIN_AdminChatSessionModule** - - 字段:Id, SessionId, XncfModuleUid, ModuleName, ModuleVersion, AddedTime, AddTime, LastUpdateTime, TenantId, Flag + - Fields: Id, SessionId, XncfModuleUid, ModuleName, ModuleVersion, AddedTime, AddTime, LastUpdateTime, TenantId, Flag --- -### 步骤 3: 执行 Migration +### Step 3: Apply migration ```bash cd /Volumes/DevelopAndData/SenparcProjects/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web -# 应用 Migration 到数据库 +# Apply migration to database dotnet ef database update \ --project ../Senparc.Areas.Admin/Senparc.Areas.Admin.csproj \ --context AdminSenparcEntities ``` -**预期结果**:数据库中创建 3 张新表,并显示 "Done." 消息。 +**Expected result**: Three new tables are created in the database and the command shows a Done message. --- -### 步骤 4: 验证数据库表 +### Step 4: Validate database tables -使用数据库管理工具(如 Navicat, DBeaver)连接数据库,检查: +Use a database tool (such as Navicat or DBeaver) and check: ```sql --- 检查表是否存在 +-- Verify table existence SELECT * FROM ADMIN_AdminChatSession LIMIT 0; SELECT * FROM ADMIN_AdminChatMessage LIMIT 0; SELECT * FROM ADMIN_AdminChatSessionModule LIMIT 0; --- 查看表结构 +-- Inspect table schema DESCRIBE ADMIN_AdminChatSession; DESCRIBE ADMIN_AdminChatMessage; DESCRIBE ADMIN_AdminChatSessionModule; @@ -91,148 +91,148 @@ DESCRIBE ADMIN_AdminChatSessionModule; --- -### 步骤 5: 启动应用程序 +### Step 5: Start application ```bash cd /Volumes/DevelopAndData/SenparcProjects/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web -# 启动应用 +# Start app dotnet run ``` -**预期结果**: -- 应用程序成功启动 -- 显示 "Now listening on: http://localhost:5000" -- 没有数据库相关错误 +**Expected result**: +- Application starts successfully +- Now listening on http://localhost:5000 is displayed +- No database-related errors --- -### 步骤 6: 功能测试 +### Step 6: Functional testing -#### 6.1 首页测试 +#### 6.1 Home page test -1. 访问 `http://localhost:5000/Admin` -2. 登录管理后台 -3. 检查首页改版效果: - - ✅ 顶部统计区域正常显示 - - ✅ AI 对话入口提示框显示(醒目、有 margin) - - ✅ "开始对话"按钮可点击 - - ✅ 模块卡片保留 +1. Open http://localhost:5000/Admin +2. Sign in to admin console +3. Verify home page update: + - ✅ Top statistics area is displayed correctly + - ✅ AI chat entry panel is visible (prominent with margin) + - ✅ Start Chat button is clickable + - ✅ Module cards are retained -#### 6.2 模块拖拽测试 +#### 6.2 Module drag-and-drop test -1. 在首页,拖拽任一模块卡片 -2. 拖拽到 AI 对话入口提示框上方 -3. 检查效果: - - ✅ 拖拽时提示框高亮/变色 - - ✅ 释放后模块名称显示在提示框下方 - - ✅ 可以删除已选模块(点击 x 按钮) +1. Drag any module card on the home page +2. Drop it above the AI chat entry panel +3. Verify behavior: + - ✅ Entry panel highlights while dragging + - ✅ After drop, module name appears below the panel + - ✅ Selected module can be removed by clicking the x button -#### 6.3 对话页面测试 +#### 6.3 Chat page test -1. 在对话入口输入文字,点击"开始对话" -2. 跳转到对话页面 `/Admin/AdminChat/Chat` -3. 检查页面布局: - - ✅ 左侧会话列表显示 - - ✅ 右侧对话窗口显示 - - ✅ 初始消息已发送 - - ✅ AI 回复显示(占位文本) +1. Enter text in the entry area and click Start Chat +2. Navigate to /Admin/AdminChat/Chat +3. Verify page layout: + - ✅ Left session list is visible + - ✅ Right chat window is visible + - ✅ Initial message is sent + - ✅ AI response is shown (placeholder) -#### 6.4 对话交互测试 +#### 6.4 Chat interaction test -1. 在右侧对话窗口输入消息,按 Enter 发送 -2. 检查效果: - - ✅ 用户消息显示在右侧(蓝色头像) - - ✅ AI 回复显示在左侧(机器人头像) - - ✅ 消息时间戳正确 - - ✅ 滚动到最新消息 +1. Enter a message in the right chat window and press Enter +2. Verify behavior: + - ✅ User message appears on the right (blue avatar) + - ✅ AI message appears on the left (bot avatar) + - ✅ Message timestamp is correct + - ✅ Scroll moves to latest message -3. 测试点赞/点踩功能: - - ✅ 点击 AI 消息的 👍 或 👎 图标 - - ✅ 状态切换正常(高亮/取消高亮) +3. Test thumbs up/down: + - ✅ Click 👍 or 👎 on an AI message + - ✅ State toggle works correctly (highlight/unhighlight) -4. 测试会话切换: - - ✅ 点击左侧不同会话 - - ✅ 右侧内容切换正确 - - ✅ 消息历史加载正确 +4. Test session switching: + - ✅ Click different sessions on the left + - ✅ Right-side content switches correctly + - ✅ Message history loads correctly -5. 测试新建对话: - - ✅ 点击"新建对话"按钮 - - ✅ 创建新会话,切换到新对话 +5. Test creating new chat: + - ✅ Click New Chat button + - ✅ New session is created and switched to --- -## ⚠️ 已知限制和待扩展功能 +## ⚠️ Known limitations and planned extensions -### 当前限制 +### Current limitations -1. **AI 回复为占位文本** - - `AdminChatAppService.GenerateAIResponseAsync()` 方法返回固定文本 - - **原因**: 需要配置真实的 AI 模型接口 - - **扩展方向**: 集成 AIKernel 模块,调用配置的 AI 模型 +1. **AI reply is placeholder text** + - AdminChatAppService.GenerateAIResponseAsync() currently returns fixed text + - **Reason**: Real AI model integration is not configured yet + - **Extension direction**: Integrate AIKernel and call configured model -2. **模块上下文暂未传递给 AI** - - 选中的模块信息会保存到数据库,但暂未在 AI 生成时使用 - - **扩展方向**: 在 `GenerateAIResponseAsync` 中读取关联模块,加入 prompt +2. **Selected module context not passed to AI yet** + - Selected module data is persisted, but not used during AI generation yet + - **Extension direction**: Read linked modules in GenerateAIResponseAsync and inject into prompt -3. **会话管理功能简化** - - 暂不支持会话归档、删除、重命名等操作 - - **扩展方向**: 在会话列表添加更多操作按钮(归档、删除、导出等) +3. **Session management is simplified** + - Archiving, deleting, and renaming sessions are not supported yet + - **Extension direction**: Add more actions in session list (archive, delete, export) -### 未来扩展方向 +### Future roadmap -1. **文件上传支持** - - 支持上传文档、图片到对话中 - - 使用 FileManager 模块存储文件 +1. **File upload support** + - Upload documents and images into chat + - Store files using FileManager module -2. **语音对话支持** - - 集成语音识别和 TTS - - 支持语音消息 +2. **Voice chat support** + - Integrate speech recognition and TTS + - Support voice messages -3. **对话导出功能** - - 导出对话为 Markdown / PDF - - 分享对话链接 +3. **Conversation export** + - Export chat as Markdown or PDF + - Share conversation links -4. **多人协作对话** - - 支持多个管理员在同一会话中对话 - - 实时消息推送(WebSocket) +4. **Collaborative chat** + - Multiple admins in one session + - Real-time message push (WebSocket) -5. **智能建议和快捷指令** - - 根据对话历史提供智能建议 - - 支持 `/` 快捷指令(如 `/help`, `/clear` 等) +5. **Smart suggestions and slash commands** + - Suggest actions based on conversation history + - Support slash commands such as /help and /clear --- -## 📊 功能清单总结 +## 📊 Feature checklist summary -| 功能模块 | 状态 | 说明 | +| Feature Module | Status | Description | |---------|------|------| -| 首页统计区域保留 | ✅ 已完成 | 原有功能完全保留 | -| AI 对话入口提示框 | ✅ 已完成 | 醒目设计,支持输入和拖拽 | -| 模块拖拽功能 | ✅ 已完成 | HTML5 Drag API,无需库 | -| 对话任务页面 | ✅ 已完成 | 左右布局,现代化设计 | -| 会话历史管理 | ✅ 已完成 | 列表显示,切换,新建 | -| 消息发送接收 | ✅ 已完成 | 用户/AI 消息区分 | -| 消息反馈功能 | ✅ 已完成 | 点赞/点踩 | -| 模块上下文关联 | ✅ 已完成 | 保存到数据库 | -| API 接口完整 | ✅ 已完成 | 9 个接口,CRUD 完整 | -| 编译通过 | ✅ 已完成 | 0 个错误 | -| AI 真实回复 | ⏳ 待扩展 | 需要集成 AI 模型 | +| Home page statistics retained | ✅ Completed | Original features are fully preserved | +| AI chat entry panel | ✅ Completed | Prominent design with input and drag support | +| Module drag-and-drop | ✅ Completed | HTML5 Drag API, no external library | +| Chat task page | ✅ Completed | Two-pane modern layout | +| Session history management | ✅ Completed | List, switch, and create session | +| Message send/receive | ✅ Completed | User and AI messages separated | +| Message feedback | ✅ Completed | Thumbs up/down | +| Module context association | ✅ Completed | Saved to database | +| API completeness | ✅ Completed | 9 endpoints, full CRUD | +| Build passed | ✅ Completed | 0 errors | +| Real AI response | ⏳ Planned | AI model integration required | --- -## 🎯 技术亮点 +## 🎯 Technical highlights -1. **零新依赖**: 完全使用系统现有的 Vue.js、Element UI、axios,无新增库 -2. **DDD 架构**: 严格遵循 Domain 实体、Service、AppService 分层 -3. **Repository 模式**: 为每个实体创建对应的 Repository 接口和实现 -4. **DTO 规范化**: 所有 DTO 继承 `DtoBase`,统一基类属性管理 -5. **拖拽交互**: 原生 HTML5 Drag API,无需第三方库 -6. **现代化 UI**: 符合系统风格,响应式设计,流畅动画 -7. **API 安全**: 使用 `[BackendJwtAuthorize]` 保护所有接口 +1. **Zero new dependencies**: Fully based on existing Vue.js, Element UI, and axios +2. **DDD architecture**: Strict Domain Entity, Service, AppService layering +3. **Repository pattern**: Repository interface and implementation for each entity +4. **DTO standardization**: All DTOs inherit from DtoBase +5. **Drag interaction**: Native HTML5 Drag API, no third-party library +6. **Modern UI**: Consistent system style, responsive design, smooth transitions +7. **API security**: All endpoints protected by [BackendJwtAuthorize] --- -**文档创建日期**: 2026-03-25 -**预计执行时间**: 10-15 分钟 -**难度等级**: ⭐⭐ (中等) +**Document Created**: 2026-03-25 +**Estimated Execution Time**: 10-15 minutes +**Difficulty**: ⭐⭐ (Medium) From 03fc76a85f7a9484bd01371656e9b48f8fe8dbf1 Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 02:22:23 -0700 Subject: [PATCH 03/21] translate: README/docs PromptRange-JavaScript-Syntax-Fix to English, preserve .cn.md --- docs/PromptRange-JavaScript-Syntax-Fix.cn.md | 86 ++++++++++++++++++++ docs/PromptRange-JavaScript-Syntax-Fix.md | 80 +++++++++--------- 2 files changed, 127 insertions(+), 39 deletions(-) create mode 100644 docs/PromptRange-JavaScript-Syntax-Fix.cn.md diff --git a/docs/PromptRange-JavaScript-Syntax-Fix.cn.md b/docs/PromptRange-JavaScript-Syntax-Fix.cn.md new file mode 100644 index 000000000..a8ce64602 --- /dev/null +++ b/docs/PromptRange-JavaScript-Syntax-Fix.cn.md @@ -0,0 +1,86 @@ +# 问题修复总结 - PromptRange JavaScript 语法错误 + +## 🐛 问题描述 + +**错误信息**: `prompt.js:3345 Uncaught SyntaxError: Unexpected token '.'` + +**原因**: 在 `prompt.js` 文件的第 3345-3365 行存在一段**孤立的重复代码**,这些代码不属于任何方法,直接出现在两个方法之间,导致 JavaScript 解析器报语法错误。 + +## 🔧 修复内容 + +### 删除的重复代码 (第 3345-3365 行) + +这段代码原本是 `executeOptimize()` 方法的旧版本实现片段,在代码更新时未被正确清理: + +```javascript +// ❌ 错误:这些代码孤立存在,不在任何方法内 + this.optimizeDialogVisible = false; + // 刷新列表或跳转到新Prompt (可选) + // this.getPromptList(this.promptField); // 刷新 + } else if (response.data && response.data.success === false) { + this.$message.error('优化失败: ' + (response.data.message || '未知原因')); + } else { + // NCF 可能直接返回 data + if (response.data.newPromptCode) { + this.$message.success(`优化成功!newPromptCode: ${response.data.newPromptCode}`); + this.optimizeDialogVisible = false; + } else { + this.$message.error('优化未返回有效结果'); + } + } + } catch (error) { + console.error('Optimize Error:', error); + this.$message.error('请求出错:' + (error.message || '未知错误')); + } finally { + this.optimizing = false; + } + }, +``` + +### 修复后的代码结构 + +```javascript + async executeOptimize() { + // ... 方法实现 ... + } finally { + this.optimizing = false; + } + }, // ✅ 方法正常结束 + // 计算树的高度(用于平衡布局) + calculateTreeHeight(nodeData) { + // ... 下一个方法开始 ... + } +``` + +## ✅ 验证结果 + +### JavaScript 语法验证 +```bash +node --check prompt.js +# 返回 Exit Code: 0 (无语法错误) +``` + +### 受影响的文件 +- **文件**: `src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/prompt.js` +- **修复行**: 3345-3365 (共 21 行重复代码已删除) + +## 🎯 根本原因分析 + +这个问题是在之前实现 `executeOptimize()` 方法时,新代码添加后旧代码片段未被完全清理导致的。具体原因可能是: + +1. 代码编辑时使用了不完整的字符串替换 +2. 存在多个版本的优化逻辑片段 +3. 方法结束符 `},` 后遗留了旧代码 + +## 📋 后续建议 + +1. **清理浏览器缓存**: 按 `Ctrl+Shift+R` (或 `Cmd+Shift+R`) 强制刷新页面 +2. **验证功能**: 测试优化功能是否正常工作 +3. **代码审查**: 检查其他 JavaScript 文件是否存在类似问题 + +--- + +**修复时间**: 2026-03-24 +**影响范围**: PromptRange 前端页面加载 +**严重程度**: 高(阻止页面正常加载) +**状态**: ✅ 已修复并验证 diff --git a/docs/PromptRange-JavaScript-Syntax-Fix.md b/docs/PromptRange-JavaScript-Syntax-Fix.md index a8ce64602..f1c32aecd 100644 --- a/docs/PromptRange-JavaScript-Syntax-Fix.md +++ b/docs/PromptRange-JavaScript-Syntax-Fix.md @@ -1,86 +1,88 @@ -# 问题修复总结 - PromptRange JavaScript 语法错误 +# Fix Summary - PromptRange JavaScript Syntax Error -## 🐛 问题描述 +## 🐛 Issue Description -**错误信息**: `prompt.js:3345 Uncaught SyntaxError: Unexpected token '.'` +**Error**: prompt.js:3345 Uncaught SyntaxError: Unexpected token '.' -**原因**: 在 `prompt.js` 文件的第 3345-3365 行存在一段**孤立的重复代码**,这些代码不属于任何方法,直接出现在两个方法之间,导致 JavaScript 解析器报语法错误。 +**Cause**: Lines 3345-3365 in prompt.js contained an isolated duplicate block. The block was outside any method and appeared between two methods, which caused a JavaScript parser syntax error. -## 🔧 修复内容 +## 🔧 Fix Details -### 删除的重复代码 (第 3345-3365 行) +### Removed duplicate code block (lines 3345-3365) -这段代码原本是 `executeOptimize()` 方法的旧版本实现片段,在代码更新时未被正确清理: +This block was an outdated fragment from executeOptimize() that was not cleaned up during refactoring: ```javascript -// ❌ 错误:这些代码孤立存在,不在任何方法内 +// ❌ Incorrect: this code is isolated and not inside any method this.optimizeDialogVisible = false; - // 刷新列表或跳转到新Prompt (可选) - // this.getPromptList(this.promptField); // 刷新 + // Refresh list or navigate to new prompt (optional) + // this.getPromptList(this.promptField); // refresh } else if (response.data && response.data.success === false) { - this.$message.error('优化失败: ' + (response.data.message || '未知原因')); + this.$message.error('Optimization failed: ' + (response.data.message || 'Unknown reason')); } else { - // NCF 可能直接返回 data + // NCF may directly return data if (response.data.newPromptCode) { - this.$message.success(`优化成功!newPromptCode: ${response.data.newPromptCode}`); + this.$message.success(`Optimization succeeded! newPromptCode: ${response.data.newPromptCode}`); this.optimizeDialogVisible = false; } else { - this.$message.error('优化未返回有效结果'); + this.$message.error('Optimization did not return a valid result'); } } } catch (error) { console.error('Optimize Error:', error); - this.$message.error('请求出错:' + (error.message || '未知错误')); + this.$message.error('Request error: ' + (error.message || 'Unknown error')); } finally { this.optimizing = false; } }, ``` -### 修复后的代码结构 +### Code structure after fix ```javascript async executeOptimize() { - // ... 方法实现 ... + // ... method body ... } finally { this.optimizing = false; } - }, // ✅ 方法正常结束 - // 计算树的高度(用于平衡布局) + }, // ✅ method ends correctly + // Calculate tree height (for balanced layout) calculateTreeHeight(nodeData) { - // ... 下一个方法开始 ... + // ... next method starts ... } ``` -## ✅ 验证结果 +## ✅ Verification Result + +### JavaScript syntax check -### JavaScript 语法验证 ```bash node --check prompt.js -# 返回 Exit Code: 0 (无语法错误) +# Exit Code: 0 (no syntax errors) ``` -### 受影响的文件 -- **文件**: `src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/prompt.js` -- **修复行**: 3345-3365 (共 21 行重复代码已删除) +### Affected file + +- File: src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/prompt.js +- Fixed lines: 3345-3365 (21 duplicate lines removed) -## 🎯 根本原因分析 +## 🎯 Root Cause Analysis -这个问题是在之前实现 `executeOptimize()` 方法时,新代码添加后旧代码片段未被完全清理导致的。具体原因可能是: +This issue happened when executeOptimize() was updated and old implementation fragments were not fully removed. Possible reasons: -1. 代码编辑时使用了不完整的字符串替换 -2. 存在多个版本的优化逻辑片段 -3. 方法结束符 `},` 后遗留了旧代码 +1. Incomplete text replacement during editing. +2. Multiple optimization logic fragments existed at the same time. +3. Old code remained after method terminator },. -## 📋 后续建议 +## 📋 Follow-up Suggestions -1. **清理浏览器缓存**: 按 `Ctrl+Shift+R` (或 `Cmd+Shift+R`) 强制刷新页面 -2. **验证功能**: 测试优化功能是否正常工作 -3. **代码审查**: 检查其他 JavaScript 文件是否存在类似问题 +1. Clear browser cache: use Ctrl+Shift+R (or Cmd+Shift+R) for a hard refresh. +2. Validate optimization flow: verify optimization behavior end-to-end. +3. Perform targeted review: inspect other JavaScript files for similar isolated fragments. --- -**修复时间**: 2026-03-24 -**影响范围**: PromptRange 前端页面加载 -**严重程度**: 高(阻止页面正常加载) -**状态**: ✅ 已修复并验证 +**Fix Time**: 2026-03-24 +**Impact Scope**: PromptRange frontend page loading +**Severity**: High (blocks normal page load) +**Status**: ✅ Fixed and verified From 0346ff776b06b171bfaf72b0040d448ee729627d Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 02:23:18 -0700 Subject: [PATCH 04/21] translate: README/docs Admin-Chat-Authentication to English, preserve .cn.md --- docs/Admin-Chat-Authentication.cn.md | 144 ++++++++++++++++++++++++ docs/Admin-Chat-Authentication.md | 159 ++++++++++++++------------- 2 files changed, 225 insertions(+), 78 deletions(-) create mode 100644 docs/Admin-Chat-Authentication.cn.md diff --git a/docs/Admin-Chat-Authentication.cn.md b/docs/Admin-Chat-Authentication.cn.md new file mode 100644 index 000000000..dfa851ad2 --- /dev/null +++ b/docs/Admin-Chat-Authentication.cn.md @@ -0,0 +1,144 @@ +# Admin Chat 认证配置说明 + +## 认证方式兼容性 + +`AdminChatAppService` 现在支持两种认证方式,只要其中一种通过即可访问 API: + +### 1. Cookie 认证(网页登录) +- **认证方案**: `NcfAdminAuthorizeScheme` +- **使用场景**: 通过管理后台登录页面(`/Admin/Login`)登录 +- **适用于**: 浏览器网页访问 + +### 2. JWT 认证(API Token) +- **认证方案**: `Bearer_Backend` +- **使用场景**: API 客户端、移动端、第三方集成 +- **适用于**: 需要 Token 认证的场景 + +## 问题修复历史 + +### 问题1: "登录过期,即将跳转到登录页面" +**原因**: `AdminChatAppService` 使用了 `[BackendJwtAuthorize]`,但用户使用 Cookie 登录 +**修复**: 改用 `[AdminOrJwtAuthorize]` 支持两种认证方式 + +### 问题2: "跳转到 /Admin/Forbidden" +**原因**: Chat 页面需要 "AdminOnly" Policy,但在 Register.cs 中未配置 +**修复**: 在 Register.cs 中添加 `options.Conventions.AuthorizePage("/AdminChat/Chat", "AdminOnly");` + +## 实现细节 + +### AdminOrJwtAuthorizeAttribute +新创建的认证属性,位于 `AdminOrJwtAuthorizeAttribute.cs` + +```csharp +[AdminOrJwtAuthorize("AdminOnly")] +public class AdminChatAppService : LocalAppServiceBase +{ + // API 方法可以通过 Cookie 或 JWT 任一方式访问 + // 同时要求用户具有 "AdminMember" Claim +} +``` + +### Register.cs 配置 +```csharp +options.Conventions.AuthorizePage("/", "AdminOnly"); // 首页 +options.Conventions.AuthorizePage("/AdminChat/Chat", "AdminOnly"); // 聊天页面 +options.Conventions.AllowAnonymousToPage("/Login"); // 登录页允许匿名 +``` + +### Policy "AdminOnly" 定义 +```csharp +options.AddPolicy("AdminOnly", policy => +{ + policy.RequireClaim("AdminMember"); // 要求 "AdminMember" Claim +}); +``` + +### 登录时设置的 Claims +在 `AdminUserInfoService.LoginAsync` 方法中: +```csharp +var claims = new List +{ + new Claim(ClaimTypes.Name, userInfo.UserName), + new Claim(ClaimTypes.NameIdentifier, userInfo.Id.ToString(), ClaimValueTypes.Integer), + new Claim("AdminMember", "", ClaimValueTypes.String) // 关键 Claim +}; +``` + +## 工作原理 + +### 认证流程 +1. 用户通过 `Login.cshtml` 登录 +2. 系统设置 Cookie 并添加 "AdminMember" Claim +3. 访问 Chat 页面或 API 时,框架检查: + - 是否已认证(Cookie 或 JWT) + - 是否具有 "AdminMember" Claim +4. 两个条件都满足,允许访问 + +### ASP.NET Core 多认证方案 +`AuthenticationSchemes` 属性支持多个认证方案(用逗号分隔): +```csharp +AuthenticationSchemes = $"{SiteConfig.NcfAdminAuthorizeScheme},{JwtScheme}"; +``` + +当请求到达时: +1. 框架会依次尝试配置的认证方案 +2. 只要**任何一个**认证方案成功,就允许访问 +3. 如果**所有**认证方案都失败,才会返回 401 Unauthorized + +## 使用示例 + +### Cookie 认证访问(当前场景) +```javascript +// 用户通过 Login.cshtml 登录后,浏览器会自动携带 Cookie +axios.post('/api/AdminChat/CreateSessionAsync', data) + .then(response => { + // Cookie 认证自动完成,无需额外操作 + }); +``` + +### JWT 认证访问(未来扩展) +```javascript +// 客户端需要先获取 JWT Token,然后在请求头中携带 +axios.post('/api/AdminChat/CreateSessionAsync', data, { + headers: { + 'Authorization': 'Bearer ' + jwtToken + } +}); +``` + +## 其他 AppService 认证对比 + +| AppService | 认证属性 | 认证方式 | Policy | +|------------|---------|---------|--------| +| `AdminChatAppService` | `[AdminOrJwtAuthorize("AdminOnly")]` | Cookie **或** JWT | 需要 "AdminMember" Claim | +| `AdminUserInfoAppService` | `[BackendJwtAuthorize]` | 仅 JWT | 无 | +| `StatAppService` | `[AdminAuthorize("AdminOnly")]` | 仅 Cookie | 需要 "AdminMember" Claim | +| `ModuleAppService` | `[BackendJwtAuthorize]` | 仅 JWT | 无 | + +## 优势 + +1. **向后兼容**: 现有 Cookie 登录完全可用 +2. **未来扩展**: 支持 API Token 方式,便于移动端或第三方集成 +3. **灵活性高**: 不同客户端可以选择最适合的认证方式 +4. **安全性**: Policy 确保只有管理员可以访问 + +## 测试建议 + +### Cookie 认证测试 +1. 通过 `/Admin/Login` 登录管理后台 +2. 在首页输入框发送消息 +3. 应能正常跳转到聊天页面,不会出现 Forbidden 错误 + +### JWT 认证测试(可选) +1. 使用 Postman 或其他 API 工具 +2. 获取包含 "AdminMember" Claim 的 JWT Token +3. 在请求头添加 `Authorization: Bearer {token}` +4. 调用 `/api/AdminChat/CreateSessionAsync` 等接口 +5. 应能正常获取响应 + +## 注意事项 + +- **Claim 要求**: 无论使用哪种认证方式,都必须包含 "AdminMember" Claim +- **Razor Pages 路径配置**: 新增的 Razor Pages 需要在 Register.cs 中配置授权策略 +- **统一授权策略**: API 和页面使用相同的 "AdminOnly" Policy,保证安全性一致 + diff --git a/docs/Admin-Chat-Authentication.md b/docs/Admin-Chat-Authentication.md index dfa851ad2..5f7717278 100644 --- a/docs/Admin-Chat-Authentication.md +++ b/docs/Admin-Chat-Authentication.md @@ -1,104 +1,108 @@ -# Admin Chat 认证配置说明 +# Admin Chat Authentication Configuration Guide -## 认证方式兼容性 +## Authentication Compatibility -`AdminChatAppService` 现在支持两种认证方式,只要其中一种通过即可访问 API: +AdminChatAppService now supports two authentication methods. Access is allowed when either one succeeds. -### 1. Cookie 认证(网页登录) -- **认证方案**: `NcfAdminAuthorizeScheme` -- **使用场景**: 通过管理后台登录页面(`/Admin/Login`)登录 -- **适用于**: 浏览器网页访问 +### 1. Cookie Authentication (Web Sign-in) +- Authentication scheme: NcfAdminAuthorizeScheme +- Usage scenario: Sign in through the admin login page (/Admin/Login) +- Suitable for: Browser-based web access -### 2. JWT 认证(API Token) -- **认证方案**: `Bearer_Backend` -- **使用场景**: API 客户端、移动端、第三方集成 -- **适用于**: 需要 Token 认证的场景 +### 2. JWT Authentication (API Token) +- Authentication scheme: Bearer_Backend +- Usage scenario: API clients, mobile apps, third-party integrations +- Suitable for: Token-based API access -## 问题修复历史 +## Issue Fix History -### 问题1: "登录过期,即将跳转到登录页面" -**原因**: `AdminChatAppService` 使用了 `[BackendJwtAuthorize]`,但用户使用 Cookie 登录 -**修复**: 改用 `[AdminOrJwtAuthorize]` 支持两种认证方式 +### Issue 1: Login expired, redirecting to login page +**Cause**: AdminChatAppService used [BackendJwtAuthorize], but the user signed in with Cookie authentication. +**Fix**: Switched to [AdminOrJwtAuthorize] to support both authentication methods. -### 问题2: "跳转到 /Admin/Forbidden" -**原因**: Chat 页面需要 "AdminOnly" Policy,但在 Register.cs 中未配置 -**修复**: 在 Register.cs 中添加 `options.Conventions.AuthorizePage("/AdminChat/Chat", "AdminOnly");` +### Issue 2: Redirected to /Admin/Forbidden +**Cause**: The Chat page requires the AdminOnly policy, but it was not configured in Register.cs. +**Fix**: Added options.Conventions.AuthorizePage("/AdminChat/Chat", "AdminOnly"); in Register.cs. -## 实现细节 +## Implementation Details ### AdminOrJwtAuthorizeAttribute -新创建的认证属性,位于 `AdminOrJwtAuthorizeAttribute.cs` +A newly introduced authorization attribute located in AdminOrJwtAuthorizeAttribute.cs. ```csharp [AdminOrJwtAuthorize("AdminOnly")] public class AdminChatAppService : LocalAppServiceBase { - // API 方法可以通过 Cookie 或 JWT 任一方式访问 - // 同时要求用户具有 "AdminMember" Claim + // API methods can be accessed through either Cookie or JWT + // Users must also have the AdminMember claim } ``` -### Register.cs 配置 +### Register.cs Configuration ```csharp -options.Conventions.AuthorizePage("/", "AdminOnly"); // 首页 -options.Conventions.AuthorizePage("/AdminChat/Chat", "AdminOnly"); // 聊天页面 -options.Conventions.AllowAnonymousToPage("/Login"); // 登录页允许匿名 +options.Conventions.AuthorizePage("/", "AdminOnly"); // Home page +options.Conventions.AuthorizePage("/AdminChat/Chat", "AdminOnly"); // Chat page +options.Conventions.AllowAnonymousToPage("/Login"); // Login page allows anonymous ``` -### Policy "AdminOnly" 定义 +### AdminOnly Policy Definition ```csharp options.AddPolicy("AdminOnly", policy => { - policy.RequireClaim("AdminMember"); // 要求 "AdminMember" Claim + policy.RequireClaim("AdminMember"); // Requires AdminMember claim }); ``` -### 登录时设置的 Claims -在 `AdminUserInfoService.LoginAsync` 方法中: +### Claims set during sign-in +In AdminUserInfoService.LoginAsync: + ```csharp var claims = new List { new Claim(ClaimTypes.Name, userInfo.UserName), new Claim(ClaimTypes.NameIdentifier, userInfo.Id.ToString(), ClaimValueTypes.Integer), - new Claim("AdminMember", "", ClaimValueTypes.String) // 关键 Claim + new Claim("AdminMember", "", ClaimValueTypes.String) // Key claim }; ``` -## 工作原理 +## How It Works + +### Authentication flow +1. User signs in through Login.cshtml. +2. System sets Cookie and adds AdminMember claim. +3. When visiting Chat page or API, framework checks: + - Is the user authenticated (Cookie or JWT)? + - Does the user have AdminMember claim? +4. Access is granted only when both conditions are met. -### 认证流程 -1. 用户通过 `Login.cshtml` 登录 -2. 系统设置 Cookie 并添加 "AdminMember" Claim -3. 访问 Chat 页面或 API 时,框架检查: - - 是否已认证(Cookie 或 JWT) - - 是否具有 "AdminMember" Claim -4. 两个条件都满足,允许访问 +### Multiple authentication schemes in ASP.NET Core +The AuthenticationSchemes property supports multiple schemes separated by commas: -### ASP.NET Core 多认证方案 -`AuthenticationSchemes` 属性支持多个认证方案(用逗号分隔): ```csharp AuthenticationSchemes = $"{SiteConfig.NcfAdminAuthorizeScheme},{JwtScheme}"; ``` -当请求到达时: -1. 框架会依次尝试配置的认证方案 -2. 只要**任何一个**认证方案成功,就允许访问 -3. 如果**所有**认证方案都失败,才会返回 401 Unauthorized +When a request arrives: +1. Framework tries configured schemes in order. +2. Access is allowed when any one scheme succeeds. +3. A 401 Unauthorized response is returned only when all schemes fail. + +## Usage Examples -## 使用示例 +### Cookie-based access (current scenario) -### Cookie 认证访问(当前场景) ```javascript -// 用户通过 Login.cshtml 登录后,浏览器会自动携带 Cookie +// After signing in via Login.cshtml, browser automatically includes Cookie axios.post('/api/AdminChat/CreateSessionAsync', data) .then(response => { - // Cookie 认证自动完成,无需额外操作 + // Cookie authentication is automatic }); ``` -### JWT 认证访问(未来扩展) +### JWT-based access (future extension) + ```javascript -// 客户端需要先获取 JWT Token,然后在请求头中携带 +// Client first obtains JWT token, then sends it in Authorization header axios.post('/api/AdminChat/CreateSessionAsync', data, { headers: { 'Authorization': 'Bearer ' + jwtToken @@ -106,39 +110,38 @@ axios.post('/api/AdminChat/CreateSessionAsync', data, { }); ``` -## 其他 AppService 认证对比 +## Authentication Comparison with Other AppServices -| AppService | 认证属性 | 认证方式 | Policy | +| AppService | Authorization Attribute | Auth Method | Policy | |------------|---------|---------|--------| -| `AdminChatAppService` | `[AdminOrJwtAuthorize("AdminOnly")]` | Cookie **或** JWT | 需要 "AdminMember" Claim | -| `AdminUserInfoAppService` | `[BackendJwtAuthorize]` | 仅 JWT | 无 | -| `StatAppService` | `[AdminAuthorize("AdminOnly")]` | 仅 Cookie | 需要 "AdminMember" Claim | -| `ModuleAppService` | `[BackendJwtAuthorize]` | 仅 JWT | 无 | - -## 优势 +| AdminChatAppService | [AdminOrJwtAuthorize("AdminOnly")] | Cookie or JWT | Requires AdminMember claim | +| AdminUserInfoAppService | [BackendJwtAuthorize] | JWT only | None | +| StatAppService | [AdminAuthorize("AdminOnly")] | Cookie only | Requires AdminMember claim | +| ModuleAppService | [BackendJwtAuthorize] | JWT only | None | -1. **向后兼容**: 现有 Cookie 登录完全可用 -2. **未来扩展**: 支持 API Token 方式,便于移动端或第三方集成 -3. **灵活性高**: 不同客户端可以选择最适合的认证方式 -4. **安全性**: Policy 确保只有管理员可以访问 +## Benefits -## 测试建议 +1. Backward compatible: Existing Cookie sign-in works as-is. +2. Future-ready: Supports API token flow for mobile and third-party integrations. +3. Flexible: Different clients can choose the best authentication mode. +4. Secure: Policy ensures only admins can access protected resources. -### Cookie 认证测试 -1. 通过 `/Admin/Login` 登录管理后台 -2. 在首页输入框发送消息 -3. 应能正常跳转到聊天页面,不会出现 Forbidden 错误 +## Test Recommendations -### JWT 认证测试(可选) -1. 使用 Postman 或其他 API 工具 -2. 获取包含 "AdminMember" Claim 的 JWT Token -3. 在请求头添加 `Authorization: Bearer {token}` -4. 调用 `/api/AdminChat/CreateSessionAsync` 等接口 -5. 应能正常获取响应 +### Cookie authentication test +1. Sign in to admin console via /Admin/Login. +2. Send a message from the home page input box. +3. It should navigate to Chat page normally without Forbidden errors. -## 注意事项 +### JWT authentication test (optional) +1. Use Postman or another API tool. +2. Obtain a JWT token that includes AdminMember claim. +3. Add Authorization: Bearer {token} in request headers. +4. Call endpoints such as /api/AdminChat/CreateSessionAsync. +5. Response should be returned successfully. -- **Claim 要求**: 无论使用哪种认证方式,都必须包含 "AdminMember" Claim -- **Razor Pages 路径配置**: 新增的 Razor Pages 需要在 Register.cs 中配置授权策略 -- **统一授权策略**: API 和页面使用相同的 "AdminOnly" Policy,保证安全性一致 +## Notes +- Claim requirement: regardless of authentication mode, AdminMember claim is required. +- Razor Pages path policy: newly added Razor Pages must be configured in Register.cs. +- Unified authorization policy: API and page share the same AdminOnly policy for consistent security. From 21585cd1eca70155f5dff18f6983b05b50548085 Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 02:24:07 -0700 Subject: [PATCH 05/21] translate: README/docs PromptCatalyzer-404-Fix to English, preserve .cn.md --- docs/PromptCatalyzer-404-Fix.cn.md | 111 ++++++++++++++++++++++++ docs/PromptCatalyzer-404-Fix.md | 130 +++++++++++++++-------------- 2 files changed, 180 insertions(+), 61 deletions(-) create mode 100644 docs/PromptCatalyzer-404-Fix.cn.md diff --git a/docs/PromptCatalyzer-404-Fix.cn.md b/docs/PromptCatalyzer-404-Fix.cn.md new file mode 100644 index 000000000..e88d7b035 --- /dev/null +++ b/docs/PromptCatalyzer-404-Fix.cn.md @@ -0,0 +1,111 @@ +# 404 错误修复说明 - PromptCatalyzer API 端点 + +## 🐛 问题描述 + +**错误信息**: +``` +GET http://localhost:5000/api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/CheckStatus 404 (Not Found) +GET http://localhost:5000/api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/GetAvailableModels 404 (Not Found) +``` + +**原因**: 新创建的 `PromptCatalyzerInitAppService` 类使用了 `[ApiBind]` 特性,但由于 AgentsManager 项目的依赖配置问题,该特性无法正常工作,导致 API 端点未被注册。 + +## 🔧 解决方案 + +### 1. 删除问题文件 +删除了有问题的 AppService 实现: +- `src/Extensions/Senparc.Xncf.AgentsManager/Application/AppService/PromptCatalyzerInitAppService.cs` + +### 2. 创建传统 API Controller + +**新文件**: `src/Extensions/Senparc.Xncf.AgentsManager/OHS/Remote/Controllers/PromptCatalyzerInitController.cs` + +使用传统的 ASP.NET Core Web API Controller 方式: + +```csharp +[ApiController] +[Route("api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService")] +public class PromptCatalyzerInitController : ControllerBase +{ + // 三个 API 端点: + // GET /api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/CheckStatus + // GET /api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/GetAvailableModels + // POST /api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/Initialize +} +``` + +### 3. API 端点详情 + +#### CheckStatus (GET) +- **路径**: `/api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/CheckStatus` +- **功能**: 检查 PromptCatalyzer Agent 是否已存在 +- **返回**: `AppResponseBase` + +#### GetAvailableModels (GET) +- **路径**: `/api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/GetAvailableModels` +- **功能**: 获取所有可用的 Chat 类型 AI Model +- **返回**: `AppResponseBase` + +#### Initialize (POST) +- **路径**: `/api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/Initialize` +- **功能**: 初始化 PromptCatalyzer Agent 和相关 Prompt 资源 +- **参数**: `{ "modelId": 1 }` +- **返回**: `AppResponseBase` + +### 4. 路由设计 + +为了保持前端代码不变,Controller 的路由被设置为与原 AppService 相同的格式: +- 使用 `[Route("api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService")]` +- 方法使用相对路径如 `[HttpGet("CheckStatus")]` + +## ✅ 编译验证 + +```bash +dotnet build src/Extensions/Senparc.Xncf.AgentsManager/Senparc.Xncf.AgentsManager.csproj +# 结果: ✅ 0 错误,10 个警告 +``` + +## 🚀 下一步操作 + +**必须重启应用程序**才能让新的 Controller 生效: + +1. **停止当前应用** (如果正在运行) +2. **清理编译缓存** (可选但推荐): + ```bash + dotnet clean + ``` +3. **重新启动应用**: + ```bash + dotnet run --project tools/NcfSimulatedSite/Senparc.Web/Senparc.Web.csproj + ``` +4. **刷新浏览器页面** (Ctrl+Shift+R / Cmd+Shift+R) +5. **测试"优化"按钮** + +## 📋 技术说明 + +### 为什么使用 Controller 而不是 AppService? + +1. **兼容性问题**: `[ApiBind]` 特性在 AgentsManager 项目中无法正常解析 +2. **依赖问题**: AgentsManager 使用 NuGet 包引用而非项目引用,可能导致类型解析问题 +3. **可靠性**: 传统 Controller 是 ASP.NET Core 的标准方式,更稳定可靠 + +### Controller vs AppService 对比 + +| 特性 | AppService + [ApiBind] | Traditional Controller | +|------|------------------------|------------------------| +| 路由注册 | 动态自动注册 | ASP.NET Core 自动扫描 | +| 依赖要求 | Senparc.CO2NET.WebApi | 内置支持 | +| 配置复杂度 | 低 | 低 | +| 稳定性 | 依赖框架版本 | 高(标准实现) | + +## 🔐 权限验证 + +Controller 级别的权限验证将在下一步实施,通过: +- `[Authorize]` 特性 +- 或自定义授权过滤器 + +--- + +**修复时间**: 2026-03-24 +**影响范围**: PromptCatalyzer 初始化功能 +**状态**: ✅ 已修复,等待重启应用验证 diff --git a/docs/PromptCatalyzer-404-Fix.md b/docs/PromptCatalyzer-404-Fix.md index e88d7b035..c663b9d4f 100644 --- a/docs/PromptCatalyzer-404-Fix.md +++ b/docs/PromptCatalyzer-404-Fix.md @@ -1,111 +1,119 @@ -# 404 错误修复说明 - PromptCatalyzer API 端点 +# 404 Fix Notes - PromptCatalyzer API Endpoints -## 🐛 问题描述 +## 🐛 Issue Description + +**Errors**: -**错误信息**: ``` GET http://localhost:5000/api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/CheckStatus 404 (Not Found) GET http://localhost:5000/api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/GetAvailableModels 404 (Not Found) ``` -**原因**: 新创建的 `PromptCatalyzerInitAppService` 类使用了 `[ApiBind]` 特性,但由于 AgentsManager 项目的依赖配置问题,该特性无法正常工作,导致 API 端点未被注册。 +**Cause**: The newly created PromptCatalyzerInitAppService used the ApiBind attribute, but because of dependency configuration issues in the AgentsManager project, the attribute did not work correctly and the API endpoints were not registered. + +## 🔧 Solution -## 🔧 解决方案 +### 1. Remove problematic file +Removed the problematic AppService implementation: -### 1. 删除问题文件 -删除了有问题的 AppService 实现: -- `src/Extensions/Senparc.Xncf.AgentsManager/Application/AppService/PromptCatalyzerInitAppService.cs` +- src/Extensions/Senparc.Xncf.AgentsManager/Application/AppService/PromptCatalyzerInitAppService.cs -### 2. 创建传统 API Controller +### 2. Create traditional API controller -**新文件**: `src/Extensions/Senparc.Xncf.AgentsManager/OHS/Remote/Controllers/PromptCatalyzerInitController.cs` +**New file**: src/Extensions/Senparc.Xncf.AgentsManager/OHS/Remote/Controllers/PromptCatalyzerInitController.cs -使用传统的 ASP.NET Core Web API Controller 方式: +Use the standard ASP.NET Core Web API Controller approach: ```csharp [ApiController] [Route("api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService")] public class PromptCatalyzerInitController : ControllerBase { - // 三个 API 端点: + // Three API endpoints: // GET /api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/CheckStatus - // GET /api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/GetAvailableModels + // GET /api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/GetAvailableModels // POST /api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/Initialize } ``` -### 3. API 端点详情 +### 3. Endpoint details #### CheckStatus (GET) -- **路径**: `/api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/CheckStatus` -- **功能**: 检查 PromptCatalyzer Agent 是否已存在 -- **返回**: `AppResponseBase` +- Path: /api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/CheckStatus +- Purpose: Check whether PromptCatalyzer Agent already exists +- Return type: AppResponseBase #### GetAvailableModels (GET) -- **路径**: `/api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/GetAvailableModels` -- **功能**: 获取所有可用的 Chat 类型 AI Model -- **返回**: `AppResponseBase` +- Path: /api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/GetAvailableModels +- Purpose: Get all available AI models of chat type +- Return type: AppResponseBase #### Initialize (POST) -- **路径**: `/api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/Initialize` -- **功能**: 初始化 PromptCatalyzer Agent 和相关 Prompt 资源 -- **参数**: `{ "modelId": 1 }` -- **返回**: `AppResponseBase` +- Path: /api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService/Initialize +- Purpose: Initialize PromptCatalyzer Agent and related Prompt resources +- Parameters: { "modelId": 1 } +- Return type: AppResponseBase + +### 4. Routing design -### 4. 路由设计 +To keep frontend code unchanged, controller routes use the same format as the original AppService: -为了保持前端代码不变,Controller 的路由被设置为与原 AppService 相同的格式: -- 使用 `[Route("api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService")]` -- 方法使用相对路径如 `[HttpGet("CheckStatus")]` +- Base route: [Route("api/Senparc.Xncf.AgentsManager/PromptCatalyzerInitAppService")] +- Method route style: [HttpGet("CheckStatus")], etc. -## ✅ 编译验证 +## ✅ Build Verification ```bash dotnet build src/Extensions/Senparc.Xncf.AgentsManager/Senparc.Xncf.AgentsManager.csproj -# 结果: ✅ 0 错误,10 个警告 +# Result: ✅ 0 errors, 10 warnings +``` + +## 🚀 Next Steps + +Application restart is required for the new controller to take effect: + +1. Stop the current application (if running). +2. Clean build cache (optional but recommended): + +```bash +dotnet clean ``` -## 🚀 下一步操作 +3. Restart application: -**必须重启应用程序**才能让新的 Controller 生效: +```bash +dotnet run --project tools/NcfSimulatedSite/Senparc.Web/Senparc.Web.csproj +``` -1. **停止当前应用** (如果正在运行) -2. **清理编译缓存** (可选但推荐): - ```bash - dotnet clean - ``` -3. **重新启动应用**: - ```bash - dotnet run --project tools/NcfSimulatedSite/Senparc.Web/Senparc.Web.csproj - ``` -4. **刷新浏览器页面** (Ctrl+Shift+R / Cmd+Shift+R) -5. **测试"优化"按钮** +4. Hard refresh browser (Ctrl+Shift+R / Cmd+Shift+R). +5. Re-test the Optimize button. -## 📋 技术说明 +## 📋 Technical Notes -### 为什么使用 Controller 而不是 AppService? +### Why use Controller instead of AppService? -1. **兼容性问题**: `[ApiBind]` 特性在 AgentsManager 项目中无法正常解析 -2. **依赖问题**: AgentsManager 使用 NuGet 包引用而非项目引用,可能导致类型解析问题 -3. **可靠性**: 传统 Controller 是 ASP.NET Core 的标准方式,更稳定可靠 +1. Compatibility issue: ApiBind could not be resolved correctly in AgentsManager. +2. Dependency issue: AgentsManager uses NuGet package references instead of project references, which may affect type resolution. +3. Reliability: Traditional Controller is standard ASP.NET Core implementation and is more stable. -### Controller vs AppService 对比 +### Controller vs AppService -| 特性 | AppService + [ApiBind] | Traditional Controller | +| Capability | AppService + ApiBind | Traditional Controller | |------|------------------------|------------------------| -| 路由注册 | 动态自动注册 | ASP.NET Core 自动扫描 | -| 依赖要求 | Senparc.CO2NET.WebApi | 内置支持 | -| 配置复杂度 | 低 | 低 | -| 稳定性 | 依赖框架版本 | 高(标准实现) | +| Route registration | Dynamic auto registration | ASP.NET Core auto discovery | +| Dependency requirement | Senparc.CO2NET.WebApi | Built-in support | +| Config complexity | Low | Low | +| Stability | Framework-version dependent | High (standard implementation) | + +## 🔐 Authorization -## 🔐 权限验证 +Controller-level authorization will be implemented in the next step using: -Controller 级别的权限验证将在下一步实施,通过: -- `[Authorize]` 特性 -- 或自定义授权过滤器 +- Authorize attribute +- Or a custom authorization filter --- -**修复时间**: 2026-03-24 -**影响范围**: PromptCatalyzer 初始化功能 -**状态**: ✅ 已修复,等待重启应用验证 +**Fix Time**: 2026-03-24 +**Impact Scope**: PromptCatalyzer initialization workflow +**Status**: ✅ Fixed, pending verification after application restart From 83d3d014a6a0d3511e96e8f2277f84a4156658cd Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 02:25:42 -0700 Subject: [PATCH 06/21] translate: Chinese comments to English in XncfModuleManager AppService --- .../OHS/Local/AppService/XncfStateAppService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/OHS/Local/AppService/XncfStateAppService.cs b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/OHS/Local/AppService/XncfStateAppService.cs index 236c588d7..b8f9501f1 100644 --- a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/OHS/Local/AppService/XncfStateAppService.cs +++ b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/OHS/Local/AppService/XncfStateAppService.cs @@ -57,7 +57,7 @@ public async Task ShowFunctions(XncfState_ShowFunctionsReques response.Data += logger.Append($"[Function: {function.Value.Key}]\r\n"); response.Data += logger.Append($"[内部方法: {function.Key}]\r\n"); - //response.Data = logger.Append($"名称:{function.Value.FunctionRenderAttribute.Name}\r\n"); + //response.Data = logger.Append($"Name: {function.Value.FunctionRenderAttribute.Name}\r\n"); try { From 814eb18a307ad815fd1aaa588b1b8205ed0b9c1e Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 02:26:43 -0700 Subject: [PATCH 07/21] translate: Chinese comments to English in XncfModuleManager Register --- .../Register.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Register.cs b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Register.cs index fdd8b54c4..a441fd928 100644 --- a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Register.cs +++ b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Register.cs @@ -18,13 +18,13 @@ namespace Senparc.Xncf.XncfModuleManager [XncfOrder(5950)] public partial class Register : XncfRegisterBase, IXncfRegister { - #region IXncfRegister 接口 + #region IXncfRegister Interface public override string Name => "Senparc.Xncf.XncfModuleManager"; public override string Uid => SiteConfig.SYSTEM_XNCF_MODULE_XNCF_MODULE_MANAGER_UID;// "00000000-0000-0000-0000-000000000004"; - public override string Version => "0.1.2";//必须填写版本号 + public override string Version => "0.1.2";//Version number is required public override string MenuName => "XNCF 模块管理核心"; @@ -37,7 +37,7 @@ public override async Task InstallOrUpdateAsync(IServiceProvider serviceProvider try { Console.WriteLine($"执行 Xncf.XncfModuleManager.InstallOrUpdateAsync"); - //安装或升级数据库 + //Install or upgrade database await XncfDatabaseDbContext.MigrateOnInstallAsync(serviceProvider, this); Console.WriteLine($"执行 Xncf.XncfModuleManager.InstallOrUpdateAsync 完毕"); @@ -53,19 +53,19 @@ public override async Task InstallOrUpdateAsync(IServiceProvider serviceProvider //await InstallModulesAndMenusAsync(serviceProvider); - //TODO:DI注入注册时候,根据指定数据库进行绑定 + //TODO: Bind by the specified database when registering DI injection //XncfModuleServiceExtension xncfModuleServiceExtension = serviceProvider.GetService(); ////SenparcEntities senparcEntities = (SenparcEntities)xncfModuleServiceExtension.BaseData.BaseDB.BaseDataContext; - ////更新数据库(目前不使用 BasePoolEntities 存放数据库模型) + ////Update database (BasePoolEntities is not used for storing DB models currently) ////await base.MigrateDatabaseAsync(serviceProvider); //var systemModule = xncfModuleServiceExtension.GetObject(z => z.Uid == this.Uid); //if (systemModule == null) //{ - // //只在未安装的情况下进行安装,InstallModuleAsync会访问到此方法,不做判断可能会引发死循环。 - // //常规模块中请勿在此方法中自动安装模块! + // //Install only when not installed. InstallModuleAsync calls this method; missing this check may cause an infinite loop. + // //Do not auto-install modules in this method for regular modules. // await xncfModuleServiceExtension.InstallModuleAsync(this.Uid).ConfigureAwait(false); //} @@ -86,7 +86,7 @@ public override IServiceCollection AddXncfModule(IServiceCollection services, IC } ///// - ///// 安装模块并设置菜单 + ///// Install module and configure menu ///// ///// ///// @@ -96,8 +96,8 @@ public override IServiceCollection AddXncfModule(IServiceCollection services, IC // var systemModule = xncfModuleServiceExtension.GetObject(z => z.Uid == this.Uid); // if (systemModule == null) // { - // //只在未安装的情况下进行安装,InstallModuleAsync会访问到此方法,不做判断可能会引发死循环。 - // //常规模块中请勿在此方法中自动安装模块! + // //Install only when not installed. InstallModuleAsync calls this method; missing this check may cause an infinite loop. + // //Do not auto-install modules in this method for regular modules. // await xncfModuleServiceExtension.InstallModuleAsync(this.Uid).ConfigureAwait(false); // } From a4fee3d5eed01ff9634d264fa79b145bc942777a Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 02:27:48 -0700 Subject: [PATCH 08/21] translate: Chinese comments to English in XncfModuleServiceExtension --- .../Services/XncfModuleServiceExtension.cs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Services/XncfModuleServiceExtension.cs b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Services/XncfModuleServiceExtension.cs index 1f67f5388..ec3f03937 100644 --- a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Services/XncfModuleServiceExtension.cs +++ b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Services/XncfModuleServiceExtension.cs @@ -20,7 +20,7 @@ namespace Senparc.Xncf.XncfModuleManager.Domain.Services { /// - /// TODO:此接口需要开放到 OHS,对外提供服务 + /// TODO: This interface needs to be exposed to OHS for external services. /// public class XncfModuleServiceExtension : XncfModuleService { @@ -28,7 +28,7 @@ public class XncfModuleServiceExtension : XncfModuleService private readonly Lazy _sysRoleService; /// - /// 获取模块起始触发 URL + /// Get module start trigger URL. /// /// /// @@ -44,10 +44,10 @@ public XncfModuleServiceExtension(IRepositoryBase repo, Lazy - /// 安装模块 + /// Install module. /// - /// 模块 Uid - /// 是否在安装完成后添加到管理员菜单 + /// Module Uid + /// Whether to add the module to the admin menu after installation. /// //public async Task, string, InstallOrUpdate?>> InstallModuleAsync(string uid) public async Task<(PagedList XncfModuleList, string scanAndInstallResult, InstallOrUpdate? InstallOrUpdate)> InstallModuleAsync(string uid, bool addMenu = true) @@ -69,7 +69,7 @@ public XncfModuleServiceExtension(IRepositoryBase repo, Lazy xncfModules = await base.GetObjectListAsync(1, 999, z => true, z => z.AddTime, Ncf.Core.Enums.OrderingType.Descending).ConfigureAwait(false); @@ -77,7 +77,7 @@ public XncfModuleServiceExtension(IRepositoryBase repo, Lazy { @@ -89,7 +89,7 @@ public XncfModuleServiceExtension(IRepositoryBase repo, Lazy repo, Lazy - /// 安装模块之后,安装菜单 + /// Install menu after module installation. /// /// /// @@ -112,14 +112,14 @@ public async Task InstallMenuAsync(IXncfRegister register, InstallOrUpdate insta if (installOrUpdate == InstallOrUpdate.Update && currentMenu != null) { - //更新菜单 + //Update menu menuDto = _sysMenuService.Value.Mapper.Map(currentMenu); - menuDto.MenuName = register.MenuName;//更新菜单名称 - menuDto.Icon = register.Icon;//更新菜单图标 + menuDto.MenuName = register.MenuName;//Update menu name + menuDto.Icon = register.Icon;//Update menu icon } else { - //新建菜单 + //Create menu var icon = register.Icon.IsNullOrEmpty() ? "fa fa-bars" : register.Icon; var order = 20; switch (register.Uid) @@ -140,9 +140,9 @@ public async Task InstallMenuAsync(IXncfRegister register, InstallOrUpdate insta if (installOrUpdate == InstallOrUpdate.Install) { - //更新菜单信息 + //Update menu information - //获取管理员组的 RoleId + //Get RoleId of the administrator group var administratorRoleCode = Senparc.Ncf.Service.Config.SYSROLE_ADMINISTRATOR_ROLE_CODE; var sysRole = await _sysRoleService.Value.GetObjectAsync(z => z.RoleCode == administratorRoleCode); @@ -170,7 +170,7 @@ public async Task InstallMenuAsync(IXncfRegister register, InstallOrUpdate insta /// - /// 获取显示的版本号信息(如果有新版本,则显示格式:[旧版本号] -> [新版本号] + /// Get display version information (if a newer version exists, format: [oldVersion] -> [newVersion]). /// /// /// @@ -186,7 +186,7 @@ public string GetVersionDisplayName(List xncfModules, IXncfRegister } /// - /// 获取未安装的 Xncf(或版本不同) + /// Get uninstalled Xncf modules (or modules with different versions). /// /// public List GetUnInstallXncfModule(List xncfModules) @@ -196,7 +196,7 @@ public List GetUnInstallXncfModule(List xncfModules) } /// - /// 获取未安装的 Xncf + /// Get only uninstalled Xncf modules. /// /// public List GetOnlyUnInstallXncfModule(List xncfModules) @@ -206,7 +206,7 @@ public List GetOnlyUnInstallXncfModule(List xncfModul } /// - /// 获取版本不同的 Xncf + /// Get Xncf modules with different versions. /// /// public List GetUpdatedInstallXncfModule(List xncfModules) From b0dbc9a1e9db864570f8e3a2fdd1eea0672b4f09 Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 02:53:10 -0700 Subject: [PATCH 09/21] translate: README/docs PROMPTRANGE_OPTIMIZATION_COMPLETE to English, preserve .cn.md --- PROMPTRANGE_OPTIMIZATION_COMPLETE.cn.md | 127 ++++++++++++++++++ PROMPTRANGE_OPTIMIZATION_COMPLETE.md | 164 ++++++++++++------------ 2 files changed, 209 insertions(+), 82 deletions(-) create mode 100644 PROMPTRANGE_OPTIMIZATION_COMPLETE.cn.md diff --git a/PROMPTRANGE_OPTIMIZATION_COMPLETE.cn.md b/PROMPTRANGE_OPTIMIZATION_COMPLETE.cn.md new file mode 100644 index 000000000..88c2b2f56 --- /dev/null +++ b/PROMPTRANGE_OPTIMIZATION_COMPLETE.cn.md @@ -0,0 +1,127 @@ +# PromptRange 自动优化功能 - 实施完成 + +## 📌 快速导航 + +完整的技术文档请查看: [`docs/PromptRange-Auto-Optimization-Guide.md`](./docs/PromptRange-Auto-Optimization-Guide.md) + +--- + +## ✅ 已完成的功能 + +### 1. 智能初始化检测 +- 首次使用时自动检测 PromptCatalyzer 是否已初始化 +- 引导用户选择 AI Model 并自动创建所需资源 +- 支持多个 AI Model 选择(只显示 Chat 类型) + +### 2. 基于打分的自动优化建议 + +#### 场景 A: 单次打分后的即时建议 +- **触发**: AI 评分或手动评分完成后 +- **条件**: 分数 < 6.0 分 +- **提示**: 弹出确认对话框,引导用户立即优化 + +#### 场景 B: 切换 Prompt 时的平均分提示 +- **触发**: 切换到某个 Prompt 后 +- **条件**: 平均分 < 6.0 分 +- **提示**: 右下角通知(非阻塞式) + +### 3. 完整的优化工作流 +- 收集完整上下文(Prompt 内容、参数、用户需求) +- 调用 AgentsManager 的 PromptCatalyzer 进行 AI 分析 +- 生成新的优化版本 +- 显示详细结果(参数对比、预测分数、优化说明) +- 自动刷新列表并切换到新 Prompt + +--- + +## 🚀 快速测试 + +### 编译项目 +```bash +dotnet build +``` + +### 测试流程 +1. 打开 PromptRange 页面 +2. 选择一个 Prompt,点击"优化"按钮 +3. **首次使用**: 选择 AI Model → 初始化(30-60秒)→ 自动打开优化对话框 +4. **后续使用**: 直接打开优化对话框 +5. 输入优化需求 → 等待结果 → 查看优化后的 Prompt + +### 测试自动建议 +1. 对 PromptResult 进行打分 +2. 如果分数 < 6.0,会弹出优化建议 +3. 点击"立即优化"进入优化流程 + +--- + +## 📁 新增/修改的文件 + +### 新增 +- ✅ `src/Extensions/Senparc.Xncf.AgentsManager/Application/AppService/PromptCatalyzerInitAppService.cs` +- ✅ `docs/PromptRange-Auto-Optimization-Guide.md`(详细技术文档) + +### 修改 +- ✅ `src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/prompt.js` + - 新增 `checkScoreAndSuggestOptimization()` - 打分后优化建议 + - 新增 `checkPromptAverageScoreAndSuggest()` - 平均分优化建议 + - 更新 `saveManualScore()` - 集成优化建议 + - 更新 `getPromptetail()` - 集成平均分检查 + +--- + +## 🎯 核心改进 + +### 用户体验 +- **零门槛**: 首次使用自动引导初始化 +- **智能提示**: 基于数据的个性化建议 +- **非侵入**: 不阻塞正常操作流程 +- **全自动**: 从初始化到优化全程自动化 + +### 技术实现 +- **EventBus 集成**: 使用高性能事件系统 +- **模块解耦**: AgentsManager 和 PromptRange 职责清晰 +- **错误处理**: 完善的异常捕获和用户提示 +- **日志完善**: 关键步骤均有日志记录 + +--- + +## 📊 验收状态 + +### 后端 API +- [x] CheckStatus - 检查初始化状态 +- [x] GetAvailableModels - 获取可用模型 +- [x] Initialize - 执行初始化 +- [x] OptimizeAsync - 执行优化 + +### 前端功能 +- [x] 初始化检测与引导 +- [x] Model 选择界面 +- [x] 优化对话框 +- [x] 打分后优化建议(确认框) +- [x] 平均分优化建议(通知) +- [x] 结果展示与自动切换 + +### 编译测试 +- [x] AgentsManager 编译通过 +- [x] PromptRange 编译通过 +- [ ] 功能端到端测试(需用户运行应用测试) + +--- + +## 🎉 总结 + +PromptRange 的 AI 自动优化功能已全面实现,包括: + +1. **智能初始化**: 自动检测并引导创建所需资源 +2. **双重建议机制**: 单次打分 + 平均分两种触发方式 +3. **完整优化流程**: 从分析到创建新版本的全自动化 +4. **优秀的用户体验**: 友好提示、加载状态、详细结果展示 + +**立即启动应用并测试!** 🚀 + +--- + +## 📞 需要帮助? + +查看详细文档: [`docs/PromptRange-Auto-Optimization-Guide.md`](./docs/PromptRange-Auto-Optimization-Guide.md) diff --git a/PROMPTRANGE_OPTIMIZATION_COMPLETE.md b/PROMPTRANGE_OPTIMIZATION_COMPLETE.md index 88c2b2f56..b8534ebe4 100644 --- a/PROMPTRANGE_OPTIMIZATION_COMPLETE.md +++ b/PROMPTRANGE_OPTIMIZATION_COMPLETE.md @@ -1,127 +1,127 @@ -# PromptRange 自动优化功能 - 实施完成 +# PromptRange Auto-Optimization - Implementation Complete -## 📌 快速导航 +## 📌 Quick Navigation -完整的技术文档请查看: [`docs/PromptRange-Auto-Optimization-Guide.md`](./docs/PromptRange-Auto-Optimization-Guide.md) +For the full technical documentation, see: [docs/PromptRange-Auto-Optimization-Guide.md](./docs/PromptRange-Auto-Optimization-Guide.md) --- -## ✅ 已完成的功能 +## ✅ Completed Features -### 1. 智能初始化检测 -- 首次使用时自动检测 PromptCatalyzer 是否已初始化 -- 引导用户选择 AI Model 并自动创建所需资源 -- 支持多个 AI Model 选择(只显示 Chat 类型) +### 1. Smart initialization detection +- Automatically checks whether PromptCatalyzer is initialized on first use. +- Guides users to choose an AI model and automatically creates required resources. +- Supports multiple AI model options (chat-type models only). -### 2. 基于打分的自动优化建议 +### 2. Score-based auto-optimization suggestions -#### 场景 A: 单次打分后的即时建议 -- **触发**: AI 评分或手动评分完成后 -- **条件**: 分数 < 6.0 分 -- **提示**: 弹出确认对话框,引导用户立即优化 +#### Scenario A: Immediate suggestion after single score +- Trigger: AI scoring or manual scoring completes. +- Condition: Score < 6.0. +- Prompt style: Confirmation dialog to guide immediate optimization. -#### 场景 B: 切换 Prompt 时的平均分提示 -- **触发**: 切换到某个 Prompt 后 -- **条件**: 平均分 < 6.0 分 -- **提示**: 右下角通知(非阻塞式) +#### Scenario B: Average score suggestion when switching prompts +- Trigger: User switches to a prompt. +- Condition: Average score < 6.0. +- Prompt style: Bottom-right notification (non-blocking). -### 3. 完整的优化工作流 -- 收集完整上下文(Prompt 内容、参数、用户需求) -- 调用 AgentsManager 的 PromptCatalyzer 进行 AI 分析 -- 生成新的优化版本 -- 显示详细结果(参数对比、预测分数、优化说明) -- 自动刷新列表并切换到新 Prompt +### 3. End-to-end optimization workflow +- Collect complete context (prompt content, parameters, user requirements). +- Call PromptCatalyzer in AgentsManager for AI analysis. +- Generate a new optimized version. +- Show detailed results (parameter comparison, predicted score, optimization notes). +- Auto-refresh list and switch to the new prompt. --- -## 🚀 快速测试 +## 🚀 Quick Test -### 编译项目 +### Build project ```bash dotnet build ``` -### 测试流程 -1. 打开 PromptRange 页面 -2. 选择一个 Prompt,点击"优化"按钮 -3. **首次使用**: 选择 AI Model → 初始化(30-60秒)→ 自动打开优化对话框 -4. **后续使用**: 直接打开优化对话框 -5. 输入优化需求 → 等待结果 → 查看优化后的 Prompt +### Test flow +1. Open the PromptRange page. +2. Select a prompt and click the Optimize button. +3. First use: choose AI model -> initialize (30-60s) -> optimization dialog opens automatically. +4. Subsequent use: optimization dialog opens directly. +5. Enter optimization requirements -> wait for result -> review optimized prompt. -### 测试自动建议 -1. 对 PromptResult 进行打分 -2. 如果分数 < 6.0,会弹出优化建议 -3. 点击"立即优化"进入优化流程 +### Test auto-suggestions +1. Score a PromptResult. +2. If score < 6.0, optimization suggestion dialog will pop up. +3. Click Optimize Now to enter the optimization flow. --- -## 📁 新增/修改的文件 +## 📁 Added/Modified Files -### 新增 -- ✅ `src/Extensions/Senparc.Xncf.AgentsManager/Application/AppService/PromptCatalyzerInitAppService.cs` -- ✅ `docs/PromptRange-Auto-Optimization-Guide.md`(详细技术文档) +### Added +- ✅ src/Extensions/Senparc.Xncf.AgentsManager/Application/AppService/PromptCatalyzerInitAppService.cs +- ✅ docs/PromptRange-Auto-Optimization-Guide.md (detailed technical documentation) -### 修改 -- ✅ `src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/prompt.js` - - 新增 `checkScoreAndSuggestOptimization()` - 打分后优化建议 - - 新增 `checkPromptAverageScoreAndSuggest()` - 平均分优化建议 - - 更新 `saveManualScore()` - 集成优化建议 - - 更新 `getPromptetail()` - 集成平均分检查 +### Modified +- ✅ src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/prompt.js + - Added checkScoreAndSuggestOptimization() - post-score optimization suggestion + - Added checkPromptAverageScoreAndSuggest() - average-score suggestion + - Updated saveManualScore() - integrated optimization suggestions + - Updated getPromptetail() - integrated average-score check --- -## 🎯 核心改进 +## 🎯 Core Improvements -### 用户体验 -- **零门槛**: 首次使用自动引导初始化 -- **智能提示**: 基于数据的个性化建议 -- **非侵入**: 不阻塞正常操作流程 -- **全自动**: 从初始化到优化全程自动化 +### User experience +- Zero barrier: first-use initialization is fully guided. +- Smart prompting: data-driven personalized suggestions. +- Non-intrusive: does not block normal operations. +- End-to-end automation: initialization through optimization is automated. -### 技术实现 -- **EventBus 集成**: 使用高性能事件系统 -- **模块解耦**: AgentsManager 和 PromptRange 职责清晰 -- **错误处理**: 完善的异常捕获和用户提示 -- **日志完善**: 关键步骤均有日志记录 +### Technical implementation +- EventBus integration: uses high-performance event system. +- Module decoupling: clear responsibility split between AgentsManager and PromptRange. +- Error handling: robust exception capture and user-facing feedback. +- Logging: key workflow steps are fully logged. --- -## 📊 验收状态 +## 📊 Acceptance Status -### 后端 API -- [x] CheckStatus - 检查初始化状态 -- [x] GetAvailableModels - 获取可用模型 -- [x] Initialize - 执行初始化 -- [x] OptimizeAsync - 执行优化 +### Backend API +- [x] CheckStatus - check initialization status +- [x] GetAvailableModels - get available models +- [x] Initialize - perform initialization +- [x] OptimizeAsync - perform optimization -### 前端功能 -- [x] 初始化检测与引导 -- [x] Model 选择界面 -- [x] 优化对话框 -- [x] 打分后优化建议(确认框) -- [x] 平均分优化建议(通知) -- [x] 结果展示与自动切换 +### Frontend features +- [x] Initialization detection and guided flow +- [x] Model selection UI +- [x] Optimization dialog +- [x] Post-score optimization suggestion (confirmation dialog) +- [x] Average-score optimization suggestion (notification) +- [x] Result display and auto-switch -### 编译测试 -- [x] AgentsManager 编译通过 -- [x] PromptRange 编译通过 -- [ ] 功能端到端测试(需用户运行应用测试) +### Build verification +- [x] AgentsManager builds successfully +- [x] PromptRange builds successfully +- [ ] End-to-end functional testing (requires running app by user) --- -## 🎉 总结 +## 🎉 Summary -PromptRange 的 AI 自动优化功能已全面实现,包括: +The AI auto-optimization feature in PromptRange is now fully implemented, including: -1. **智能初始化**: 自动检测并引导创建所需资源 -2. **双重建议机制**: 单次打分 + 平均分两种触发方式 -3. **完整优化流程**: 从分析到创建新版本的全自动化 -4. **优秀的用户体验**: 友好提示、加载状态、详细结果展示 +1. Smart initialization: automatically detects and guides required resource creation. +2. Dual suggestion mechanism: triggered by single score and average score. +3. Complete optimization flow: full automation from analysis to new version creation. +4. Excellent user experience: friendly prompts, loading status, and detailed result display. -**立即启动应用并测试!** 🚀 +Start the application and test now. --- -## 📞 需要帮助? +## 📞 Need Help? -查看详细文档: [`docs/PromptRange-Auto-Optimization-Guide.md`](./docs/PromptRange-Auto-Optimization-Guide.md) +See detailed guide: [docs/PromptRange-Auto-Optimization-Guide.md](./docs/PromptRange-Auto-Optimization-Guide.md) From 0df3bc5b0a3f38f9a86c24c0696f79ead08ca100 Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 02:53:59 -0700 Subject: [PATCH 10/21] translate: README/docs TODO_CHECKLIST to English, preserve .cn.md --- TODO_CHECKLIST.cn.md | 137 ++++++++++++++++++++++++++++++++++++ TODO_CHECKLIST.md | 164 +++++++++++++++++++++---------------------- 2 files changed, 219 insertions(+), 82 deletions(-) create mode 100644 TODO_CHECKLIST.cn.md diff --git a/TODO_CHECKLIST.cn.md b/TODO_CHECKLIST.cn.md new file mode 100644 index 000000000..427005ddb --- /dev/null +++ b/TODO_CHECKLIST.cn.md @@ -0,0 +1,137 @@ +# 🎯 Prompt 优化功能实施清单 + +## 📋 准备工作 + +### Step 0: 确认依赖服务 ⏳ + +**需要您提供的信息:** + +1. **AI 调用服务** + - [ ] 确认项目中用于调用 AI 的服务类名 + - [ ] 确认该服务的调用方法签名(如 `ChatAsync`, `CompletionAsync` 等) + - [ ] 确认如何在 Handler 中注入该服务 + + **示例问题**: + ```csharp + // 是这样的吗? + public class AIKernelService + { + public Task ChatAsync(string systemPrompt, string userMessage, ...); + } + + // 还是这样? + public class ChatService + { + public Task SendAsync(ChatRequest request); + } + ``` + +2. **PromptItem 查询方法** + - [ ] 确认 `PromptItemService` 是否有通过 `FullVersion` 查询的方法 + - [ ] 如果没有,我将帮您实现一个 + +3. **测试环境** + - [ ] 确认有可用的 AI Model 配置 + - [ ] 确认 AI API Key 已配置 + +--- + +## 🔧 实施任务 + +### Task 1: 更新 PromptOptimizationRequestHandler ⏳ + +**文件**: `src/Extensions/Senparc.Xncf.PromptRange/Application/EventHandlers/PromptOptimizationRequestHandler.cs` + +**当前问题**: +- 使用旧的事件格式(缺少 `NewPromptContent` 和 `Parameters`) +- 只是模拟优化,没有真正调用 AI + +**需要实现**: +1. [ ] 添加必要的服务依赖注入(AI 服务、PromptRangeService 等) +2. [ ] 实现 `GetPromptItemByCode()` 方法来解析和查询 PromptItem +3. [ ] 实现 `BuildOptimizationSystemPrompt()` 方法 +4. [ ] 实现 `BuildOptimizationUserInput()` 方法 +5. [ ] 调用 AI 服务进行优化 +6. [ ] 解析 AI 返回的 JSON(包含优化后的内容和参数) +7. [ ] 创建新的 PromptItem +8. [ ] 发布正确格式的响应事件 + +**我将提供**:完整的实现代码模板(需要您填入 AI 服务调用部分) + +--- + +### Task 2: 更新前端 JavaScript ⏳ + +**文件**: `src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/prompt.js` + +**需要修改的方法**: `executeOptimize()` (约在第 3010 行) + +**需要实现**: +1. [ ] 获取当前 Prompt 的完整信息(包括参数) +2. [ ] 构建包含 `promptContent` 和 `context` 的请求对象 +3. [ ] 显示更详细的优化结果(包括参数变化) +4. [ ] 刷新 Prompt 列表 +5. [ ] 可选:自动切换到新的 Prompt + +**我将提供**:完整的 JavaScript 代码 + +--- + +### Task 3: 测试和验证 ⏳ + +**测试步骤**: +1. [ ] 单元测试:测试 AI 优化逻辑 +2. [ ] 集成测试:测试完整流程 +3. [ ] 端到端测试:从前端点击到看到结果 + +**验收标准**: +- [ ] 首次调用时自动创建 "PromptCatalyzer" Agent +- [ ] 能够调用 AI 优化 Prompt 内容 +- [ ] 能够优化参数(Temperature 等) +- [ ] 创建新的 PromptItem 并返回 PromptCode +- [ ] 前端显示优化结果 + +--- + +## 📊 进度追踪 + +| 任务 | 状态 | 完成时间 | +|------|------|----------| +| Step 0: 确认依赖 | ⏳ 等待中 | - | +| Task 1: 更新 Handler | ⏳ 待开始 | - | +| Task 2: 更新前端 | ⏳ 待开始 | - | +| Task 3: 测试验证 | ⏳ 待开始 | - | + +--- + +## 🚀 开始行动 + +**当前步骤**: Step 0 - 需要您提供 AI 服务的信息 + +**请您做的事**: +1. 查看您的项目中 AI 调用服务的代码 +2. 回复以下信息: + - AI 服务的类名 + - AI 服务的调用方法签名 + - 如何注入该服务 + +**示例回复**: +``` +我的 AI 服务是 `Senparc.AI.Kernel.AIChatService`, +调用方法是 `Task ChatAsync(string prompt, ChatOptions options)`, +通过构造函数注入。 +``` + +收到您的信息后,我将立即为您提供完整的实现代码! + +--- + +## 📞 需要帮助? + +如果遇到任何问题,请随时告诉我: +- AI 服务相关问题 +- 代码实现疑问 +- 测试过程中的错误 +- 任何其他问题 + +我会逐步帮您解决! diff --git a/TODO_CHECKLIST.md b/TODO_CHECKLIST.md index 427005ddb..a28b744af 100644 --- a/TODO_CHECKLIST.md +++ b/TODO_CHECKLIST.md @@ -1,137 +1,137 @@ -# 🎯 Prompt 优化功能实施清单 +# 🎯 Prompt Optimization Implementation Checklist -## 📋 准备工作 +## 📋 Preparation -### Step 0: 确认依赖服务 ⏳ +### Step 0: Confirm dependency services ⏳ -**需要您提供的信息:** +**Information needed from you:** -1. **AI 调用服务** - - [ ] 确认项目中用于调用 AI 的服务类名 - - [ ] 确认该服务的调用方法签名(如 `ChatAsync`, `CompletionAsync` 等) - - [ ] 确认如何在 Handler 中注入该服务 - - **示例问题**: +1. **AI invocation service** + - [ ] Confirm the service class name used for AI calls in the project. + - [ ] Confirm the method signature (for example, ChatAsync, CompletionAsync). + - [ ] Confirm how this service is injected into handlers. + + **Example questions:** ```csharp - // 是这样的吗? + // Is it like this? public class AIKernelService { public Task ChatAsync(string systemPrompt, string userMessage, ...); } - - // 还是这样? + + // Or like this? public class ChatService { public Task SendAsync(ChatRequest request); } ``` -2. **PromptItem 查询方法** - - [ ] 确认 `PromptItemService` 是否有通过 `FullVersion` 查询的方法 - - [ ] 如果没有,我将帮您实现一个 +2. **PromptItem query method** + - [ ] Confirm whether PromptItemService has a query method by FullVersion. + - [ ] If not, I can help implement one. -3. **测试环境** - - [ ] 确认有可用的 AI Model 配置 - - [ ] 确认 AI API Key 已配置 +3. **Test environment** + - [ ] Confirm that an available AI model is configured. + - [ ] Confirm that the AI API key is configured. --- -## 🔧 实施任务 +## 🔧 Implementation Tasks -### Task 1: 更新 PromptOptimizationRequestHandler ⏳ +### Task 1: Update PromptOptimizationRequestHandler ⏳ -**文件**: `src/Extensions/Senparc.Xncf.PromptRange/Application/EventHandlers/PromptOptimizationRequestHandler.cs` +**File**: src/Extensions/Senparc.Xncf.PromptRange/Application/EventHandlers/PromptOptimizationRequestHandler.cs -**当前问题**: -- 使用旧的事件格式(缺少 `NewPromptContent` 和 `Parameters`) -- 只是模拟优化,没有真正调用 AI +**Current issues:** +- Uses old event format (missing NewPromptContent and Parameters). +- Only simulates optimization; no real AI call. -**需要实现**: -1. [ ] 添加必要的服务依赖注入(AI 服务、PromptRangeService 等) -2. [ ] 实现 `GetPromptItemByCode()` 方法来解析和查询 PromptItem -3. [ ] 实现 `BuildOptimizationSystemPrompt()` 方法 -4. [ ] 实现 `BuildOptimizationUserInput()` 方法 -5. [ ] 调用 AI 服务进行优化 -6. [ ] 解析 AI 返回的 JSON(包含优化后的内容和参数) -7. [ ] 创建新的 PromptItem -8. [ ] 发布正确格式的响应事件 +**Required implementation:** +1. [ ] Add required service dependency injection (AI service, PromptRangeService, etc.). +2. [ ] Implement GetPromptItemByCode() for parsing and querying PromptItem. +3. [ ] Implement BuildOptimizationSystemPrompt(). +4. [ ] Implement BuildOptimizationUserInput(). +5. [ ] Call AI service for optimization. +6. [ ] Parse returned AI JSON (including optimized content and parameters). +7. [ ] Create new PromptItem. +8. [ ] Publish response event in the correct format. -**我将提供**:完整的实现代码模板(需要您填入 AI 服务调用部分) +**I will provide**: a full implementation template (you only need to fill in the AI service invocation part). --- -### Task 2: 更新前端 JavaScript ⏳ +### Task 2: Update frontend JavaScript ⏳ -**文件**: `src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/prompt.js` +**File**: src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/prompt.js -**需要修改的方法**: `executeOptimize()` (约在第 3010 行) +**Method to modify**: executeOptimize() (around line 3010) -**需要实现**: -1. [ ] 获取当前 Prompt 的完整信息(包括参数) -2. [ ] 构建包含 `promptContent` 和 `context` 的请求对象 -3. [ ] 显示更详细的优化结果(包括参数变化) -4. [ ] 刷新 Prompt 列表 -5. [ ] 可选:自动切换到新的 Prompt +**Required implementation:** +1. [ ] Get full prompt information (including parameters). +2. [ ] Build request payload including promptContent and context. +3. [ ] Show more detailed optimization result (including parameter changes). +4. [ ] Refresh prompt list. +5. [ ] Optional: auto-switch to the new prompt. -**我将提供**:完整的 JavaScript 代码 +**I will provide**: complete JavaScript code. --- -### Task 3: 测试和验证 ⏳ +### Task 3: Testing and verification ⏳ -**测试步骤**: -1. [ ] 单元测试:测试 AI 优化逻辑 -2. [ ] 集成测试:测试完整流程 -3. [ ] 端到端测试:从前端点击到看到结果 +**Test steps:** +1. [ ] Unit test: AI optimization logic. +2. [ ] Integration test: full workflow. +3. [ ] End-to-end test: click from frontend to result display. -**验收标准**: -- [ ] 首次调用时自动创建 "PromptCatalyzer" Agent -- [ ] 能够调用 AI 优化 Prompt 内容 -- [ ] 能够优化参数(Temperature 等) -- [ ] 创建新的 PromptItem 并返回 PromptCode -- [ ] 前端显示优化结果 +**Acceptance criteria:** +- [ ] Auto-create PromptCatalyzer agent on first invocation. +- [ ] Successfully call AI to optimize prompt content. +- [ ] Successfully optimize parameters (for example, Temperature). +- [ ] Create new PromptItem and return PromptCode. +- [ ] Frontend displays optimization result correctly. --- -## 📊 进度追踪 +## 📊 Progress Tracking -| 任务 | 状态 | 完成时间 | +| Task | Status | Completion Time | |------|------|----------| -| Step 0: 确认依赖 | ⏳ 等待中 | - | -| Task 1: 更新 Handler | ⏳ 待开始 | - | -| Task 2: 更新前端 | ⏳ 待开始 | - | -| Task 3: 测试验证 | ⏳ 待开始 | - | +| Step 0: Confirm dependencies | ⏳ Waiting | - | +| Task 1: Update handler | ⏳ Not started | - | +| Task 2: Update frontend | ⏳ Not started | - | +| Task 3: Testing & verification | ⏳ Not started | - | --- -## 🚀 开始行动 +## 🚀 Start Action -**当前步骤**: Step 0 - 需要您提供 AI 服务的信息 +**Current step**: Step 0 - AI service information is required. -**请您做的事**: -1. 查看您的项目中 AI 调用服务的代码 -2. 回复以下信息: - - AI 服务的类名 - - AI 服务的调用方法签名 - - 如何注入该服务 +**What you need to do:** +1. Check AI invocation service code in your project. +2. Reply with the following: + - AI service class name + - AI service method signature + - Injection approach -**示例回复**: +**Example reply:** ``` -我的 AI 服务是 `Senparc.AI.Kernel.AIChatService`, -调用方法是 `Task ChatAsync(string prompt, ChatOptions options)`, -通过构造函数注入。 +My AI service is Senparc.AI.Kernel.AIChatService, +method signature is Task ChatAsync(string prompt, ChatOptions options), +injected via constructor. ``` -收到您的信息后,我将立即为您提供完整的实现代码! +After receiving this info, I will provide a complete implementation immediately. --- -## 📞 需要帮助? +## 📞 Need Help? -如果遇到任何问题,请随时告诉我: -- AI 服务相关问题 -- 代码实现疑问 -- 测试过程中的错误 -- 任何其他问题 +If you run into any issue, tell me anytime: +- AI service related questions +- Implementation details +- Test errors +- Any other blockers -我会逐步帮您解决! +I will help you resolve them step by step. From 33d94d1721cfc65bb6de11f6250590316f61cc7b Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 02:54:43 -0700 Subject: [PATCH 11/21] translate: README/docs TASK1_COMPLETION_SUMMARY to English, preserve .cn.md --- TASK1_COMPLETION_SUMMARY.cn.md | 171 ++++++++++++++++++++++++++++++++ TASK1_COMPLETION_SUMMARY.md | 172 +++++++++++++++++---------------- 2 files changed, 261 insertions(+), 82 deletions(-) create mode 100644 TASK1_COMPLETION_SUMMARY.cn.md diff --git a/TASK1_COMPLETION_SUMMARY.cn.md b/TASK1_COMPLETION_SUMMARY.cn.md new file mode 100644 index 000000000..dba1bb3d1 --- /dev/null +++ b/TASK1_COMPLETION_SUMMARY.cn.md @@ -0,0 +1,171 @@ +# 任务 1 完成总结:EventBus 高并发优化 + +## ✅ 已完成的改进 + +### 1. **高并发支持** +- ✅ 重构 `EventBusHostedService`,支持并发事件处理 +- ✅ 使用 `SemaphoreSlim` 控制最大并发度(可配置) +- ✅ 修改 `UnboundedChannelOptions.SingleReader = false`,支持多消费者 + +### 2. **防止重复引用机制** +- ✅ 在 `InMemoryEventBus` 中实现事件 ID 追踪 +- ✅ 使用 `ConcurrentDictionary` 记录已处理的事件 +- ✅ 滑动窗口机制(10 分钟过期自动清理) +- ✅ 线程安全的重复检测 + +### 3. **失败重试机制** +- ✅ 实现指数退避策略(1s, 2s, 4s...) +- ✅ 可配置重试次数和是否启用重试 +- ✅ 详细的重试日志记录 + +### 4. **配置选项** +新增 `EventBusOptions` 类,支持: +```csharp +public class EventBusOptions +{ + public int MaxConcurrency { get; set; } // 最大并发数 + public bool EnableDuplicateDetection { get; set; } // 启用重复检测 + public bool RetryOnFailure { get; set; } // 启用失败重试 + public int MaxRetryAttempts { get; set; } // 最大重试次数 +} +``` + +### 5. **性能监控** +- ✅ 事件处理时间记录 +- ✅ Handler 执行时间记录 +- ✅ 重复事件告警 +- ✅ 详细的日志记录(支持 Debug/Information 级别) + +## 📊 性能对比 + +### 优化前(串行处理) +- 10,000 个事件处理时间:~50-100 秒 +- 并发度:1(完全串行) +- 吞吐量:~100-200 事件/秒 + +### 优化后(并发处理,MaxConcurrency = 20) +- 10,000 个事件处理时间:~5-10 秒 +- 并发度:20(可配置) +- 吞吐量:~1000-2000 事件/秒 +- **性能提升:10-20 倍** + +## 🎯 使用建议 + +### 高并发场景配置 +```csharp +services.AddSenparcEventBus( + options => + { + options.MaxConcurrency = Environment.ProcessorCount * 2; // CPU 核心数 * 2 + options.EnableDuplicateDetection = true; + options.RetryOnFailure = true; + options.MaxRetryAttempts = 3; + }, + typeof(YourHandler).Assembly +); +``` + +### 数据库密集型场景 +```csharp +options.MaxConcurrency = 10; // 数据库连接池大小 / 2 +``` + +### 外部 API 调用场景 +```csharp +options.MaxConcurrency = 50; // 根据外部 API QPS 限制 +``` + +## 📝 重要变更 + +### API 变更 +```csharp +// 旧版本 +services.AddSenparcEventBus(typeof(Handler).Assembly); + +// 新版本(兼容旧版本) +services.AddSenparcEventBus( + options => { /* 可选配置 */ }, + typeof(Handler).Assembly +); +``` + +### 事件基类增强 +```csharp +public abstract record IntegrationEvent : IIntegrationEvent +{ + public Guid Id { get; } = Guid.NewGuid(); + public DateTime CreationDate { get; } = DateTime.UtcNow; + + // 新增:用于日志记录的摘要信息 + public virtual string GetEventSummary() => $"{GetType().Name}[{Id:N}]"; +} +``` + +## ⚠️ 注意事项 + +1. **事件顺序不保证** + - 并发处理会打乱事件顺序 + - 如需严格顺序,设置 `MaxConcurrency = 1` + +2. **数据库连接池** + - 确保连接池大小 >= MaxConcurrency + - 建议:`MaxConcurrency = ConnectionPoolSize / 2` + +3. **内存消耗** + - 重复检测会占用内存(每个事件 ~40 字节) + - 10 分钟窗口约占用:`40 * 事件数 * 10分钟` 字节 + +4. **事件幂等性** + - 尽管有重复检测,Handler 仍应设计为幂等 + - 防止应用重启导致的重复处理 + +## 📚 文档 + +详细文档已创建: +- `/src/Basic/Senparc.Ncf.Core/EventBus/README.md` + +## ✅ 测试建议 + +### 并发测试 +```csharp +[TestMethod] +public async Task EventBus_HighConcurrency_Test() +{ + var eventCount = 10000; + var startTime = DateTime.UtcNow; + + for (int i = 0; i < eventCount; i++) + { + await _eventBus.PublishAsync(new TestEvent(i)); + } + + // 等待所有事件处理完成 + await WaitForAllEventsProcessed(); + + var duration = DateTime.UtcNow - startTime; + Console.WriteLine($"处理 {eventCount} 个事件耗时: {duration.TotalSeconds}s"); + Assert.IsTrue(duration.TotalSeconds < 20, "高并发性能不达标"); +} +``` + +### 重复检测测试 +```csharp +[TestMethod] +public async Task EventBus_DuplicateDetection_Test() +{ + var @event = new TestEvent(1); + var eventId = @event.Id; + + // 发布相同 ID 的事件两次 + await _eventBus.PublishAsync(@event); + await Task.Delay(100); + await _eventBus.PublishAsync(@event); + + // 验证只处理了一次 + Assert.AreEqual(1, _handlerCallCount); +} +``` + +## 🔄 下一步 + +任务 2:检查 AgentsManager 和 PromptRange 的实现和集成 diff --git a/TASK1_COMPLETION_SUMMARY.md b/TASK1_COMPLETION_SUMMARY.md index dba1bb3d1..5820c302f 100644 --- a/TASK1_COMPLETION_SUMMARY.md +++ b/TASK1_COMPLETION_SUMMARY.md @@ -1,62 +1,64 @@ -# 任务 1 完成总结:EventBus 高并发优化 +# Task 1 Completion Summary: EventBus High-Concurrency Optimization -## ✅ 已完成的改进 +## ✅ Completed Improvements -### 1. **高并发支持** -- ✅ 重构 `EventBusHostedService`,支持并发事件处理 -- ✅ 使用 `SemaphoreSlim` 控制最大并发度(可配置) -- ✅ 修改 `UnboundedChannelOptions.SingleReader = false`,支持多消费者 +### 1. High-concurrency support +- ✅ Refactored EventBusHostedService to support concurrent event processing. +- ✅ Added SemaphoreSlim to control maximum concurrency (configurable). +- ✅ Updated UnboundedChannelOptions.SingleReader = false to support multiple consumers. -### 2. **防止重复引用机制** -- ✅ 在 `InMemoryEventBus` 中实现事件 ID 追踪 -- ✅ 使用 `ConcurrentDictionary` 记录已处理的事件 -- ✅ 滑动窗口机制(10 分钟过期自动清理) -- ✅ 线程安全的重复检测 +### 2. Duplicate-reference prevention mechanism +- ✅ Implemented event ID tracking in InMemoryEventBus. +- ✅ Used ConcurrentDictionary to record processed events. +- ✅ Added sliding-window cleanup (auto-expire after 10 minutes). +- ✅ Ensured thread-safe duplicate detection. -### 3. **失败重试机制** -- ✅ 实现指数退避策略(1s, 2s, 4s...) -- ✅ 可配置重试次数和是否启用重试 -- ✅ 详细的重试日志记录 +### 3. Failure retry mechanism +- ✅ Implemented exponential backoff strategy (1s, 2s, 4s...). +- ✅ Added configurable retry count and retry switch. +- ✅ Added detailed retry logging. + +### 4. Configuration options +Added EventBusOptions class with support for: -### 4. **配置选项** -新增 `EventBusOptions` 类,支持: ```csharp public class EventBusOptions { - public int MaxConcurrency { get; set; } // 最大并发数 - public bool EnableDuplicateDetection { get; set; } // 启用重复检测 - public bool RetryOnFailure { get; set; } // 启用失败重试 - public int MaxRetryAttempts { get; set; } // 最大重试次数 + public int MaxConcurrency { get; set; } // Maximum concurrency + public bool EnableDuplicateDetection { get; set; } // Enable duplicate detection + public bool RetryOnFailure { get; set; } // Enable retry on failure + public int MaxRetryAttempts { get; set; } // Maximum retry attempts } ``` -### 5. **性能监控** -- ✅ 事件处理时间记录 -- ✅ Handler 执行时间记录 -- ✅ 重复事件告警 -- ✅ 详细的日志记录(支持 Debug/Information 级别) +### 5. Performance monitoring +- ✅ Event processing time tracking. +- ✅ Handler execution time tracking. +- ✅ Duplicate event warning logs. +- ✅ Detailed logging (Debug/Information levels). + +## 📊 Performance Comparison -## 📊 性能对比 +### Before optimization (serial processing) +- Time for 10,000 events: ~50-100 seconds +- Concurrency: 1 (fully serial) +- Throughput: ~100-200 events/second -### 优化前(串行处理) -- 10,000 个事件处理时间:~50-100 秒 -- 并发度:1(完全串行) -- 吞吐量:~100-200 事件/秒 +### After optimization (concurrent, MaxConcurrency = 20) +- Time for 10,000 events: ~5-10 seconds +- Concurrency: 20 (configurable) +- Throughput: ~1000-2000 events/second +- Performance improvement: 10-20x -### 优化后(并发处理,MaxConcurrency = 20) -- 10,000 个事件处理时间:~5-10 秒 -- 并发度:20(可配置) -- 吞吐量:~1000-2000 事件/秒 -- **性能提升:10-20 倍** +## 🎯 Usage Recommendations -## 🎯 使用建议 +### Configuration for high-concurrency scenarios -### 高并发场景配置 ```csharp services.AddSenparcEventBus( options => { - options.MaxConcurrency = Environment.ProcessorCount * 2; // CPU 核心数 * 2 + options.MaxConcurrency = Environment.ProcessorCount * 2; // CPU core count * 2 options.EnableDuplicateDetection = true; options.RetryOnFailure = true; options.MaxRetryAttempts = 3; @@ -65,107 +67,113 @@ services.AddSenparcEventBus( ); ``` -### 数据库密集型场景 +### Database-intensive scenarios + ```csharp -options.MaxConcurrency = 10; // 数据库连接池大小 / 2 +options.MaxConcurrency = 10; // Database connection pool size / 2 ``` -### 外部 API 调用场景 +### External API call scenarios + ```csharp -options.MaxConcurrency = 50; // 根据外部 API QPS 限制 +options.MaxConcurrency = 50; // Based on external API QPS limit ``` -## 📝 重要变更 +## 📝 Important Changes + +### API change -### API 变更 ```csharp -// 旧版本 +// Old version services.AddSenparcEventBus(typeof(Handler).Assembly); -// 新版本(兼容旧版本) +// New version (backward-compatible) services.AddSenparcEventBus( - options => { /* 可选配置 */ }, + options => { /* optional configuration */ }, typeof(Handler).Assembly ); ``` -### 事件基类增强 +### IntegrationEvent base class enhancement + ```csharp public abstract record IntegrationEvent : IIntegrationEvent { public Guid Id { get; } = Guid.NewGuid(); public DateTime CreationDate { get; } = DateTime.UtcNow; - - // 新增:用于日志记录的摘要信息 + + // New: summary info for logging public virtual string GetEventSummary() => $"{GetType().Name}[{Id:N}]"; } ``` -## ⚠️ 注意事项 +## ⚠️ Notes -1. **事件顺序不保证** - - 并发处理会打乱事件顺序 - - 如需严格顺序,设置 `MaxConcurrency = 1` +1. Event order is not guaranteed. + - Concurrent processing may reorder events. + - For strict ordering, set MaxConcurrency = 1. -2. **数据库连接池** - - 确保连接池大小 >= MaxConcurrency - - 建议:`MaxConcurrency = ConnectionPoolSize / 2` +2. Database connection pool. + - Ensure connection pool size >= MaxConcurrency. + - Recommended: MaxConcurrency = ConnectionPoolSize / 2. -3. **内存消耗** - - 重复检测会占用内存(每个事件 ~40 字节) - - 10 分钟窗口约占用:`40 * 事件数 * 10分钟` 字节 +3. Memory consumption. + - Duplicate detection uses memory (about 40 bytes per event). + - 10-minute window usage is approximately: 40 * eventCount * 10 minutes bytes. -4. **事件幂等性** - - 尽管有重复检测,Handler 仍应设计为幂等 - - 防止应用重启导致的重复处理 +4. Event idempotency. + - Even with duplicate detection, handlers should remain idempotent. + - This prevents duplicate side effects after app restart. -## 📚 文档 +## 📚 Documentation -详细文档已创建: -- `/src/Basic/Senparc.Ncf.Core/EventBus/README.md` +Detailed documentation has been created at: +- /src/Basic/Senparc.Ncf.Core/EventBus/README.md -## ✅ 测试建议 +## ✅ Testing Recommendations + +### Concurrency test -### 并发测试 ```csharp [TestMethod] public async Task EventBus_HighConcurrency_Test() { var eventCount = 10000; var startTime = DateTime.UtcNow; - + for (int i = 0; i < eventCount; i++) { await _eventBus.PublishAsync(new TestEvent(i)); } - - // 等待所有事件处理完成 + + // Wait until all events are processed await WaitForAllEventsProcessed(); - + var duration = DateTime.UtcNow - startTime; - Console.WriteLine($"处理 {eventCount} 个事件耗时: {duration.TotalSeconds}s"); - Assert.IsTrue(duration.TotalSeconds < 20, "高并发性能不达标"); + Console.WriteLine($"Processed {eventCount} events in: {duration.TotalSeconds}s"); + Assert.IsTrue(duration.TotalSeconds < 20, "High-concurrency performance does not meet target"); } ``` -### 重复检测测试 +### Duplicate detection test + ```csharp [TestMethod] public async Task EventBus_DuplicateDetection_Test() { var @event = new TestEvent(1); var eventId = @event.Id; - - // 发布相同 ID 的事件两次 + + // Publish same ID event twice await _eventBus.PublishAsync(@event); await Task.Delay(100); await _eventBus.PublishAsync(@event); - - // 验证只处理了一次 + + // Verify it was processed only once Assert.AreEqual(1, _handlerCallCount); } ``` -## 🔄 下一步 +## 🔄 Next Step -任务 2:检查 AgentsManager 和 PromptRange 的实现和集成 +Task 2: Inspect implementation and integration between AgentsManager and PromptRange. From 19bfab3b0541e04b8eda54144802bf8f53b83138 Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 02:55:31 -0700 Subject: [PATCH 12/21] translate: README/docs EVENTBUS_QUICK_REFERENCE to English, preserve .cn.md --- EVENTBUS_QUICK_REFERENCE.cn.md | 179 +++++++++++++++++++++++++++++++++ EVENTBUS_QUICK_REFERENCE.md | 142 +++++++++++++------------- 2 files changed, 250 insertions(+), 71 deletions(-) create mode 100644 EVENTBUS_QUICK_REFERENCE.cn.md diff --git a/EVENTBUS_QUICK_REFERENCE.cn.md b/EVENTBUS_QUICK_REFERENCE.cn.md new file mode 100644 index 000000000..c74b5ae12 --- /dev/null +++ b/EVENTBUS_QUICK_REFERENCE.cn.md @@ -0,0 +1,179 @@ +# EventBus 循环引用防护 - 快速参考 + +## 🚀 快速开始 + +### 1. 在 Handler 中发布派生事件 + +```csharp +public class MyRequestHandler : IIntegrationEventHandler +{ + private readonly IEventBus _eventBus; + + public async Task Handle(MyRequestEvent @event, CancellationToken ct) + { + // ... 处理业务逻辑 ... + + var response = new MyResponseEvent(result); + + // ⭐ 使用 PublishDerivedAsync(自动继承链信息) + await _eventBus.PublishDerivedAsync(response, @event); + } +} +``` + +### 2. 配置防护选项 + +```csharp +services.AddSenparcEventBus(options => +{ + options.MaxEventChainDepth = 10; // 最大深度 + options.EnableCircularReferenceDetection = true; // 循环检测 +}, +typeof(YourAssembly).Assembly); +``` + +--- + +## 🛡️ 三层防护机制 + +### 第一层: 深度限制 +``` +检查时机: 运行时(处理事件前) +检查逻辑: event.Depth >= MaxEventChainDepth +处理方式: 丢弃事件 + 记录错误日志 +默认限制: 10 层 +``` + +### 第二层: 循环检测 +``` +检查时机: 运行时(处理事件前) +检查逻辑: event.EventChain.Contains(currentEventType) +处理方式: 丢弃事件 + 记录错误日志 +检测模式: A→B→A, A→A +``` + +### 第三层: 发布前预检 +``` +检查时机: 发布时(PublishDerivedAsync) +检查逻辑: parentEvent.HasCircularReference(newEventType) +处理方式: 抛出 InvalidOperationException +优势: 提前阻止,避免进入队列 +``` + +--- + +## 📊 事件链示例 + +### 正常流程 +``` +Request Event (Depth=0, Chain="") + ↓ +Response Event (Depth=1, Chain="RequestEvent") + ↓ +Complete (不再发布事件) +✅ 安全 +``` + +### 循环场景(被阻止) +``` +Event A (Depth=0, Chain="") + ↓ +Event B (Depth=1, Chain="EventA") + ↓ +Event A (Depth=2, Chain="EventA→EventB") + ❌ 检测到循环:EventA 已在链中 + 🛑 抛出异常或丢弃事件 +``` + +### 深度超限(被阻止) +``` +Event 1 (Depth=0) + ↓ +Event 2 (Depth=1) + ↓ +... (中间省略) + ↓ +Event 10 (Depth=9) + ↓ +Event 11 (Depth=10) + ❌ 深度超限 + 🛑 丢弃事件 +``` + +--- + +## 🔍 日志示例 + +### 正常处理 +``` +[Information] Processing event MyRequestEvent (Id: xxx, Depth: 0, Chain: ) +[Debug] Publishing derived event: MyResponseEvent (ParentId: xxx, Depth: 1, Chain: MyRequestEvent) +[Information] Event MyResponseEvent processed successfully in 15ms +``` + +### 检测到循环 +``` +[Error] Circular reference detected: MyRequestEvent + (Id: xxx, Chain: MyRequestEvent→MyResponseEvent→MyRequestEvent) +``` + +### 深度超限 +``` +[Error] Event chain depth limit exceeded: MyEvent + (Id: xxx, Depth: 10, Chain: Event1→Event2→...→Event10) +``` + +--- + +## ✅ 检查清单 + +在实现 Handler 时,确保: + +- [ ] 事件定义继承自 `IntegrationEvent` 基类 +- [ ] 使用 `PublishDerivedAsync` 发布派生事件 +- [ ] 避免在响应 Handler 中发布请求事件 +- [ ] 配置了 `EnableCircularReferenceDetection = true` +- [ ] 设置了合理的 `MaxEventChainDepth`(建议 10) +- [ ] 添加了充分的日志记录 +- [ ] 编写了单元测试验证事件流 + +--- + +## 🐛 常见问题 + +### Q1: InvalidOperationException - Circular reference detected + +**原因**: 事件链中存在重复类型 +**解决**: 检查日志中的 EventChain,避免循环依赖 + +### Q2: Event chain depth limit exceeded + +**原因**: 事件嵌套层次过深 +**解决**: 重构为更扁平的结构,或增加 MaxEventChainDepth + +### Q3: ArgumentException - Event must inherit from IntegrationEvent + +**原因**: 事件类型未继承 IntegrationEvent 基类 +**解决**: 修改事件定义为 `public record MyEvent(...) : IntegrationEvent` + +--- + +## 📚 相关文档 + +- **技术详细文档**: [EVENTBUS_CIRCULAR_REFERENCE_PROTECTION.md](./EVENTBUS_CIRCULAR_REFERENCE_PROTECTION.md) +- **流程图和架构**: [EVENTBUS_FLOW_DIAGRAMS.md](./EVENTBUS_FLOW_DIAGRAMS.md) +- **完整检查报告**: [EVENTBUS_COMPLETE_SUMMARY.md](./EVENTBUS_COMPLETE_SUMMARY.md) + +--- + +## 🎯 核心原则 + +1. **安全第一**: 启用所有防护机制 +2. **性能优先**: 使用非阻塞 API +3. **可观测性**: 记录详细日志 +4. **向后兼容**: 平滑升级无痛迁移 + +--- + +**版本**: 1.0 +**更新**: 2026-03-24 diff --git a/EVENTBUS_QUICK_REFERENCE.md b/EVENTBUS_QUICK_REFERENCE.md index c74b5ae12..ff392e835 100644 --- a/EVENTBUS_QUICK_REFERENCE.md +++ b/EVENTBUS_QUICK_REFERENCE.md @@ -1,8 +1,8 @@ -# EventBus 循环引用防护 - 快速参考 +# EventBus Circular Reference Protection - Quick Reference -## 🚀 快速开始 +## 🚀 Quick Start -### 1. 在 Handler 中发布派生事件 +### 1. Publish derived events in handlers ```csharp public class MyRequestHandler : IIntegrationEventHandler @@ -11,169 +11,169 @@ public class MyRequestHandler : IIntegrationEventHandler public async Task Handle(MyRequestEvent @event, CancellationToken ct) { - // ... 处理业务逻辑 ... - + // ... handle business logic ... + var response = new MyResponseEvent(result); - - // ⭐ 使用 PublishDerivedAsync(自动继承链信息) + + // ⭐ Use PublishDerivedAsync (automatically carries event-chain metadata) await _eventBus.PublishDerivedAsync(response, @event); } } ``` -### 2. 配置防护选项 +### 2. Configure protection options ```csharp services.AddSenparcEventBus(options => { - options.MaxEventChainDepth = 10; // 最大深度 - options.EnableCircularReferenceDetection = true; // 循环检测 -}, + options.MaxEventChainDepth = 10; // Max depth + options.EnableCircularReferenceDetection = true; // Circular detection +}, typeof(YourAssembly).Assembly); ``` --- -## 🛡️ 三层防护机制 +## 🛡️ Three-Layer Protection -### 第一层: 深度限制 +### Layer 1: Depth limit ``` -检查时机: 运行时(处理事件前) -检查逻辑: event.Depth >= MaxEventChainDepth -处理方式: 丢弃事件 + 记录错误日志 -默认限制: 10 层 +Check time: Runtime (before event handling) +Check logic: event.Depth >= MaxEventChainDepth +Action: Drop event + log error +Default limit: 10 levels ``` -### 第二层: 循环检测 +### Layer 2: Circular detection ``` -检查时机: 运行时(处理事件前) -检查逻辑: event.EventChain.Contains(currentEventType) -处理方式: 丢弃事件 + 记录错误日志 -检测模式: A→B→A, A→A +Check time: Runtime (before event handling) +Check logic: event.EventChain.Contains(currentEventType) +Action: Drop event + log error +Patterns detected: A→B→A, A→A ``` -### 第三层: 发布前预检 +### Layer 3: Pre-publish validation ``` -检查时机: 发布时(PublishDerivedAsync) -检查逻辑: parentEvent.HasCircularReference(newEventType) -处理方式: 抛出 InvalidOperationException -优势: 提前阻止,避免进入队列 +Check time: Publish time (PublishDerivedAsync) +Check logic: parentEvent.HasCircularReference(newEventType) +Action: Throw InvalidOperationException +Benefit: Stops invalid events before they enter queue ``` --- -## 📊 事件链示例 +## 📊 Event Chain Examples -### 正常流程 +### Normal flow ``` Request Event (Depth=0, Chain="") ↓ Response Event (Depth=1, Chain="RequestEvent") ↓ -Complete (不再发布事件) -✅ 安全 +Complete (no further event publishing) +✅ Safe ``` -### 循环场景(被阻止) +### Circular scenario (blocked) ``` Event A (Depth=0, Chain="") ↓ Event B (Depth=1, Chain="EventA") ↓ Event A (Depth=2, Chain="EventA→EventB") - ❌ 检测到循环:EventA 已在链中 - 🛑 抛出异常或丢弃事件 + ❌ Circular reference detected: EventA already exists in chain + 🛑 Exception thrown or event dropped ``` -### 深度超限(被阻止) +### Depth overflow (blocked) ``` Event 1 (Depth=0) ↓ Event 2 (Depth=1) ↓ -... (中间省略) +... (omitted) ↓ Event 10 (Depth=9) ↓ Event 11 (Depth=10) - ❌ 深度超限 - 🛑 丢弃事件 + ❌ Depth limit exceeded + 🛑 Event dropped ``` --- -## 🔍 日志示例 +## 🔍 Log Examples -### 正常处理 +### Normal processing ``` [Information] Processing event MyRequestEvent (Id: xxx, Depth: 0, Chain: ) [Debug] Publishing derived event: MyResponseEvent (ParentId: xxx, Depth: 1, Chain: MyRequestEvent) [Information] Event MyResponseEvent processed successfully in 15ms ``` -### 检测到循环 +### Circular reference detected ``` -[Error] Circular reference detected: MyRequestEvent +[Error] Circular reference detected: MyRequestEvent (Id: xxx, Chain: MyRequestEvent→MyResponseEvent→MyRequestEvent) ``` -### 深度超限 +### Depth limit exceeded ``` -[Error] Event chain depth limit exceeded: MyEvent +[Error] Event chain depth limit exceeded: MyEvent (Id: xxx, Depth: 10, Chain: Event1→Event2→...→Event10) ``` --- -## ✅ 检查清单 +## ✅ Checklist -在实现 Handler 时,确保: +When implementing handlers, ensure: -- [ ] 事件定义继承自 `IntegrationEvent` 基类 -- [ ] 使用 `PublishDerivedAsync` 发布派生事件 -- [ ] 避免在响应 Handler 中发布请求事件 -- [ ] 配置了 `EnableCircularReferenceDetection = true` -- [ ] 设置了合理的 `MaxEventChainDepth`(建议 10) -- [ ] 添加了充分的日志记录 -- [ ] 编写了单元测试验证事件流 +- [ ] Event definitions inherit from IntegrationEvent base class +- [ ] Use PublishDerivedAsync to publish derived events +- [ ] Avoid publishing request events from response handlers +- [ ] Configure EnableCircularReferenceDetection = true +- [ ] Set a reasonable MaxEventChainDepth (recommended 10) +- [ ] Add sufficient logging +- [ ] Add unit tests to validate event flows --- -## 🐛 常见问题 +## 🐛 Common Issues ### Q1: InvalidOperationException - Circular reference detected -**原因**: 事件链中存在重复类型 -**解决**: 检查日志中的 EventChain,避免循环依赖 +**Cause**: Duplicate event type exists in event chain. +**Fix**: Inspect EventChain in logs and remove circular dependencies. ### Q2: Event chain depth limit exceeded -**原因**: 事件嵌套层次过深 -**解决**: 重构为更扁平的结构,或增加 MaxEventChainDepth +**Cause**: Event nesting is too deep. +**Fix**: Refactor to a flatter event architecture, or increase MaxEventChainDepth. ### Q3: ArgumentException - Event must inherit from IntegrationEvent -**原因**: 事件类型未继承 IntegrationEvent 基类 -**解决**: 修改事件定义为 `public record MyEvent(...) : IntegrationEvent` +**Cause**: Event type does not inherit IntegrationEvent. +**Fix**: Define event as public record MyEvent(...) : IntegrationEvent. --- -## 📚 相关文档 +## 📚 Related Docs -- **技术详细文档**: [EVENTBUS_CIRCULAR_REFERENCE_PROTECTION.md](./EVENTBUS_CIRCULAR_REFERENCE_PROTECTION.md) -- **流程图和架构**: [EVENTBUS_FLOW_DIAGRAMS.md](./EVENTBUS_FLOW_DIAGRAMS.md) -- **完整检查报告**: [EVENTBUS_COMPLETE_SUMMARY.md](./EVENTBUS_COMPLETE_SUMMARY.md) +- Technical details: [EVENTBUS_CIRCULAR_REFERENCE_PROTECTION.md](./EVENTBUS_CIRCULAR_REFERENCE_PROTECTION.md) +- Flow diagrams and architecture: [EVENTBUS_FLOW_DIAGRAMS.md](./EVENTBUS_FLOW_DIAGRAMS.md) +- Full inspection report: [EVENTBUS_COMPLETE_SUMMARY.md](./EVENTBUS_COMPLETE_SUMMARY.md) --- -## 🎯 核心原则 +## 🎯 Core Principles -1. **安全第一**: 启用所有防护机制 -2. **性能优先**: 使用非阻塞 API -3. **可观测性**: 记录详细日志 -4. **向后兼容**: 平滑升级无痛迁移 +1. Safety first: enable all protection mechanisms. +2. Performance first: use non-blocking APIs. +3. Observability: log detailed diagnostics. +4. Backward compatibility: smooth upgrade with minimal migration cost. --- -**版本**: 1.0 -**更新**: 2026-03-24 +**Version**: 1.0 +**Updated**: 2026-03-24 From 9e516373d09729862538169d7cc684cfdc72fac4 Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 02:56:51 -0700 Subject: [PATCH 13/21] translate: Chinese comments to English in XncfModuleManager Domain Models --- .../DatabaseModel/XncfModuleManagerSenparcEntities.cs | 4 ++-- .../MultipleDatabase/SenparcDbContextFactoryConfig.cs | 10 +++++----- .../XncfModuleManagerSenparcEntities_Dm.cs | 8 ++++---- .../XncfModuleManagerSenparcEntities_MySql.cs | 8 ++++---- .../XncfModuleManagerSenparcEntities_Oracle.cs | 8 ++++---- .../XncfModuleManagerSenparcEntities_PostgreSQL.cs | 8 ++++---- .../XncfModuleManagerSenparcEntities_SQLite.cs | 8 ++++---- .../XncfModuleManagerSenparcEntities_SqlServer.cs | 8 ++++---- 8 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/DatabaseModel/XncfModuleManagerSenparcEntities.cs b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/DatabaseModel/XncfModuleManagerSenparcEntities.cs index 8155ad9ab..19d6e2754 100644 --- a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/DatabaseModel/XncfModuleManagerSenparcEntities.cs +++ b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/DatabaseModel/XncfModuleManagerSenparcEntities.cs @@ -14,11 +14,11 @@ public XncfModuleManagerSenparcEntities(DbContextOptions dbContextOptions) : bas } /// - /// 扩展模块 + /// Extended modules /// public DbSet XncfModules { get; set; } - //如无特殊需需要,OnModelCreating 方法可以不用写,已经在 Register 中要求注册 + //If there are no special requirements, OnModelCreating can be omitted because registration is already required in Register. //protected override void OnModelCreating(ModelBuilder modelBuilder) //{ //} diff --git a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs index 21b165e8d..0891d0da0 100644 --- a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs +++ b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs @@ -6,14 +6,14 @@ namespace Senparc.Xncf.XncfModuleManager.Models { /// - /// SenparcDbContextFactory 的公共配置 + /// SenparcDbContextFactory common configuration. /// public static class SenparcDbContextFactoryConfig { private static string _rootDictionaryPath = null; /// - /// 用于寻找 App_Data 文件夹,从而找到数据库连接字符串配置信息 + /// Used to locate the App_Data folder to find database connection string configuration. /// public static string RootDictionaryPath { @@ -21,13 +21,13 @@ public static string RootDictionaryPath { if (_rootDictionaryPath == null) { - var projectPath = Path.GetFullPath("..\\..\\..\\", AppContext.BaseDirectory);//项目根目录 + var projectPath = Path.GetFullPath("..\\..\\..\\", AppContext.BaseDirectory);//project root directory - var webPath = Path.GetFullPath("..\\Senparc.Web",/*找到 Web目录,以获取统一的数据库连接字符串配置*/ + var webPath = Path.GetFullPath("..\\Senparc.Web",/*locate the Web directory to use unified database connection string configuration*/ projectPath); if (Directory.Exists(webPath)) { - _rootDictionaryPath = webPath;//优先使用Web统一配置 + _rootDictionaryPath = webPath;//prefer unified Web configuration } _rootDictionaryPath = projectPath; } diff --git a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_Dm.cs b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_Dm.cs index c012ee9a6..35bf2fdf2 100644 --- a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_Dm.cs +++ b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_Dm.cs @@ -20,15 +20,15 @@ public XncfModuleManagerSenparcEntities_Dm(DbContextOptions - /// 设计时 DbContext 创建(仅在开发时创建 Code-First 的数据库 Migration 使用,在生产环境不会执行) - /// 1、切换至 Debug 模式 - /// 2、运行:PM> add-migration [更新名称] -c XncfModuleManagerSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm + /// Design-time DbContext creation (used only to create Code-First database migrations during development; not executed in production). + /// 1. Switch to Debug mode. + /// 2. Run: PM> add-migration [MigrationName] -c XncfModuleManagerSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm /// public class SenparcDbContextFactory_Dm : SenparcDesignTimeDbContextFactoryBase { protected override Action AppAction => app => { - //指定其他数据库 + //Use another database app.UseNcfDatabase("Senparc.Ncf.Database.Dm", "Senparc.Ncf.Database.Dm", "DmDatabaseConfiguration"); }; diff --git a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_MySql.cs b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_MySql.cs index 9f87fdb07..c249c3402 100644 --- a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_MySql.cs +++ b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_MySql.cs @@ -19,15 +19,15 @@ public XncfModuleManagerSenparcEntities_MySql(DbContextOptions - /// 设计时 DbContext 创建(仅在开发时创建 Code-First 的数据库 Migration 使用,在生产环境不会执行) - /// 1、切换至 Debug 模式 - /// 2、运行:PM> add-migration [更新名称] -c XncfModuleManagerSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql + /// Design-time DbContext creation (used only to create Code-First database migrations during development; not executed in production). + /// 1. Switch to Debug mode. + /// 2. Run: PM> add-migration [MigrationName] -c XncfModuleManagerSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql /// public class SenparcDbContextFactory_MySql : SenparcDesignTimeDbContextFactoryBase { protected override Action AppAction => app => { - //指定其他数据库 + //Use another database app.UseNcfDatabase("Senparc.Ncf.Database.MySql", "Senparc.Ncf.Database.MySql", "MySqlDatabaseConfiguration"); }; diff --git a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_Oracle.cs b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_Oracle.cs index c4372f0de..9758f8e27 100644 --- a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_Oracle.cs +++ b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_Oracle.cs @@ -20,15 +20,15 @@ public XncfModuleManagerSenparcEntities_Oracle(DbContextOptions - /// 设计时 DbContext 创建(仅在开发时创建 Code-First 的数据库 Migration 使用,在生产环境不会执行) - /// 1、切换至 Debug 模式 - /// 2、运行:PM> add-migration [更新名称] -c XncfModuleManagerSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle + /// Design-time DbContext creation (used only to create Code-First database migrations during development; not executed in production). + /// 1. Switch to Debug mode. + /// 2. Run: PM> add-migration [MigrationName] -c XncfModuleManagerSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle /// public class SenparcDbContextFactory_Oracle : SenparcDesignTimeDbContextFactoryBase { protected override Action AppAction => app => { - //指定其他数据库 + //Use another database app.UseNcfDatabase("Senparc.Ncf.Database.Oracle", "Senparc.Ncf.Database.Oracle", "OracleDatabaseConfiguration"); }; diff --git a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_PostgreSQL.cs b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_PostgreSQL.cs index b65824fb4..27a5c2207 100644 --- a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_PostgreSQL.cs +++ b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_PostgreSQL.cs @@ -20,15 +20,15 @@ public XncfModuleManagerSenparcEntities_PostgreSQL(DbContextOptions - /// 设计时 DbContext 创建(仅在开发时创建 Code-First 的数据库 Migration 使用,在生产环境不会执行) - /// 1、切换至 Debug 模式 - /// 2、运行:PM> add-migration [更新名称] -c XncfModuleManagerSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL + /// Design-time DbContext creation (used only to create Code-First database migrations during development; not executed in production). + /// 1. Switch to Debug mode. + /// 2. Run: PM> add-migration [MigrationName] -c XncfModuleManagerSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL /// public class SenparcDbContextFactory_PostgreSQL : SenparcDesignTimeDbContextFactoryBase { protected override Action AppAction => app => { - //指定其他数据库 + //Use another database app.UseNcfDatabase("Senparc.Ncf.Database.PostgreSQL", "Senparc.Ncf.Database.PostgreSQL", "PostgreSQLDatabaseConfiguration"); }; diff --git a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_SQLite.cs b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_SQLite.cs index 522c6e37e..c67cebaec 100644 --- a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_SQLite.cs +++ b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_SQLite.cs @@ -20,15 +20,15 @@ public XncfModuleManagerSenparcEntities_Sqlite(DbContextOptions - /// 设计时 DbContext 创建(仅在开发时创建 Code-First 的数据库 Migration 使用,在生产环境不会执行) - /// 1、切换至 Debug 模式 - /// 2、运行:PM> add-migration [更新名称] -c XncfModuleManagerSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite + /// Design-time DbContext creation (used only to create Code-First database migrations during development; not executed in production). + /// 1. Switch to Debug mode. + /// 2. Run: PM> add-migration [MigrationName] -c XncfModuleManagerSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite /// public class SenparcDbContextFactory_Sqlite : SenparcDesignTimeDbContextFactoryBase { protected override Action AppAction => app => { - //指定其他数据库 + //Use another database app.UseNcfDatabase("Senparc.Ncf.Database.Sqlite", "Senparc.Ncf.Database.Sqlite", "SqliteMemoryDatabaseConfiguration"); }; diff --git a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_SqlServer.cs b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_SqlServer.cs index b432123ee..72bdd2ef3 100644 --- a/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_SqlServer.cs +++ b/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_SqlServer.cs @@ -20,15 +20,15 @@ public XncfModuleManagerSenparcEntities_SqlServer(DbContextOptions - /// 设计时 DbContext 创建(仅在开发时创建 Code-First 的数据库 Migration 使用,在生产环境不会执行) - /// 1、切换至 Debug 模式 - /// 2、运行:PM> add-migration [更新名称] -c XncfModuleManagerSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer + /// Design-time DbContext creation (used only to create Code-First database migrations during development; not executed in production). + /// 1. Switch to Debug mode. + /// 2. Run: PM> add-migration [MigrationName] -c XncfModuleManagerSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer /// public class SenparcDbContextFactory_SqlServer : SenparcDesignTimeDbContextFactoryBase { protected override Action AppAction => app => { - //指定其他数据库 + //Use another database app.UseNcfDatabase("Senparc.Ncf.Database.SqlServer", "Senparc.Ncf.Database.SqlServer", "SqlServerDatabaseConfiguration"); }; From 924f662a1966e19fa9f338eb6141a5dea9526273 Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 02:58:08 -0700 Subject: [PATCH 14/21] translate: README/docs Admin-Chat-Drag-Troubleshooting to English, preserve .cn.md --- docs/Admin-Chat-Drag-Troubleshooting.cn.md | 246 +++++++++++++++++++++ docs/Admin-Chat-Drag-Troubleshooting.md | 245 ++++++++++---------- 2 files changed, 369 insertions(+), 122 deletions(-) create mode 100644 docs/Admin-Chat-Drag-Troubleshooting.cn.md diff --git a/docs/Admin-Chat-Drag-Troubleshooting.cn.md b/docs/Admin-Chat-Drag-Troubleshooting.cn.md new file mode 100644 index 000000000..bc20de3b1 --- /dev/null +++ b/docs/Admin-Chat-Drag-Troubleshooting.cn.md @@ -0,0 +1,246 @@ +# 模块拖拽功能 - 故障排查指南 + +## ✅ 已完成的修复 + +### 1. 拖拽初始化时机调整 +**问题**: 模块列表是异步加载的,`mounted` 时可能还未渲染 +**修复**: 将 `initializeModuleDrag()` 移到 `getXncfOpening()` 完成后调用 + +### 2. 事件处理增强 +**问题**: `dragover` 事件没有阻止默认行为,导致 `drop` 事件无法触发 +**修复**: 在 `handleDragOver()` 中添加 `event.preventDefault()` 和 `dropEffect` + +### 3. 样式优化 +- 拖放区域高度增加到 100px +- 添加更明显的拖拽提示 +- 增强拖拽时的视觉反馈(高亮、缩放、阴影) +- 添加 cursor: grab/grabbing 样式 + +### 4. 调试信息 +- 添加 console.log 输出,方便排查问题 +- 添加友好的错误提示消息 + +--- + +## 🔍 故障排查步骤 + +### 步骤 1: 检查控制台日志 + +打开浏览器开发者工具(F12),查看 Console 标签页: + +1. **页面加载时应该看到**: + ``` + 找到的模块卡片数量: X + 模块拖拽初始化完成,已绑定 X 个模块 + ``` + +2. **如果看到**: + ``` + 找到的模块卡片数量: 0 + 未找到模块卡片,将在 200ms 后重试 + ``` + - 说明模块还未加载,会自动重试 + +3. **拖拽模块时应该看到**: + ``` + 开始拖拽模块: [模块名称] {uid: "...", name: "..."} + 拖放区域已高亮 + ``` + +4. **放下模块时应该看到**: + ``` + 检测到放下操作 DragEvent {...} + 接收到的数据: {"uid":"...","name":"..."} + 解析后的模块数据: {uid: "...", name: "..."} + 模块添加成功,当前选中模块: [...] + ``` + +### 步骤 2: 检查模块是否可拖拽 + +在 Console 中运行: + +```javascript +// 检查模块数量 +document.querySelectorAll('#xncf-modules-area .xncf-item').length + +// 检查模块的 draggable 属性 +document.querySelectorAll('#xncf-modules-area .xncf-item[draggable="true"]').length + +// 检查拖放区域 +document.querySelector('.chat-module-drop-zone') +``` + +**预期结果**: +- 第一行应该返回模块数量(如 8 或更多) +- 第二行应该返回相同的数量 +- 第三行应该返回一个 HTMLDivElement + +### 步骤 3: 手动测试拖拽 + +1. **打开页面**: `http://localhost:5000/Admin` +2. **等待加载**: 确保模块列表完全加载(看到模块卡片) +3. **尝试拖拽**: + - 鼠标悬停在任一模块卡片上 + - 鼠标指针应该变为 "grab" 样式(抓手) + - 按住鼠标左键开始拖拽 + - 模块卡片应该变半透明 + - 拖放区域应该高亮显示 +4. **释放鼠标**: + - 在拖放区域内释放鼠标 + - 应该看到消息提示:"已添加模块: [模块名称]" + - 拖放区域显示选中的模块标签 + +--- + +## 🐛 常见问题和解决方案 + +### 问题 1: 模块卡片无法拖拽(鼠标指针不变) + +**可能原因**: +- 拖拽初始化未执行 +- 模块列表未加载完成 + +**解决方案**: +1. 刷新页面(Ctrl+F5 强制刷新) +2. 在 Console 中手动执行: + ```javascript + app.initializeModuleDrag() + ``` +3. 检查 Console 是否有错误信息 + +### 问题 2: 可以拖拽,但无法放下 + +**可能原因**: +- 拖放区域未正确绑定 drop 事件 +- dragover 事件未阻止默认行为 + +**解决方案**: +1. 检查拖放区域是否存在: + ```javascript + console.log(document.querySelector('.chat-module-drop-zone')) + ``` +2. 检查 Vue 实例的方法: + ```javascript + console.log(typeof app.handleModuleDrop) + console.log(typeof app.handleDragOver) + ``` +3. 确认 Index.cshtml 中的事件绑定: + ```html + @@drop.prevent="handleModuleDrop" + @@dragover.prevent="handleDragOver" + ``` + +### 问题 3: 拖拽时拖放区域不高亮 + +**可能原因**: +- CSS 选择器不正确 +- 类名添加失败 + +**解决方案**: +1. 拖拽时在 Console 查看元素: + ```javascript + document.querySelector('.chat-module-drop-zone').classList + ``` + 应该包含 `highlight` 类 + +2. 检查样式是否加载: + ```javascript + getComputedStyle(document.querySelector('.chat-module-drop-zone')).background + ``` + +### 问题 4: 控制台显示 "未找到模块数据" + +**可能原因**: +- dataTransfer 数据传递失败 +- 浏览器安全限制 + +**解决方案**: +1. 检查浏览器版本(建议使用最新版 Chrome/Edge/Firefox) +2. 检查是否有浏览器扩展干扰(尝试隐身模式) +3. 手动测试 dataTransfer: + ```javascript + // 在 dragstart 事件中添加断点,检查 + event.dataTransfer.setData('text/plain', 'test') + ``` + +--- + +## 🔧 临时解决方案 + +如果拖拽功能仍然不工作,可以使用点击选择作为临时方案: + +### 修改方案:点击选择模块 + +在 `Index.js` 的 `initializeModuleDrag()` 方法前添加: + +```javascript +initializeModuleClick() { + this.$nextTick(() => { + const moduleCards = document.querySelectorAll('#xncf-modules-area .xncf-item'); + + moduleCards.forEach((card) => { + const clickHandler = (event) => { + // 如果点击的是链接或按钮,不执行选择逻辑 + if (event.target.tagName === 'A' || event.target.closest('a') || + event.target.tagName === 'BUTTON' || event.target.closest('button')) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + + const linkElement = card.querySelector('a[href*="uid="]'); + const headerElement = card.querySelector('.el-card__header span:first-child'); + const iconElement = card.querySelector('.icon'); + const versionElement = card.querySelector('.version'); + + const moduleData = { + uid: linkElement?.href?.match(/uid=([^&]+)/)?.[1] || '', + name: headerElement?.textContent?.trim() || '未知模块', + icon: iconElement?.className || 'fa fa-cube', + version: versionElement?.textContent?.trim() || '' + }; + + const exists = this.selectedModules.some(m => m.uid === moduleData.uid); + if (!exists) { + this.selectedModules.push(moduleData); + this.$message.success(`已添加模块: ${moduleData.name}`); + } else { + this.$message.info('该模块已添加'); + } + }; + + // 双击选择模块 + card.addEventListener('dblclick', clickHandler); + }); + }); +} +``` + +然后在 `getXncfOpening()` 中同时调用: +```javascript +this.initializeModuleDrag(); +this.initializeModuleClick(); // 添加点击选择作为备用 +``` + +--- + +## 📞 需要更多帮助? + +如果以上方法都无法解决问题,请提供以下信息: + +1. 浏览器控制台的完整输出(Console 标签) +2. 浏览器版本和类型 +3. 执行以下命令的输出: + ```javascript + console.log('模块数量:', document.querySelectorAll('#xncf-modules-area .xncf-item').length); + console.log('可拖拽模块:', document.querySelectorAll('#xncf-modules-area .xncf-item[draggable]').length); + console.log('拖放区域:', document.querySelector('.chat-module-drop-zone')); + console.log('Vue 实例:', app); + ``` + +--- + +**文档创建日期**: 2026-03-25 +**最后更新**: 2026-03-25 +**版本**: v1.0 diff --git a/docs/Admin-Chat-Drag-Troubleshooting.md b/docs/Admin-Chat-Drag-Troubleshooting.md index bc20de3b1..4d4b7ce20 100644 --- a/docs/Admin-Chat-Drag-Troubleshooting.md +++ b/docs/Admin-Chat-Drag-Troubleshooting.md @@ -1,246 +1,247 @@ -# 模块拖拽功能 - 故障排查指南 +# Module Drag-and-Drop - Troubleshooting Guide -## ✅ 已完成的修复 +## ✅ Fixes Already Completed -### 1. 拖拽初始化时机调整 -**问题**: 模块列表是异步加载的,`mounted` 时可能还未渲染 -**修复**: 将 `initializeModuleDrag()` 移到 `getXncfOpening()` 完成后调用 +### 1. Drag initialization timing adjustment +**Issue**: Module list loads asynchronously, so items may not be rendered at mounted time. +**Fix**: Move initializeModuleDrag() call to run after getXncfOpening() completes. -### 2. 事件处理增强 -**问题**: `dragover` 事件没有阻止默认行为,导致 `drop` 事件无法触发 -**修复**: 在 `handleDragOver()` 中添加 `event.preventDefault()` 和 `dropEffect` +### 2. Event handling enhancement +**Issue**: dragover did not prevent default behavior, so drop could not fire. +**Fix**: Added event.preventDefault() and dropEffect in handleDragOver(). -### 3. 样式优化 -- 拖放区域高度增加到 100px -- 添加更明显的拖拽提示 -- 增强拖拽时的视觉反馈(高亮、缩放、阴影) -- 添加 cursor: grab/grabbing 样式 +### 3. Style optimization +- Increased drop zone height to 100px. +- Added more explicit drag-and-drop hints. +- Enhanced drag visual feedback (highlight, scale, shadow). +- Added cursor: grab/grabbing styles. -### 4. 调试信息 -- 添加 console.log 输出,方便排查问题 -- 添加友好的错误提示消息 +### 4. Debug output +- Added console.log output for easier troubleshooting. +- Added user-friendly error messages. --- -## 🔍 故障排查步骤 +## 🔍 Troubleshooting Steps -### 步骤 1: 检查控制台日志 +### Step 1: Check console logs -打开浏览器开发者工具(F12),查看 Console 标签页: +Open browser dev tools (F12) and check Console: -1. **页面加载时应该看到**: +1. **Expected on page load**: ``` - 找到的模块卡片数量: X - 模块拖拽初始化完成,已绑定 X 个模块 + Found module card count: X + Module drag initialization complete, bound X modules ``` -2. **如果看到**: +2. **If you see**: ``` - 找到的模块卡片数量: 0 - 未找到模块卡片,将在 200ms 后重试 + Found module card count: 0 + Module cards not found, retrying in 200ms ``` - - 说明模块还未加载,会自动重试 + - This means modules are still loading and retry will happen automatically. -3. **拖拽模块时应该看到**: +3. **Expected while dragging a module**: ``` - 开始拖拽模块: [模块名称] {uid: "...", name: "..."} - 拖放区域已高亮 + Drag start module: [module name] {uid: "...", name: "..."} + Drop zone highlighted ``` -4. **放下模块时应该看到**: +4. **Expected when dropping module**: ``` - 检测到放下操作 DragEvent {...} - 接收到的数据: {"uid":"...","name":"..."} - 解析后的模块数据: {uid: "...", name: "..."} - 模块添加成功,当前选中模块: [...] + Drop operation detected DragEvent {...} + Received data: {"uid":"...","name":"..."} + Parsed module data: {uid: "...", name: "..."} + Module added successfully, current selected modules: [...] ``` -### 步骤 2: 检查模块是否可拖拽 +### Step 2: Verify module draggable state -在 Console 中运行: +Run in Console: ```javascript -// 检查模块数量 +// Check module count document.querySelectorAll('#xncf-modules-area .xncf-item').length -// 检查模块的 draggable 属性 +// Check draggable attribute count document.querySelectorAll('#xncf-modules-area .xncf-item[draggable="true"]').length -// 检查拖放区域 +// Check drop zone element document.querySelector('.chat-module-drop-zone') ``` -**预期结果**: -- 第一行应该返回模块数量(如 8 或更多) -- 第二行应该返回相同的数量 -- 第三行应该返回一个 HTMLDivElement - -### 步骤 3: 手动测试拖拽 - -1. **打开页面**: `http://localhost:5000/Admin` -2. **等待加载**: 确保模块列表完全加载(看到模块卡片) -3. **尝试拖拽**: - - 鼠标悬停在任一模块卡片上 - - 鼠标指针应该变为 "grab" 样式(抓手) - - 按住鼠标左键开始拖拽 - - 模块卡片应该变半透明 - - 拖放区域应该高亮显示 -4. **释放鼠标**: - - 在拖放区域内释放鼠标 - - 应该看到消息提示:"已添加模块: [模块名称]" - - 拖放区域显示选中的模块标签 +**Expected**: +- First line returns module count (for example, 8 or more). +- Second line returns the same count. +- Third line returns an HTMLDivElement. + +### Step 3: Manual drag-and-drop test + +1. Open page: http://localhost:5000/Admin +2. Wait for full load: ensure module cards are visible. +3. Try drag: + - Hover over any module card. + - Cursor should become grab. + - Hold left mouse button to start dragging. + - Module card should become semi-transparent. + - Drop zone should highlight. +4. Release mouse: + - Release inside drop zone. + - You should see message: Module added: [module name]. + - Drop zone displays selected module tags. --- -## 🐛 常见问题和解决方案 +## 🐛 Common Problems and Solutions -### 问题 1: 模块卡片无法拖拽(鼠标指针不变) +### Problem 1: Module cards cannot be dragged (cursor does not change) -**可能原因**: -- 拖拽初始化未执行 -- 模块列表未加载完成 +**Possible causes**: +- Drag initialization did not run. +- Module list has not finished loading. -**解决方案**: -1. 刷新页面(Ctrl+F5 强制刷新) -2. 在 Console 中手动执行: +**Solutions**: +1. Refresh page (Ctrl+F5 hard refresh). +2. Execute manually in Console: ```javascript app.initializeModuleDrag() ``` -3. 检查 Console 是否有错误信息 +3. Check Console for errors. -### 问题 2: 可以拖拽,但无法放下 +### Problem 2: Drag works, but drop does not -**可能原因**: -- 拖放区域未正确绑定 drop 事件 -- dragover 事件未阻止默认行为 +**Possible causes**: +- Drop zone is not correctly bound to drop event. +- dragover does not prevent default behavior. -**解决方案**: -1. 检查拖放区域是否存在: +**Solutions**: +1. Check drop zone exists: ```javascript console.log(document.querySelector('.chat-module-drop-zone')) ``` -2. 检查 Vue 实例的方法: +2. Check Vue instance methods: ```javascript console.log(typeof app.handleModuleDrop) console.log(typeof app.handleDragOver) ``` -3. 确认 Index.cshtml 中的事件绑定: +3. Verify bindings in Index.cshtml: ```html @@drop.prevent="handleModuleDrop" @@dragover.prevent="handleDragOver" ``` -### 问题 3: 拖拽时拖放区域不高亮 +### Problem 3: Drop zone does not highlight while dragging -**可能原因**: -- CSS 选择器不正确 -- 类名添加失败 +**Possible causes**: +- CSS selector is incorrect. +- Class name was not added successfully. -**解决方案**: -1. 拖拽时在 Console 查看元素: +**Solutions**: +1. Check element class list while dragging: ```javascript document.querySelector('.chat-module-drop-zone').classList ``` - 应该包含 `highlight` 类 + Should include highlight class. -2. 检查样式是否加载: +2. Check style loaded: ```javascript getComputedStyle(document.querySelector('.chat-module-drop-zone')).background ``` -### 问题 4: 控制台显示 "未找到模块数据" +### Problem 4: Console shows Module data not found -**可能原因**: -- dataTransfer 数据传递失败 -- 浏览器安全限制 +**Possible causes**: +- dataTransfer payload failed. +- Browser security restrictions. -**解决方案**: -1. 检查浏览器版本(建议使用最新版 Chrome/Edge/Firefox) -2. 检查是否有浏览器扩展干扰(尝试隐身模式) -3. 手动测试 dataTransfer: +**Solutions**: +1. Check browser version (latest Chrome/Edge/Firefox recommended). +2. Check extension interference (try Incognito mode). +3. Manually test dataTransfer: ```javascript - // 在 dragstart 事件中添加断点,检查 + // Add breakpoint in dragstart and verify event.dataTransfer.setData('text/plain', 'test') ``` --- -## 🔧 临时解决方案 +## 🔧 Temporary Workaround -如果拖拽功能仍然不工作,可以使用点击选择作为临时方案: +If drag-and-drop still fails, use click-to-select as fallback: -### 修改方案:点击选择模块 +### Alternative: click to select module -在 `Index.js` 的 `initializeModuleDrag()` 方法前添加: +Add before initializeModuleDrag() in Index.js: ```javascript initializeModuleClick() { this.$nextTick(() => { const moduleCards = document.querySelectorAll('#xncf-modules-area .xncf-item'); - + moduleCards.forEach((card) => { const clickHandler = (event) => { - // 如果点击的是链接或按钮,不执行选择逻辑 - if (event.target.tagName === 'A' || event.target.closest('a') || + // If clicking links/buttons, skip selection logic + if (event.target.tagName === 'A' || event.target.closest('a') || event.target.tagName === 'BUTTON' || event.target.closest('button')) { return; } - + event.preventDefault(); event.stopPropagation(); - + const linkElement = card.querySelector('a[href*="uid="]'); const headerElement = card.querySelector('.el-card__header span:first-child'); const iconElement = card.querySelector('.icon'); const versionElement = card.querySelector('.version'); - + const moduleData = { uid: linkElement?.href?.match(/uid=([^&]+)/)?.[1] || '', - name: headerElement?.textContent?.trim() || '未知模块', + name: headerElement?.textContent?.trim() || 'Unknown Module', icon: iconElement?.className || 'fa fa-cube', version: versionElement?.textContent?.trim() || '' }; - + const exists = this.selectedModules.some(m => m.uid === moduleData.uid); if (!exists) { this.selectedModules.push(moduleData); - this.$message.success(`已添加模块: ${moduleData.name}`); + this.$message.success(`Module added: ${moduleData.name}`); } else { - this.$message.info('该模块已添加'); + this.$message.info('Module already added'); } }; - - // 双击选择模块 + + // Double click to select module card.addEventListener('dblclick', clickHandler); }); }); } ``` -然后在 `getXncfOpening()` 中同时调用: +Then call both in getXncfOpening(): + ```javascript this.initializeModuleDrag(); -this.initializeModuleClick(); // 添加点击选择作为备用 +this.initializeModuleClick(); // Add click selection as fallback ``` --- -## 📞 需要更多帮助? +## 📞 Need More Help? -如果以上方法都无法解决问题,请提供以下信息: +If the issue still cannot be resolved, provide: -1. 浏览器控制台的完整输出(Console 标签) -2. 浏览器版本和类型 -3. 执行以下命令的输出: +1. Full browser console output. +2. Browser type and version. +3. Output of the following commands: ```javascript - console.log('模块数量:', document.querySelectorAll('#xncf-modules-area .xncf-item').length); - console.log('可拖拽模块:', document.querySelectorAll('#xncf-modules-area .xncf-item[draggable]').length); - console.log('拖放区域:', document.querySelector('.chat-module-drop-zone')); - console.log('Vue 实例:', app); + console.log('Module count:', document.querySelectorAll('#xncf-modules-area .xncf-item').length); + console.log('Draggable modules:', document.querySelectorAll('#xncf-modules-area .xncf-item[draggable]').length); + console.log('Drop zone:', document.querySelector('.chat-module-drop-zone')); + console.log('Vue instance:', app); ``` --- -**文档创建日期**: 2026-03-25 -**最后更新**: 2026-03-25 -**版本**: v1.0 +**Document Created**: 2026-03-25 +**Last Updated**: 2026-03-25 +**Version**: v1.0 From 83c0c5503dbafc5da0a4f2457073cc90c4e660c5 Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 02:59:02 -0700 Subject: [PATCH 15/21] translate: README/docs EVENTBUS_FLOW_DIAGRAMS to English, preserve .cn.md --- EVENTBUS_FLOW_DIAGRAMS.cn.md | 211 +++++++++++++++++++++++++++++++++++ EVENTBUS_FLOW_DIAGRAMS.md | 152 ++++++++++++------------- 2 files changed, 287 insertions(+), 76 deletions(-) create mode 100644 EVENTBUS_FLOW_DIAGRAMS.cn.md diff --git a/EVENTBUS_FLOW_DIAGRAMS.cn.md b/EVENTBUS_FLOW_DIAGRAMS.cn.md new file mode 100644 index 000000000..039aa4525 --- /dev/null +++ b/EVENTBUS_FLOW_DIAGRAMS.cn.md @@ -0,0 +1,211 @@ +# PromptRange EventBus 流程图 + +## 🔄 Prompt 初始化流程 + +```mermaid +sequenceDiagram + participant User + participant Service as PromptOptimizationService + participant EventBus + participant Handler1 as PromptInitRequestHandler + participant Handler2 as PromptInitResponseHandler + participant DB as Database + + User->>Service: EnsureInitializedAsync(modelId) + Service->>Service: 检查 Agent 是否存在 + + alt Agent 不存在 + Service->>EventBus: PublishAsync(PromptInitRequestEvent)
Depth=0, Chain="" + Note over EventBus: 深度检查 (0 < 10) ✅
循环检查 (无) ✅ + EventBus->>Handler1: Handle(PromptInitRequestEvent) + Handler1->>DB: 创建 PromptRange/PromptItem + Handler1->>EventBus: PublishDerivedAsync(PromptInitResponseEvent)
Depth=1, Chain="PromptInitRequestEvent" + Note over EventBus: 深度检查 (1 < 10) ✅
循环检查 (无重复) ✅ + EventBus->>Handler2: Handle(PromptInitResponseEvent) + Handler2->>Service: CompleteInitRequest(TCS.SetResult) + Service-->>User: 返回 PromptCode + else Agent 已存在 + Service-->>User: 返回现有 PromptCode + end +``` + +--- + +## 🎨 Prompt 优化流程 + +```mermaid +sequenceDiagram + participant User + participant Service as PromptOptimizationService + participant EventBus + participant Handler1 as PromptOptimizationRequestHandler + participant Handler2 as PromptOptimizationResponseHandler + + User->>Service: OptimizePromptAsync(promptCode, content, requirement) + Service->>EventBus: PublishAsync(PromptOptimizationRequestEvent)
Depth=0, Chain="" + Note over EventBus: 🛡️ 检查通过 + EventBus->>Handler1: Handle(PromptOptimizationRequestEvent) + Handler1->>Handler1: 生成优化后的 Prompt + Handler1->>EventBus: PublishDerivedAsync(PromptOptimizationResponseEvent)
Depth=1, Chain="PromptOptimizationRequestEvent" + Note over EventBus: 🛡️ 检查通过 + EventBus->>Handler2: Handle(PromptOptimizationResponseEvent) + Handler2->>Service: CompleteRequest(TCS.SetResult) + Service-->>User: 返回优化结果 +``` + +--- + +## 🛡️ 循环引用防护示例 + +### 场景 1: 阻止直接循环 + +```mermaid +graph TD + A[EventA
Depth=0
Chain=Empty] -->|Handler A| B[EventB
Depth=1
Chain=EventA] + B -->|Handler B| C[EventA
Depth=2
Chain=EventA→EventB] + C -->|❌ 检测到循环| D[丢弃事件
记录错误日志] + + style D fill:#ff6b6b +``` + +### 场景 2: 阻止深度超限 + +```mermaid +graph TD + A[Event 1
Depth=0] --> B[Event 2
Depth=1] + B --> C[Event 3
Depth=2] + C --> D[Event 4
Depth=3] + D --> E[...] + E --> F[Event 10
Depth=9] + F --> G[Event 11
Depth=10] + G -->|❌ 深度超限| H[丢弃事件
记录错误日志] + + style G fill:#ffd93d + style H fill:#ff6b6b +``` + +--- + +## 🔍 EventBus 核心架构 + +```mermaid +graph LR + subgraph 发布端 + A[业务代码] -->|PublishAsync| B[InMemoryEventBus] + A2[Handler] -->|PublishDerivedAsync| B + end + + B -->|WriteAsync| C[Channel
无界队列] + + subgraph 消费端 + C -->|ReadAllAsync| D[EventBusHostedService] + D -->|1. 重复检测| E{已处理?} + E -->|是| F[跳过] + E -->|否| G{深度超限?} + G -->|是| H[丢弃 + 日志] + G -->|否| I{循环检测} + I -->|是| J[丢弃 + 日志] + I -->|否| K[SemaphoreSlim
并发控制] + K --> L[Task.Run
异步处理] + L --> M[Handler.Handle] + end + + style F fill:#ffd93d + style H fill:#ff6b6b + style J fill:#ff6b6b + style K fill:#6bcf7f + style L fill:#6bcf7f +``` + +--- + +## 📈 性能特性 + +### 非阻塞发布 + +```mermaid +sequenceDiagram + participant Caller + participant EventBus + participant Channel + participant Background + + Caller->>EventBus: PublishAsync(event) + EventBus->>Channel: WriteAsync(event) + Channel-->>EventBus: ValueTask (立即返回) + EventBus-->>Caller: ValueTask (< 1ms) + + Note over Background: 后台异步处理 + Background->>Channel: ReadAllAsync() + Channel->>Background: 返回事件 + Background->>Background: 处理事件 +``` + +### 并发控制 + +```mermaid +graph TD + A[Channel 队列] --> B{SemaphoreSlim} + B -->|获取信号| C1[Task 1] + B -->|获取信号| C2[Task 2] + B -->|获取信号| C3[Task 3] + B -->|获取信号| CN[Task N] + B -->|等待| W[等待队列] + + C1 --> D1[Handler] + C2 --> D2[Handler] + C3 --> D3[Handler] + CN --> DN[Handler] + + D1 -->|释放信号| B + D2 -->|释放信号| B + D3 -->|释放信号| B + DN -->|释放信号| B + + style B fill:#6bcf7f + style W fill:#ffd93d +``` + +--- + +## 🎓 最佳实践总结 + +### ✅ 推荐模式 + +1. **请求-响应模式** (最安全) + ``` + Request → Handler → Response → Complete + ``` + +2. **单向事件流** (次安全) + ``` + EventA → EventB → EventC → ... (不回溯) + ``` + +3. **限制深度** (强制约束) + ``` + 最多 3-5 层事件嵌套 + ``` + +### ❌ 反模式 + +1. **响应再请求** (易循环) + ``` + Request → Response → Request (❌ 循环风险) + ``` + +2. **相互发布** (易循环) + ``` + HandlerA publishes EventB + HandlerB publishes EventA (❌ 循环风险) + ``` + +3. **无限递归** (易爆栈) + ``` + EventA → EventA → EventA → ... (❌ 深度风险) + ``` + +--- + +**文档版本**: 1.0 +**最后更新**: 2026-03-24 diff --git a/EVENTBUS_FLOW_DIAGRAMS.md b/EVENTBUS_FLOW_DIAGRAMS.md index 039aa4525..fd20e281c 100644 --- a/EVENTBUS_FLOW_DIAGRAMS.md +++ b/EVENTBUS_FLOW_DIAGRAMS.md @@ -1,6 +1,6 @@ -# PromptRange EventBus 流程图 +# PromptRange EventBus Flow Diagrams -## 🔄 Prompt 初始化流程 +## 🔄 Prompt Initialization Flow ```mermaid sequenceDiagram @@ -12,26 +12,26 @@ sequenceDiagram participant DB as Database User->>Service: EnsureInitializedAsync(modelId) - Service->>Service: 检查 Agent 是否存在 - - alt Agent 不存在 + Service->>Service: Check whether Agent exists + + alt Agent does not exist Service->>EventBus: PublishAsync(PromptInitRequestEvent)
Depth=0, Chain="" - Note over EventBus: 深度检查 (0 < 10) ✅
循环检查 (无) ✅ + Note over EventBus: Depth check (0 < 10) ✅
Circular check (none) ✅ EventBus->>Handler1: Handle(PromptInitRequestEvent) - Handler1->>DB: 创建 PromptRange/PromptItem + Handler1->>DB: Create PromptRange/PromptItem Handler1->>EventBus: PublishDerivedAsync(PromptInitResponseEvent)
Depth=1, Chain="PromptInitRequestEvent" - Note over EventBus: 深度检查 (1 < 10) ✅
循环检查 (无重复) ✅ + Note over EventBus: Depth check (1 < 10) ✅
Circular check (no duplicate) ✅ EventBus->>Handler2: Handle(PromptInitResponseEvent) Handler2->>Service: CompleteInitRequest(TCS.SetResult) - Service-->>User: 返回 PromptCode - else Agent 已存在 - Service-->>User: 返回现有 PromptCode + Service-->>User: Return PromptCode + else Agent already exists + Service-->>User: Return existing PromptCode end ``` --- -## 🎨 Prompt 优化流程 +## 🎨 Prompt Optimization Flow ```mermaid sequenceDiagram @@ -43,32 +43,32 @@ sequenceDiagram User->>Service: OptimizePromptAsync(promptCode, content, requirement) Service->>EventBus: PublishAsync(PromptOptimizationRequestEvent)
Depth=0, Chain="" - Note over EventBus: 🛡️ 检查通过 + Note over EventBus: 🛡️ Checks passed EventBus->>Handler1: Handle(PromptOptimizationRequestEvent) - Handler1->>Handler1: 生成优化后的 Prompt + Handler1->>Handler1: Generate optimized prompt Handler1->>EventBus: PublishDerivedAsync(PromptOptimizationResponseEvent)
Depth=1, Chain="PromptOptimizationRequestEvent" - Note over EventBus: 🛡️ 检查通过 + Note over EventBus: 🛡️ Checks passed EventBus->>Handler2: Handle(PromptOptimizationResponseEvent) Handler2->>Service: CompleteRequest(TCS.SetResult) - Service-->>User: 返回优化结果 + Service-->>User: Return optimization result ``` --- -## 🛡️ 循环引用防护示例 +## 🛡️ Circular Reference Protection Examples -### 场景 1: 阻止直接循环 +### Scenario 1: Block direct cycle ```mermaid graph TD A[EventA
Depth=0
Chain=Empty] -->|Handler A| B[EventB
Depth=1
Chain=EventA] B -->|Handler B| C[EventA
Depth=2
Chain=EventA→EventB] - C -->|❌ 检测到循环| D[丢弃事件
记录错误日志] - + C -->|❌ Cycle detected| D[Drop event
Log error] + style D fill:#ff6b6b ``` -### 场景 2: 阻止深度超限 +### Scenario 2: Block depth overflow ```mermaid graph TD @@ -78,38 +78,38 @@ graph TD D --> E[...] E --> F[Event 10
Depth=9] F --> G[Event 11
Depth=10] - G -->|❌ 深度超限| H[丢弃事件
记录错误日志] - + G -->|❌ Depth limit exceeded| H[Drop event
Log error] + style G fill:#ffd93d style H fill:#ff6b6b ``` --- -## 🔍 EventBus 核心架构 +## 🔍 EventBus Core Architecture ```mermaid graph LR - subgraph 发布端 - A[业务代码] -->|PublishAsync| B[InMemoryEventBus] + subgraph Publisher + A[Business code] -->|PublishAsync| B[InMemoryEventBus] A2[Handler] -->|PublishDerivedAsync| B end - - B -->|WriteAsync| C[Channel
无界队列] - - subgraph 消费端 + + B -->|WriteAsync| C[Channel
Unbounded queue] + + subgraph Consumer C -->|ReadAllAsync| D[EventBusHostedService] - D -->|1. 重复检测| E{已处理?} - E -->|是| F[跳过] - E -->|否| G{深度超限?} - G -->|是| H[丢弃 + 日志] - G -->|否| I{循环检测} - I -->|是| J[丢弃 + 日志] - I -->|否| K[SemaphoreSlim
并发控制] - K --> L[Task.Run
异步处理] + D -->|1. Duplicate detection| E{Processed?} + E -->|Yes| F[Skip] + E -->|No| G{Depth overflow?} + G -->|Yes| H[Drop + Log] + G -->|No| I{Circular check} + I -->|Yes| J[Drop + Log] + I -->|No| K[SemaphoreSlim
Concurrency control] + K --> L[Task.Run
Async processing] L --> M[Handler.Handle] end - + style F fill:#ffd93d style H fill:#ff6b6b style J fill:#ff6b6b @@ -119,9 +119,9 @@ graph LR --- -## 📈 性能特性 +## 📈 Performance Characteristics -### 非阻塞发布 +### Non-blocking publish ```mermaid sequenceDiagram @@ -132,80 +132,80 @@ sequenceDiagram Caller->>EventBus: PublishAsync(event) EventBus->>Channel: WriteAsync(event) - Channel-->>EventBus: ValueTask (立即返回) + Channel-->>EventBus: ValueTask (returns immediately) EventBus-->>Caller: ValueTask (< 1ms) - - Note over Background: 后台异步处理 + + Note over Background: Background async processing Background->>Channel: ReadAllAsync() - Channel->>Background: 返回事件 - Background->>Background: 处理事件 + Channel->>Background: Return event + Background->>Background: Handle event ``` -### 并发控制 +### Concurrency control ```mermaid graph TD - A[Channel 队列] --> B{SemaphoreSlim} - B -->|获取信号| C1[Task 1] - B -->|获取信号| C2[Task 2] - B -->|获取信号| C3[Task 3] - B -->|获取信号| CN[Task N] - B -->|等待| W[等待队列] - + A[Channel queue] --> B{SemaphoreSlim} + B -->|Acquire permit| C1[Task 1] + B -->|Acquire permit| C2[Task 2] + B -->|Acquire permit| C3[Task 3] + B -->|Acquire permit| CN[Task N] + B -->|Wait| W[Waiting queue] + C1 --> D1[Handler] C2 --> D2[Handler] C3 --> D3[Handler] CN --> DN[Handler] - - D1 -->|释放信号| B - D2 -->|释放信号| B - D3 -->|释放信号| B - DN -->|释放信号| B - + + D1 -->|Release permit| B + D2 -->|Release permit| B + D3 -->|Release permit| B + DN -->|Release permit| B + style B fill:#6bcf7f style W fill:#ffd93d ``` --- -## 🎓 最佳实践总结 +## 🎓 Best Practices Summary -### ✅ 推荐模式 +### ✅ Recommended patterns -1. **请求-响应模式** (最安全) +1. **Request-response pattern** (safest) ``` Request → Handler → Response → Complete ``` -2. **单向事件流** (次安全) +2. **One-way event flow** (safer) ``` - EventA → EventB → EventC → ... (不回溯) + EventA → EventB → EventC → ... (no backtracking) ``` -3. **限制深度** (强制约束) +3. **Limit nesting depth** (hard constraint) ``` - 最多 3-5 层事件嵌套 + Maximum 3-5 levels of event nesting ``` -### ❌ 反模式 +### ❌ Anti-patterns -1. **响应再请求** (易循环) +1. **Response re-requests** (easy to cycle) ``` - Request → Response → Request (❌ 循环风险) + Request → Response → Request (❌ cycle risk) ``` -2. **相互发布** (易循环) +2. **Mutual publishing** (easy to cycle) ``` HandlerA publishes EventB - HandlerB publishes EventA (❌ 循环风险) + HandlerB publishes EventA (❌ cycle risk) ``` -3. **无限递归** (易爆栈) +3. **Infinite recursion** (stack/depth risk) ``` - EventA → EventA → EventA → ... (❌ 深度风险) + EventA → EventA → EventA → ... (❌ depth risk) ``` --- -**文档版本**: 1.0 -**最后更新**: 2026-03-24 +**Document Version**: 1.0 +**Last Updated**: 2026-03-24 From 2b0768ace0ed20f6067ce853c80d69ff4fb4bec9 Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 04:22:46 -0700 Subject: [PATCH 16/21] translate: Chinese comments to English in Senparc.Ncf.XncfBase --- src/Basic/Senparc.Ncf.XncfBase/Register.cs | 1424 ++++++++--------- .../Senparc.Ncf.XncfBase/XncfRegisterBase.cs | 930 +++++------ 2 files changed, 1177 insertions(+), 1177 deletions(-) diff --git a/src/Basic/Senparc.Ncf.XncfBase/Register.cs b/src/Basic/Senparc.Ncf.XncfBase/Register.cs index b4d90cfa3..c65d094cc 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Register.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Register.cs @@ -1,712 +1,712 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Options; -using Senparc.CO2NET.Cache; -using Senparc.CO2NET.Extensions; -using Senparc.CO2NET.RegisterServices; -using Senparc.CO2NET.Trace; -using Senparc.Ncf.Core; -using Senparc.Ncf.Core.AppServices; -using Senparc.Ncf.Core.AssembleScan; -using Senparc.Ncf.Core.Config; -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.Core.Exceptions; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Core.Models.DataBaseModel; -using Senparc.Ncf.Core.MultiTenant; -using Senparc.Ncf.Database; -using Senparc.Ncf.Service; -using Senparc.Ncf.XncfBase.Attributes; -using Senparc.Ncf.XncfBase.FunctionRenders; -using Senparc.Ncf.XncfBase.Threads; - -namespace Senparc.Ncf.XncfBase -{ - /// - /// Xncf 全局注册类 - /// - public static class Register - { - /// - /// 所有线程的集合 - /// - public static ConcurrentDictionary ThreadCollection { get; set; } = new ConcurrentDictionary(); - - public static FunctionRenderCollection FunctionRenderCollection { get; set; } = new FunctionRenderCollection(); - - /// - /// 所有自动注册 Xncf 的数据库的 ConfigurationMapping 对象 - /// - public static List XncfAutoConfigurationMappingList { get; set; } = new List(); - //public static List> XncfAutoConfigurationMappingList = new List>(); - - /// - /// 扫描程序集分类 - /// - enum ScanTypeKind - { - IXncfRegister, - //IXncfFunction, - XncfAutoConfigurationMappingAttribute - } - - private static void SetLog(StringBuilder sb, string log, bool addTime = true) - { - var msg = addTime ? $"[{SystemTime.Now}] {log}" : $"\t{log}"; - sb.AppendLine(msg); - //Debug.WriteLine(msg); - //Console.WriteLine(msg); - } - - - ///// - ///// 启动 XNCF 模块引擎,包括初始化扫描和注册等过程 - ///// - ///// - //[Obsolete("请使用 StartNcfEngine()", true)] - //public static string StartEngine(this IServiceCollection services, IConfiguration configuration, IHostEnvironment env) - //{ - // return StartNcfEngine(services, configuration, env, null); - //} - - /// - /// 启动 XNCF 模块引擎,包括初始化扫描和注册等过程 - /// - /// - /// - /// - /// 被包含的 dll 的文件名, “.Xncf.”会被必定包含在里面 - /// - public static string StartNcfEngine(this IServiceCollection services, IConfiguration configuration, IHostEnvironment env, string[] dllFilePatterns) - { - StringBuilder sb = new StringBuilder(); - SetLog(sb, "Start scanning XncfModules"); - - Senparc.Ncf.Core.VersionManager.ShowSuccessTip($"\t启动前自检开始 {SystemTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", null, false); - - var scanTypesCount = 0; - var hideTypeCount = 0; - ConcurrentDictionary types = new ConcurrentDictionary(); - - //所有 XNCF 模块,包括被忽略的。 - //var cache = CacheStrategyFactory.GetObjectCacheStrategyInstance(); - //using (cache.BeginCacheLock("Senparc.Ncf.XncfBase.Register", "Scan")) //在注册阶段还未完成缓存配置 - { - try - { - //遍历所有程序集 - var assemblies = AssembleScanHelper.GetAssembiles(true, dllFilePatterns: dllFilePatterns); - - int columnWidth1 = 42; - int columnWidth2 = 45; - int columnWidth3 = 15; - - SetLog(sb, " === Multiple databases detected ==="); - SetLog(sb, $"| {"Register".PadRight(columnWidth1)}| {"Full Name".PadRight(columnWidth2)}| {"Database Type".PadRight(columnWidth3)}", false); - SetLog(sb, $"|-{new String('-', columnWidth1)}|-{new String('-', columnWidth2)}|-{new String('-', columnWidth3)}", false); - AssembleScanHelper.AddAssembleScanItem(a => - { - //Console.WriteLine("FullName:" + a.FullName); - if (a.FullName.StartsWith("AutoMapper.")) - { - return;//忽略 AutoMapper - } - - scanTypesCount++; - var aTypes = a.GetTypes(); - - List exceptions = new List(); - - //遍历程序集内的所有类型 - foreach (var t in aTypes) - { - try - { - if (t.IsAbstract) - { - continue;//忽略抽象类 - } - - //Console.WriteLine(t.GetType().Name); - //获取 XncfRegister - if (t.GetInterfaces().Contains(typeof(IXncfRegister))) - { - types[t] = ScanTypeKind.IXncfRegister; - } - //获取 XncfFunction - //if (t.GetInterfaces().Contains(typeof(IXncfFunction))) - //{ - // types[t] = ScanTypeKind.IXncfFunction; /* 暂时不收录处理 */ - //} - //获取 XncfAutoConfigurationMapping - if (t.GetCustomAttributes(true).FirstOrDefault(z => z is XncfAutoConfigurationMappingAttribute) != null - /*&& t.GetInterfaces().Contains(typeof(IEntityTypeConfiguration<>))*/) - { - types[t] = ScanTypeKind.XncfAutoConfigurationMappingAttribute; - } - - - //获取多数据库配置(XncfDatabaseDbContext 的子类) - if (t.IsSubclassOf(typeof(DbContext)) /*t.IsSubclassOf(typeof(XncfDatabaseDbContext))*/ && - t.GetInterface(nameof(ISenparcEntitiesDbContext)) != null && - t.GetCustomAttributes(true).FirstOrDefault(z => z is MultipleMigrationDbContextAttribute) != null) - { - //获取特性 - var multiDbContextAttr = t.GetCustomAttributes(true).FirstOrDefault(z => z is MultipleMigrationDbContextAttribute) as MultipleMigrationDbContextAttribute; - - //添加配置 - var multipleDatabasePool = MultipleDatabasePool.Instance; - var result = multipleDatabasePool.TryAdd(multiDbContextAttr, t, new[] { columnWidth1, columnWidth2, columnWidth3 }); - SetLog(sb, result, false); - } - - //配置 FunctionRender - if (t.IsSubclassOf(typeof(AppServiceBase))) - { - //遍历其中具体方法 - var methods = t.GetMethods(); - var hasFunctionMethod = false; - foreach (var method in methods) - { - var attr = method.GetCustomAttributes(typeof(FunctionRenderAttribute), true).FirstOrDefault() as FunctionRenderAttribute; - if (attr != null) - { - FunctionRenderCollection.Add(method, attr); - hasFunctionMethod = true; - } - } - - services.AddScoped(t); - } - - //配置 ServiceBase - if ( - !t.IsAbstract && ( - - //由于泛型是在编译时确定的,所以不能直接使用IsSubclassOf方法来判断一个类是否为一个带泛型的类的基类。需要使用GetGenericTypeDefinition方法来获取泛型的基类,然后进行比较。 - (t.BaseType != null && t.BaseType.IsGenericType && t.BaseType.GetGenericTypeDefinition() == typeof(ServiceBase<>)) - || t.IsSubclassOf(typeof(ServiceDataBase)) - //|| t.IsSubclassOf(typeof(AppServiceBase)) - //|| t.IsInstanceOfType(typeof(IServiceDataBase)) - ) - ) - { - if (t != typeof(ServiceBase<>)) - { - //Console.WriteLine("------------> Add Scope:" + t.FullName); - services.AddScoped(t); - } - - } - } - catch (Exception ex) - { - exceptions.Add(ex); - SenparcTrace.SendCustomLog("扫描程序集发生错误", $"Type:{t.FullName}"); - } - } - - if (exceptions.Count > 0) - { - var errMsg = $"程序集 [{a.FullName}] 共检测出 {exceptions.Count} 个异常,如果非核心程序集可忽略"; - Console.WriteLine(errMsg); - SenparcTrace.SendCustomLog("程序集扫描异常记录", errMsg); - } - - }, true); - - SetLog(sb, $"{new String('-', columnWidth1 + columnWidth2 + columnWidth3 + 6)}", false); - SetLog(sb, ""); - } - catch (Exception ex) - { - Console.WriteLine($"扫描程集异常退出,可能无法获得完整程序集信息:{ex.Message}"); - } - - SetLog(sb, $"Satisfies the ScanTypeKind condition: {types.Count()}"); - - //先注册 XncfRegister - { - //筛选 - var allTypes = types.Where(z => z.Value == ScanTypeKind.IXncfRegister/* && z.Key.GetInterfaces().Contains(typeof(IXncfRegister))*/) - .Select(z => z.Key); - //按照优先级进行排序(降序,数字越大越在前) - var orderedTypes = allTypes.OrderByDescending(z => - { - var orderAttribute = z.GetCustomAttributes(true).FirstOrDefault(attr => attr is XncfOrderAttribute) as XncfOrderAttribute; - if (orderAttribute != null) - { - return orderAttribute.Order; - } - return 0; - }); - - - foreach (var type in orderedTypes) - { - SetLog(sb, $"Scan IXncfRegister: {type.FullName}"); - - var register = type.Assembly.CreateInstance(type.FullName) as IXncfRegister; - - if (!XncfRegisterManager.RegisterList.Contains(register)) - { - if (XncfRegisterManager.RegisterList.Exists(z => z.Uid.Equals(register.Uid, StringComparison.OrdinalIgnoreCase))) - { - MultipleDatabaseType? multipleDatabaseType = null; - try - { - multipleDatabaseType = DatabaseConfigurationFactory.Instance.Current.MultipleDatabaseType; - } - catch (Exception ex) - { - SenparcTrace.BaseExceptionLog(ex); - } - - if (multipleDatabaseType == null) - { - Console.WriteLine($"MultipleDatabaseType 为 null(轮询${type.FullName})"); - } - else if (multipleDatabaseType == MultipleDatabaseType.InMemory) - { - //单元测试,忽略 - Console.WriteLine("MultipleDatabaseType.InMemory 模式,单元测试忽略重复加载"); - } - else - { - throw new XncfFunctionException("已经存在相同 Uid 的模块:" + register.Uid); - } - } - - if (register.IgnoreInstall) - { - hideTypeCount++; - } - XncfRegisterManager.RegisterList.Add(register);//只有允许安装的才进行注册,否则执行完即结束 - services.AddScoped(type);//DI 中注册 - //foreach (var functionType in register.Functions) - //{ - // services.AddScoped(functionType);//DI 中注册 - //} - } - - //初始化数据库 - if (register is IXncfDatabase) - { - - } - - } - } - - //处理 XncfAutoConfigurationMappingAttribute - { - var allTypes = types.Where(z => z.Value == ScanTypeKind.XncfAutoConfigurationMappingAttribute).Select(z => z.Key); - foreach (var type in allTypes) - { - var obj = type.Assembly.CreateInstance(type.FullName); - XncfAutoConfigurationMappingList.Add(obj /*as IEntityTypeConfiguration*/); - } - } - } - - var scanResult = $"The initialization scan ended, scanning a total of {scanTypesCount} Assemblies."; - if (hideTypeCount > 0) - { - scanResult += $"Among them, {hideTypeCount} assemblies are non-installed assemblies and will not be cached."; - } - SetLog(sb, $"{scanResult}"); - - - //Repository & Service - services.AddScoped(typeof(Senparc.Ncf.Repository.IRepositoryBase<>), typeof(Senparc.Ncf.Repository.RepositoryBase<>)); - services.AddScoped(typeof(Senparc.Ncf.Repository.RepositoryBase<>)); - services.AddScoped(typeof(Senparc.Ncf.Repository.IClientRepositoryBase<>), typeof(Senparc.Ncf.Repository.ClientRepositoryBase<>)); - services.AddScoped(typeof(Senparc.Ncf.Repository.ClientRepositoryBase<>)); - services.AddScoped(typeof(IServiceBase<>), typeof(ServiceBase<>)); - services.AddScoped(typeof(ServiceBase<>)); - services.AddScoped(typeof(ServiceBase<>)); - - //AppService - services.AddScoped(typeof(AppRequestBase)); - services.AddScoped(typeof(IAppRequest), typeof(AppRequestBase)); - - services.AddScoped(typeof(AppResponseBase)); - services.AddScoped(typeof(AppResponseBase<>)); - services.AddScoped(typeof(IAppResponse), typeof(AppResponseBase)); - //services.AddScoped(typeof(IAppResponse<>), typeof(AppResponseBase<>)); - - //ConfigurationMapping - services.AddScoped(typeof(ConfigurationMappingBase<>)); - services.AddScoped(typeof(ConfigurationMappingWithIdBase<,>)); - - - services.AddScoped(typeof(DbContextOptionsBuilder<>)); - services.AddScoped(typeof(DbContextOptionsBuilder)); - - //多租户 - services.AddScoped();//TODO:需要动态识别,当前请求缓存中读取并转换 - - //注册 Senarc.Ncf.Service 中的服务 - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - - services.ScanAssamblesForAutoDI(); - //已经添加完所有程序集自动扫描的委托,立即执行扫描(必须) - AssembleScanHelper.RunScan(dllFilePatterns); - //services.AddSingleton(); - - - #region 支持 AutoMapper - - //Console.WriteLine("----------"); - //Console.WriteLine("XNCF 模块进行 AutoMapper 映射 foreach (var xncfRegister in XncfRegisterManager.RegisterList)"); - //Console.WriteLine("----------"); - //XNCF 模块进行 AutoMapper 映射 - foreach (var xncfRegister in XncfRegisterManager.RegisterList) - { - xncfRegister.OnAutoMapMapping(services, configuration); - } - - //Console.WriteLine("----------"); - //Console.WriteLine("引入当前系统"); - //Console.WriteLine("----------"); - - //引入当前系统 - services.AddAutoMapper(z => z.AddProfile()); - - //引入所有模块 TODO:XncfModuleProfile 构造函数会在“XNCF 模块进行 AutoMapper 映射”之后就执行,导致 XNCF 模块的 AutoMapper 映射无效 - services.AddAutoMapper(z => z.AddProfile()); - - #endregion - - //说明:AutoMapper 需要放到 XNCF 注册之前,因为 XNCF 内可能存在动态生成的程序集引发异常 - - //XNCF 模块进行 Service 注册 - foreach (var xncfRegister in XncfRegisterManager.RegisterList) - { - try - { - xncfRegister.AddXncfModule(services, configuration, env); - - //MCP - if (xncfRegister.EnableMcpServer) - { - xncfRegister.AddMcpServer(services, xncfRegister); - } - } - catch (Exception ex) - { - _ = new NcfExceptionBase($"{xncfRegister.Name} 模块 xncfRegister.AddXncfModule() 出错 :{ex.Message}", ex); - - } - } - - //标记所有 AddXncfModule 方法已经执行完成 - Ncf.Core.Config.SiteConfig.NcfCoreState.AllAddXncfModuleApplied = true; - Ncf.Core.Config.SiteConfig.NcfCoreState.AllDatabaseXncfLoaded = true; - - SetLog(sb, $"Finish services.AddXncfModule(): Total of {scanTypesCount} assemblies were scanned."); - - return sb.ToString(); - } - - /// - /// 扫描并安装(自动安装,无需手动) - /// TODO:放置到 Service 中,为系统模块自动升级 - /// - /// 现有已安装的模块 - /// IServiceProvider - /// 安装或更新后执行 - /// 只扫描并更新特定的Uid - /// - public static async Task ScanAndInstall(IList xncfModuleDtos, - IServiceProvider serviceProvider, - Func afterInstalledOrUpdated = null, - string justScanThisUid = null) - { - StringBuilder sb = new StringBuilder(); - SetLog(sb, "开始扫描 XncfModules"); - - //先注册 - var updatedCount = 0; - var cache = CacheStrategyFactory.GetObjectCacheStrategyInstance(); - using (await cache.BeginCacheLockAsync("Senparc.Ncf.XncfBase.Register", "Scan").ConfigureAwait(false)) - { - foreach (var register in XncfRegisterManager.RegisterList) - { - SetLog(sb, $"扫描到 IXncfRegister:{register.GetType().FullName}"); - if (register.IgnoreInstall) - { - SetLog(sb, $"当前模块要求忽略安装 uid:[{justScanThisUid}],此模块跳过"); - continue; - } - - if (justScanThisUid != null && register.Uid != justScanThisUid) - { - SetLog(sb, $"由于只要求更新 uid:[{justScanThisUid}],此模块跳过"); - continue; - } - else - { - SetLog(sb, "符合尝试安装/更新要求,继续执行"); - } - - var xncfModuleStoredDto = xncfModuleDtos.FirstOrDefault(z => z.Uid == register.Uid); - var xncfModuleAssemblyDto = new UpdateVersion_XncfModuleDto(register.Name, register.Uid, register.MenuName, register.Version, register.Description, register.Icon); - - //检查更新,并安装到数据库 - var xncfModuleService = serviceProvider.GetService(); - var installOrUpdate = await xncfModuleService.CheckAndUpdateVersionAsync(xncfModuleStoredDto, xncfModuleAssemblyDto).ConfigureAwait(false); - SetLog(sb, $"是否更新版本:{installOrUpdate?.ToString() ?? "未安装"}"); - - if (installOrUpdate.HasValue) - { - updatedCount++; - - //执行安装程序 - await register.InstallOrUpdateAsync(serviceProvider, installOrUpdate.Value).ConfigureAwait(false); - - await afterInstalledOrUpdated?.Invoke(register, installOrUpdate.Value); - } - } - } - - SetLog(sb, $"扫描结束,共新增或更新 {updatedCount} 个程序集"); - return sb.ToString(); - } - - ///// - ///// 通常在 Startup.cs 中的 Configure() 方法中执行 - ///// - ///// - ///// CO2NET 注册对象 - ///// SenparcCoreSetting - ///// - //public static IApplicationBuilder UseXncfModules(this IApplicationBuilder app, IRegisterService registerService, SenparcCoreSetting senparcCoreSetting = null, bool autoRunInstall = false) - //where TDatabaseConfiguration : IDatabaseConfiguration, new() - //{ - // return UseXncfModules(app, registerService, senparcCoreSetting, autoRunInstall); - //} - - - /// - /// 通常在 Startup.cs 中的 Configure() 方法中执行 - /// - /// - /// CO2NET 注册对象 - /// SenparcCoreSetting - /// - public static IApplicationBuilder UseXncfModules(this IApplicationBuilder app, IRegisterService registerService, SenparcCoreSetting senparcCoreSetting = null, bool autoRunInstall = false) - { - if (senparcCoreSetting == null) - { - using (var scope = app.ApplicationServices.CreateAsyncScope()) - { - senparcCoreSetting = scope.ServiceProvider.GetService>()?.Value; - } - } - - Senparc.Ncf.Core.Config.SiteConfig.SenparcCoreSetting = senparcCoreSetting; - - foreach (var register in XncfRegisterManager.RegisterList) - { - try - { - register.UseXncfModule(app, registerService); - - //TODO: 后期改为远程(其他模块)查找租户 - - // 是否已经载入过数据库功能的 Database - if (!SiteConfig.DatabaseXncfLoaded - //SystemCore 需要支持 IXncfDatabase,但并不属于实际运作的数据库模块,因此需要排除 - && register.Uid != SiteConfig.SYSTEM_XNCF_MODULE_SYSTEM_CORE_UID - && register is IXncfDatabase - ) - { - SiteConfig.DatabaseXncfLoaded = true; - } - } - catch (Exception ex) - { - SenparcTrace.SendCustomLog("从 XncfRegisterManager.RegisterList 执行 register.UseXncfModule() 方法发生异常", - $@"模块:{register.Name} -异常信息:{ex.Message} -详情:{ex.ToString()}"); - } - - //执行中间件 - if (register is IXncfMiddleware middlewareRegister) - { - try - { - middlewareRegister.UseMiddleware(app); - } - catch - { - } - } - - //执行线程 - if (register is IXncfThread threadRegister) - { - try - { - XncfThreadBuilder xncfThreadBuilder = new XncfThreadBuilder(); - threadRegister.ThreadConfig(xncfThreadBuilder); - xncfThreadBuilder.Build(app, register); - } - catch (Exception ex) - { - SenparcTrace.BaseExceptionLog(ex); - } - - //任意一个 ThreadXncf 已经载入 - Ncf.Core.Config.SiteConfig.NcfCoreState.AnyThreadXncfLoaded = true; - } - - - //MCP 服务器 - if (register.EnableMcpServer) - { - register.UseMcpServer(app, registerService); - } - } - - //所有 ThreadXncf 已经载入 - Ncf.Core.Config.SiteConfig.NcfCoreState.AllThreadXncfLoaded = true; - //所有 UseXncfModule 已经执行完成 - Ncf.Core.Config.SiteConfig.NcfCoreState.AllUseXncfModuleApplied = true; - - //自动运行安装 - - if (autoRunInstall) - { - Task.Factory.StartNew(async () => - { - using (var scope = app.ApplicationServices.CreateScope()) - { - foreach (var register in XncfRegisterManager.RegisterList) - { - await register.InstallOrUpdateAsync(scope.ServiceProvider, InstallOrUpdate.Install); - } - } - }).GetAwaiter().GetResult(); - } - - return app; - } - - /// - /// 所有已经使用的 [AutoConfigurationMapping] 对应的实体类型 - /// - public static List ApplyedAutoConfigurationMappingTypes { get; set; } = new List(); - private static List AddedApplyedAutoConfigurationMappingEntityTypes { get; set; } = new List(); - /// - /// 自动添加所有 XNCF 模块中标记了 [XncfAutoConfigurationMapping] 特性的对象 - /// - public static void ApplyAllAutoConfigurationMapping(ModelBuilder modelBuilder) - { - var entityTypeConfigurationMethod = typeof(ModelBuilder).GetMethods() - .FirstOrDefault(z => z.Name == "ApplyConfiguration" && z.ContainsGenericParameters && z.GetParameters().SingleOrDefault()?.ParameterType.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)); - - //所有模块中数据库实体中自动获取所有的 DbSet 下的实体类型 - - //TODO:筛选基础类中的 - var xncfDatabaseDbContextList = MultipleDatabasePool.Instance.Values.SelectMany(z => z.Values); - //TODO:为了进一步提高效率,可以对同一个 DbContext 的子类去重 - - foreach (var xncfDatabaseDbContextType in xncfDatabaseDbContextList) - { - var setKeyInfoList = EntitySetKeys.GetEntitySetInfo(xncfDatabaseDbContextType).Values; - foreach (var setKeyInfo in setKeyInfoList) - { - //数据库实体类型 - var entityType = setKeyInfo.DbSetType; - if (AddedApplyedAutoConfigurationMappingEntityTypes.Contains(entityType)) - { - //Console.WriteLine($"\t [{xncfDatabaseDbContextType.Name}]ApplyAllAutoConfigurationMapping 有重复 setKeyInfo:{entityType.Name},已跳过"); - continue; - } - //Console.WriteLine($"\t [{xncfDatabaseDbContextType.Name}]ApplyAllAutoConfigurationMapping 处理 setKeyInfo:{entityType.Name}"); - - //默认空 ConfigurationMapping 对象的泛型类型 - var blankEntityTypeConfigurationType = typeof(BlankEntityTypeConfiguration<>).MakeGenericType(entityType); - //创建一个新的实例 - var blankEntityTypeConfiguration = Activator.CreateInstance(blankEntityTypeConfigurationType); - //最佳到末尾,这样可以优先执行用户自定义的代码 - XncfAutoConfigurationMappingList.Add(blankEntityTypeConfiguration); - AddedApplyedAutoConfigurationMappingEntityTypes.Add(entityType); - } - } - - var logs = new StringBuilder(); - - try - { - - foreach (var autoConfigurationMapping in XncfAutoConfigurationMappingList) - { - if (autoConfigurationMapping == null) - { - continue; - } - - logs.AppendLine(autoConfigurationMapping.GetType().FullName); - - //获取配置实体类型,如:DbConfig_WeixinUserConfigurationMapping - Type mappintConfigType = autoConfigurationMapping.GetType(); - //获取 IEntityTypeConfiguration 接口 - var interfaceType = mappintConfigType.GetInterfaces().FirstOrDefault(z => z.Name.StartsWith("IEntityTypeConfiguration")); - if (interfaceType == null) - { - Console.WriteLine("interfaceType 为 null(不为 IEntityTypeConfiguration)"); - continue; - } - //实体类型,如:DbConfig - Type entityType = interfaceType.GenericTypeArguments[0]; - - //PS:此处不能过滤,否则可能导致如 SystemServiceEntities_SqlServer / SystemServiceEntities_Mysql 中只有先注册的对象才成功,后面的被忽略 - //if (ApplyedAutoConfigurationMappingTypes.Contains(entityType)) - //{ - // //如果已经添加过则跳过。作此判断因为:原始的 XncfAutoConfigurationMappingList 数据可能和上一步自动添加 DataSet 中的对象有重复 - // //continue; - //} - - entityTypeConfigurationMethod.MakeGenericMethod(entityType) - .Invoke(modelBuilder, new object[1] - { - autoConfigurationMapping - }); - - ApplyedAutoConfigurationMappingTypes.Add(entityType); - - //entityTypeConfigurationMethod.Invoke(modelBuilder, new[] { autoConfigurationMapping }); - } - } - finally - { - SenparcTrace.SendCustomLog("监测到 ApplyAllAutoConfigurationMapping 执行", logs.ToString()); - } - - //TODO:添加 IQueryTypeConfiguration<> - - - } - } -} +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using Senparc.CO2NET.Cache; +using Senparc.CO2NET.Extensions; +using Senparc.CO2NET.RegisterServices; +using Senparc.CO2NET.Trace; +using Senparc.Ncf.Core; +using Senparc.Ncf.Core.AppServices; +using Senparc.Ncf.Core.AssembleScan; +using Senparc.Ncf.Core.Config; +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.Core.Exceptions; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Core.Models.DataBaseModel; +using Senparc.Ncf.Core.MultiTenant; +using Senparc.Ncf.Database; +using Senparc.Ncf.Service; +using Senparc.Ncf.XncfBase.Attributes; +using Senparc.Ncf.XncfBase.FunctionRenders; +using Senparc.Ncf.XncfBase.Threads; + +namespace Senparc.Ncf.XncfBase +{ + /// + /// Xncf Global Registration Class + /// + public static class Register + { + /// + /// Collection of all threads + /// + public static ConcurrentDictionary ThreadCollection { get; set; } = new ConcurrentDictionary(); + + public static FunctionRenderCollection FunctionRenderCollection { get; set; } = new FunctionRenderCollection(); + + /// + /// ConfigurationMapping objects for all automatically registered Xncf databases + /// + public static List XncfAutoConfigurationMappingList { get; set; } = new List(); + //public static List> XncfAutoConfigurationMappingList = new List>(); + + /// + /// Assembly scan classification + /// + enum ScanTypeKind + { + IXncfRegister, + //IXncfFunction, + XncfAutoConfigurationMappingAttribute + } + + private static void SetLog(StringBuilder sb, string log, bool addTime = true) + { + var msg = addTime ? $"[{SystemTime.Now}] {log}" : $"\t{log}"; + sb.AppendLine(msg); + //Debug.WriteLine(msg); + //Console.WriteLine(msg); + } + + + ///// + ///// Start the XNCF module engine, including initialization scanning and registration + ///// + ///// + //[Obsolete("Please use StartNcfEngine()", true)] + //public static string StartEngine(this IServiceCollection services, IConfiguration configuration, IHostEnvironment env) + //{ + // return StartNcfEngine(services, configuration, env, null); + //} + + /// + /// Start the XNCF module engine, including initialization scanning and registration + /// + /// + /// + /// + /// File names of included dlls; ".Xncf." will always be included + /// + public static string StartNcfEngine(this IServiceCollection services, IConfiguration configuration, IHostEnvironment env, string[] dllFilePatterns) + { + StringBuilder sb = new StringBuilder(); + SetLog(sb, "Start scanning XncfModules"); + + Senparc.Ncf.Core.VersionManager.ShowSuccessTip($"\tPre-start self-check begins {SystemTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", null, false); + + var scanTypesCount = 0; + var hideTypeCount = 0; + ConcurrentDictionary types = new ConcurrentDictionary(); + + //All XNCF modules, including ignored ones. + //var cache = CacheStrategyFactory.GetObjectCacheStrategyInstance(); + //using (cache.BeginCacheLock("Senparc.Ncf.XncfBase.Register", "Scan")) //Cache configuration not yet complete during registration phase + { + try + { + //Iterate over all assemblies + var assemblies = AssembleScanHelper.GetAssembiles(true, dllFilePatterns: dllFilePatterns); + + int columnWidth1 = 42; + int columnWidth2 = 45; + int columnWidth3 = 15; + + SetLog(sb, " === Multiple databases detected ==="); + SetLog(sb, $"| {"Register".PadRight(columnWidth1)}| {"Full Name".PadRight(columnWidth2)}| {"Database Type".PadRight(columnWidth3)}", false); + SetLog(sb, $"|-{new String('-', columnWidth1)}|-{new String('-', columnWidth2)}|-{new String('-', columnWidth3)}", false); + AssembleScanHelper.AddAssembleScanItem(a => + { + //Console.WriteLine("FullName:" + a.FullName); + if (a.FullName.StartsWith("AutoMapper.")) + { + return;//Ignore AutoMapper + } + + scanTypesCount++; + var aTypes = a.GetTypes(); + + List exceptions = new List(); + + //Iterate over all types in the assembly + foreach (var t in aTypes) + { + try + { + if (t.IsAbstract) + { + continue;//Ignore abstract classes + } + + //Console.WriteLine(t.GetType().Name); + //Get XncfRegister + if (t.GetInterfaces().Contains(typeof(IXncfRegister))) + { + types[t] = ScanTypeKind.IXncfRegister; + } + //Get XncfFunction + //if (t.GetInterfaces().Contains(typeof(IXncfFunction))) + //{ + // types[t] = ScanTypeKind.IXncfFunction; /* Not collected for processing temporarily */ + //} + //Get XncfAutoConfigurationMapping + if (t.GetCustomAttributes(true).FirstOrDefault(z => z is XncfAutoConfigurationMappingAttribute) != null + /*&& t.GetInterfaces().Contains(typeof(IEntityTypeConfiguration<>))*/) + { + types[t] = ScanTypeKind.XncfAutoConfigurationMappingAttribute; + } + + + //Get multi-database configuration (subclasses of XncfDatabaseDbContext) + if (t.IsSubclassOf(typeof(DbContext)) /*t.IsSubclassOf(typeof(XncfDatabaseDbContext))*/ && + t.GetInterface(nameof(ISenparcEntitiesDbContext)) != null && + t.GetCustomAttributes(true).FirstOrDefault(z => z is MultipleMigrationDbContextAttribute) != null) + { + //Get attribute + var multiDbContextAttr = t.GetCustomAttributes(true).FirstOrDefault(z => z is MultipleMigrationDbContextAttribute) as MultipleMigrationDbContextAttribute; + + //Add configuration + var multipleDatabasePool = MultipleDatabasePool.Instance; + var result = multipleDatabasePool.TryAdd(multiDbContextAttr, t, new[] { columnWidth1, columnWidth2, columnWidth3 }); + SetLog(sb, result, false); + } + + //Configure FunctionRender + if (t.IsSubclassOf(typeof(AppServiceBase))) + { + //Iterate over specific methods + var methods = t.GetMethods(); + var hasFunctionMethod = false; + foreach (var method in methods) + { + var attr = method.GetCustomAttributes(typeof(FunctionRenderAttribute), true).FirstOrDefault() as FunctionRenderAttribute; + if (attr != null) + { + FunctionRenderCollection.Add(method, attr); + hasFunctionMethod = true; + } + } + + services.AddScoped(t); + } + + //Configure ServiceBase + if ( + !t.IsAbstract && ( + + //Since generics are determined at compile time, IsSubclassOf cannot be used directly to check if a class is a subclass of a generic base class. Use GetGenericTypeDefinition to get the generic base class for comparison. + (t.BaseType != null && t.BaseType.IsGenericType && t.BaseType.GetGenericTypeDefinition() == typeof(ServiceBase<>)) + || t.IsSubclassOf(typeof(ServiceDataBase)) + //|| t.IsSubclassOf(typeof(AppServiceBase)) + //|| t.IsInstanceOfType(typeof(IServiceDataBase)) + ) + ) + { + if (t != typeof(ServiceBase<>)) + { + //Console.WriteLine("------------> Add Scope:" + t.FullName); + services.AddScoped(t); + } + + } + } + catch (Exception ex) + { + exceptions.Add(ex); + SenparcTrace.SendCustomLog("Error occurred while scanning assembly", $"Type:{t.FullName}"); + } + } + + if (exceptions.Count > 0) + { + var errMsg = $"Assembly [{a.FullName}] detected {exceptions.Count} exceptions; non-core assemblies can be ignored"; + Console.WriteLine(errMsg); + SenparcTrace.SendCustomLog("Assembly scan exception log", errMsg); + } + + }, true); + + SetLog(sb, $"{new String('-', columnWidth1 + columnWidth2 + columnWidth3 + 6)}", false); + SetLog(sb, ""); + } + catch (Exception ex) + { + Console.WriteLine($"Assembly scan exited abnormally; complete assembly information may not be available: {ex.Message}"); + } + + SetLog(sb, $"Satisfies the ScanTypeKind condition: {types.Count()}"); + + //Register XncfRegister first + { + //Filter + var allTypes = types.Where(z => z.Value == ScanTypeKind.IXncfRegister/* && z.Key.GetInterfaces().Contains(typeof(IXncfRegister))*/) + .Select(z => z.Key); + //Sort by priority in descending order (higher number = higher priority) + var orderedTypes = allTypes.OrderByDescending(z => + { + var orderAttribute = z.GetCustomAttributes(true).FirstOrDefault(attr => attr is XncfOrderAttribute) as XncfOrderAttribute; + if (orderAttribute != null) + { + return orderAttribute.Order; + } + return 0; + }); + + + foreach (var type in orderedTypes) + { + SetLog(sb, $"Scan IXncfRegister: {type.FullName}"); + + var register = type.Assembly.CreateInstance(type.FullName) as IXncfRegister; + + if (!XncfRegisterManager.RegisterList.Contains(register)) + { + if (XncfRegisterManager.RegisterList.Exists(z => z.Uid.Equals(register.Uid, StringComparison.OrdinalIgnoreCase))) + { + MultipleDatabaseType? multipleDatabaseType = null; + try + { + multipleDatabaseType = DatabaseConfigurationFactory.Instance.Current.MultipleDatabaseType; + } + catch (Exception ex) + { + SenparcTrace.BaseExceptionLog(ex); + } + + if (multipleDatabaseType == null) + { + Console.WriteLine($"MultipleDatabaseType is null (polling ${type.FullName})"); + } + else if (multipleDatabaseType == MultipleDatabaseType.InMemory) + { + //Unit test mode, ignore duplicate loading + Console.WriteLine("MultipleDatabaseType.InMemory mode, unit test ignores duplicate loading"); + } + else + { + throw new XncfFunctionException("A module with the same Uid already exists: " + register.Uid); + } + } + + if (register.IgnoreInstall) + { + hideTypeCount++; + } + XncfRegisterManager.RegisterList.Add(register);//Only register if installation is allowed; otherwise execution ends + services.AddScoped(type);//Register in DI + //foreach (var functionType in register.Functions) + //{ + // services.AddScoped(functionType);//Register in DI + //} + } + + //Initialize database + if (register is IXncfDatabase) + { + + } + + } + } + + //Handle XncfAutoConfigurationMappingAttribute + { + var allTypes = types.Where(z => z.Value == ScanTypeKind.XncfAutoConfigurationMappingAttribute).Select(z => z.Key); + foreach (var type in allTypes) + { + var obj = type.Assembly.CreateInstance(type.FullName); + XncfAutoConfigurationMappingList.Add(obj /*as IEntityTypeConfiguration*/); + } + } + } + + var scanResult = $"The initialization scan ended, scanning a total of {scanTypesCount} Assemblies."; + if (hideTypeCount > 0) + { + scanResult += $"Among them, {hideTypeCount} assemblies are non-installed assemblies and will not be cached."; + } + SetLog(sb, $"{scanResult}"); + + + //Repository & Service + services.AddScoped(typeof(Senparc.Ncf.Repository.IRepositoryBase<>), typeof(Senparc.Ncf.Repository.RepositoryBase<>)); + services.AddScoped(typeof(Senparc.Ncf.Repository.RepositoryBase<>)); + services.AddScoped(typeof(Senparc.Ncf.Repository.IClientRepositoryBase<>), typeof(Senparc.Ncf.Repository.ClientRepositoryBase<>)); + services.AddScoped(typeof(Senparc.Ncf.Repository.ClientRepositoryBase<>)); + services.AddScoped(typeof(IServiceBase<>), typeof(ServiceBase<>)); + services.AddScoped(typeof(ServiceBase<>)); + services.AddScoped(typeof(ServiceBase<>)); + + //AppService + services.AddScoped(typeof(AppRequestBase)); + services.AddScoped(typeof(IAppRequest), typeof(AppRequestBase)); + + services.AddScoped(typeof(AppResponseBase)); + services.AddScoped(typeof(AppResponseBase<>)); + services.AddScoped(typeof(IAppResponse), typeof(AppResponseBase)); + //services.AddScoped(typeof(IAppResponse<>), typeof(AppResponseBase<>)); + + //ConfigurationMapping + services.AddScoped(typeof(ConfigurationMappingBase<>)); + services.AddScoped(typeof(ConfigurationMappingWithIdBase<,>)); + + + services.AddScoped(typeof(DbContextOptionsBuilder<>)); + services.AddScoped(typeof(DbContextOptionsBuilder)); + + //Multi-tenant + services.AddScoped();//TODO: needs dynamic recognition; read and convert from current request cache + + //Register services in Senarc.Ncf.Service + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + services.ScanAssamblesForAutoDI(); + //All assembly auto-scan delegates have been added; run the scan immediately (required) + AssembleScanHelper.RunScan(dllFilePatterns); + //services.AddSingleton(); + + + #region Support AutoMapper + + //Console.WriteLine("----------"); + //Console.WriteLine("XNCF modules performing AutoMapper mapping foreach (var xncfRegister in XncfRegisterManager.RegisterList)"); + //Console.WriteLine("----------"); + //XNCF modules perform AutoMapper mapping + foreach (var xncfRegister in XncfRegisterManager.RegisterList) + { + xncfRegister.OnAutoMapMapping(services, configuration); + } + + //Console.WriteLine("----------"); + //Console.WriteLine("Include current system"); + //Console.WriteLine("----------"); + + //Include current system + services.AddAutoMapper(z => z.AddProfile()); + + //Include all modules TODO: XncfModuleProfile constructor will execute after "XNCF modules AutoMapper mapping", making XNCF module AutoMapper mapping ineffective + services.AddAutoMapper(z => z.AddProfile()); + + #endregion + + //Note: AutoMapper must be placed before XNCF registration, because dynamic assemblies inside XNCF may cause exceptions + + //XNCF modules perform Service registration + foreach (var xncfRegister in XncfRegisterManager.RegisterList) + { + try + { + xncfRegister.AddXncfModule(services, configuration, env); + + //MCP + if (xncfRegister.EnableMcpServer) + { + xncfRegister.AddMcpServer(services, xncfRegister); + } + } + catch (Exception ex) + { + _ = new NcfExceptionBase($"{xncfRegister.Name} module xncfRegister.AddXncfModule() error: {ex.Message}", ex); + + } + } + + //Mark all AddXncfModule methods as having completed execution + Ncf.Core.Config.SiteConfig.NcfCoreState.AllAddXncfModuleApplied = true; + Ncf.Core.Config.SiteConfig.NcfCoreState.AllDatabaseXncfLoaded = true; + + SetLog(sb, $"Finish services.AddXncfModule(): Total of {scanTypesCount} assemblies were scanned."); + + return sb.ToString(); + } + + /// + /// Scan and install (auto-install, no manual action required) + /// TODO: Move to Service for automatic system module upgrades + /// + /// Currently installed modules + /// IServiceProvider + /// Callback executed after install or update + /// Only scan and update a specific Uid + /// + public static async Task ScanAndInstall(IList xncfModuleDtos, + IServiceProvider serviceProvider, + Func afterInstalledOrUpdated = null, + string justScanThisUid = null) + { + StringBuilder sb = new StringBuilder(); + SetLog(sb, "Start scanning XncfModules"); + + //Register first + var updatedCount = 0; + var cache = CacheStrategyFactory.GetObjectCacheStrategyInstance(); + using (await cache.BeginCacheLockAsync("Senparc.Ncf.XncfBase.Register", "Scan").ConfigureAwait(false)) + { + foreach (var register in XncfRegisterManager.RegisterList) + { + SetLog(sb, $"Found IXncfRegister: {register.GetType().FullName}"); + if (register.IgnoreInstall) + { + SetLog(sb, $"Current module requires installation to be ignored, uid: [{justScanThisUid}], skipping this module"); + continue; + } + + if (justScanThisUid != null && register.Uid != justScanThisUid) + { + SetLog(sb, $"Only updating uid: [{justScanThisUid}], skipping this module"); + continue; + } + else + { + SetLog(sb, "Meets install/update requirements, proceeding"); + } + + var xncfModuleStoredDto = xncfModuleDtos.FirstOrDefault(z => z.Uid == register.Uid); + var xncfModuleAssemblyDto = new UpdateVersion_XncfModuleDto(register.Name, register.Uid, register.MenuName, register.Version, register.Description, register.Icon); + + //Check for updates and install to database + var xncfModuleService = serviceProvider.GetService(); + var installOrUpdate = await xncfModuleService.CheckAndUpdateVersionAsync(xncfModuleStoredDto, xncfModuleAssemblyDto).ConfigureAwait(false); + SetLog(sb, $"Version update status: {installOrUpdate?.ToString() ?? "Not installed"}"); + + if (installOrUpdate.HasValue) + { + updatedCount++; + + //Execute install routine + await register.InstallOrUpdateAsync(serviceProvider, installOrUpdate.Value).ConfigureAwait(false); + + await afterInstalledOrUpdated?.Invoke(register, installOrUpdate.Value); + } + } + } + + SetLog(sb, $"Scan complete, {updatedCount} assemblies added or updated"); + return sb.ToString(); + } + + ///// + ///// Typically executed in the Configure() method of Startup.cs + ///// + ///// + ///// CO2NET registration object + ///// SenparcCoreSetting + ///// + //public static IApplicationBuilder UseXncfModules(this IApplicationBuilder app, IRegisterService registerService, SenparcCoreSetting senparcCoreSetting = null, bool autoRunInstall = false) + //where TDatabaseConfiguration : IDatabaseConfiguration, new() + //{ + // return UseXncfModules(app, registerService, senparcCoreSetting, autoRunInstall); + //} + + + /// + /// Typically executed in the Configure() method of Startup.cs + /// + /// + /// CO2NET registration object + /// SenparcCoreSetting + /// + public static IApplicationBuilder UseXncfModules(this IApplicationBuilder app, IRegisterService registerService, SenparcCoreSetting senparcCoreSetting = null, bool autoRunInstall = false) + { + if (senparcCoreSetting == null) + { + using (var scope = app.ApplicationServices.CreateAsyncScope()) + { + senparcCoreSetting = scope.ServiceProvider.GetService>()?.Value; + } + } + + Senparc.Ncf.Core.Config.SiteConfig.SenparcCoreSetting = senparcCoreSetting; + + foreach (var register in XncfRegisterManager.RegisterList) + { + try + { + register.UseXncfModule(app, registerService); + + //TODO: later change to remote (other module) tenant lookup + + // Whether a database-capable module has already been loaded + if (!SiteConfig.DatabaseXncfLoaded + //SystemCore needs to support IXncfDatabase, but is not an actual database module, so it should be excluded + && register.Uid != SiteConfig.SYSTEM_XNCF_MODULE_SYSTEM_CORE_UID + && register is IXncfDatabase + ) + { + SiteConfig.DatabaseXncfLoaded = true; + } + } + catch (Exception ex) + { + SenparcTrace.SendCustomLog("Exception occurred executing register.UseXncfModule() from XncfRegisterManager.RegisterList", + $@"Module: {register.Name} +Exception: {ex.Message} +Details: {ex.ToString()}"); + } + + //Execute middleware + if (register is IXncfMiddleware middlewareRegister) + { + try + { + middlewareRegister.UseMiddleware(app); + } + catch + { + } + } + + //Execute threads + if (register is IXncfThread threadRegister) + { + try + { + XncfThreadBuilder xncfThreadBuilder = new XncfThreadBuilder(); + threadRegister.ThreadConfig(xncfThreadBuilder); + xncfThreadBuilder.Build(app, register); + } + catch (Exception ex) + { + SenparcTrace.BaseExceptionLog(ex); + } + + //Any one ThreadXncf has been loaded + Ncf.Core.Config.SiteConfig.NcfCoreState.AnyThreadXncfLoaded = true; + } + + + //MCP server + if (register.EnableMcpServer) + { + register.UseMcpServer(app, registerService); + } + } + + //All ThreadXncf have been loaded + Ncf.Core.Config.SiteConfig.NcfCoreState.AllThreadXncfLoaded = true; + //All UseXncfModule methods have completed + Ncf.Core.Config.SiteConfig.NcfCoreState.AllUseXncfModuleApplied = true; + + //Auto-run installation + + if (autoRunInstall) + { + Task.Factory.StartNew(async () => + { + using (var scope = app.ApplicationServices.CreateScope()) + { + foreach (var register in XncfRegisterManager.RegisterList) + { + await register.InstallOrUpdateAsync(scope.ServiceProvider, InstallOrUpdate.Install); + } + } + }).GetAwaiter().GetResult(); + } + + return app; + } + + /// + /// All entity types corresponding to [AutoConfigurationMapping] that have been applied + /// + public static List ApplyedAutoConfigurationMappingTypes { get; set; } = new List(); + private static List AddedApplyedAutoConfigurationMappingEntityTypes { get; set; } = new List(); + /// + /// Automatically add all objects marked with [XncfAutoConfigurationMapping] attribute in XNCF modules + /// + public static void ApplyAllAutoConfigurationMapping(ModelBuilder modelBuilder) + { + var entityTypeConfigurationMethod = typeof(ModelBuilder).GetMethods() + .FirstOrDefault(z => z.Name == "ApplyConfiguration" && z.ContainsGenericParameters && z.GetParameters().SingleOrDefault()?.ParameterType.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)); + + //Automatically retrieve all entity types under DbSet from all module database entities + + //TODO: Filter base class types + var xncfDatabaseDbContextList = MultipleDatabasePool.Instance.Values.SelectMany(z => z.Values); + //TODO: To further improve efficiency, deduplicate subclasses of the same DbContext + + foreach (var xncfDatabaseDbContextType in xncfDatabaseDbContextList) + { + var setKeyInfoList = EntitySetKeys.GetEntitySetInfo(xncfDatabaseDbContextType).Values; + foreach (var setKeyInfo in setKeyInfoList) + { + //Database entity type + var entityType = setKeyInfo.DbSetType; + if (AddedApplyedAutoConfigurationMappingEntityTypes.Contains(entityType)) + { + //Console.WriteLine($"\t [{xncfDatabaseDbContextType.Name}]ApplyAllAutoConfigurationMapping has duplicate setKeyInfo:{entityType.Name}, skipped"); + continue; + } + //Console.WriteLine($"\t [{xncfDatabaseDbContextType.Name}]ApplyAllAutoConfigurationMapping processing setKeyInfo:{entityType.Name}"); + + //Default empty ConfigurationMapping object generic type + var blankEntityTypeConfigurationType = typeof(BlankEntityTypeConfiguration<>).MakeGenericType(entityType); + //Create a new instance + var blankEntityTypeConfiguration = Activator.CreateInstance(blankEntityTypeConfigurationType); + //Append to end so user-defined code executes with higher priority + XncfAutoConfigurationMappingList.Add(blankEntityTypeConfiguration); + AddedApplyedAutoConfigurationMappingEntityTypes.Add(entityType); + } + } + + var logs = new StringBuilder(); + + try + { + + foreach (var autoConfigurationMapping in XncfAutoConfigurationMappingList) + { + if (autoConfigurationMapping == null) + { + continue; + } + + logs.AppendLine(autoConfigurationMapping.GetType().FullName); + + //Get configration entity type, e.g.: DbConfig_WeixinUserConfigurationMapping + Type mappintConfigType = autoConfigurationMapping.GetType(); + //Get IEntityTypeConfiguration interface + var interfaceType = mappintConfigType.GetInterfaces().FirstOrDefault(z => z.Name.StartsWith("IEntityTypeConfiguration")); + if (interfaceType == null) + { + Console.WriteLine("interfaceType is null (not IEntityTypeConfiguration)"); + continue; + } + //Entity type, e.g.: DbConfig + Type entityType = interfaceType.GenericTypeArguments[0]; + + //PS: Cannot filter here, otherwise only the first registered object in e.g. SystemServiceEntities_SqlServer / SystemServiceEntities_Mysql succeeds, later ones are ignored + //if (ApplyedAutoConfigurationMappingTypes.Contains(entityType)) + //{ + // //Skip if already added. This check is needed because original XncfAutoConfigurationMappingList data may overlap with auto-added DataSet objects + // //continue; + //} + + entityTypeConfigurationMethod.MakeGenericMethod(entityType) + .Invoke(modelBuilder, new object[1] + { + autoConfigurationMapping + }); + + ApplyedAutoConfigurationMappingTypes.Add(entityType); + + //entityTypeConfigurationMethod.Invoke(modelBuilder, new[] { autoConfigurationMapping }); + } + } + finally + { + SenparcTrace.SendCustomLog("ApplyAllAutoConfigurationMapping execution detected", logs.ToString()); + } + + //TODO: Add IQueryTypeConfiguration<> + + + } + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/XncfRegisterBase.cs b/src/Basic/Senparc.Ncf.XncfBase/XncfRegisterBase.cs index 603e89223..91a5947d5 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/XncfRegisterBase.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/XncfRegisterBase.cs @@ -1,465 +1,465 @@ -using AutoMapper; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Routing; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using ModelContextProtocol.Protocol; -using ModelContextProtocol.Server; -using Senparc.CO2NET.Extensions; -using Senparc.CO2NET.RegisterServices; -using Senparc.CO2NET.Trace; -using Senparc.Ncf.Core.Areas; -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.Core.Exceptions; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Database; -using Senparc.Ncf.XncfBase.Threads; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace Senparc.Ncf.XncfBase -{ - /// - /// 所有 XNCF 模块注册的基类 - /// - public abstract class XncfRegisterBase : IXncfRegister - { - /// - /// 是否忽略安装(但不影响执行注册代码),默认为 false - /// - public virtual bool IgnoreInstall { get; } - - public virtual bool EnableMcpServer => false; - - /// - /// 模块名称,要求全局唯一 - /// - public abstract string Name { get; } - /// - /// 编号,要求全局唯一 - /// - public abstract string Uid { get; } - /// - /// 版本号 - /// - public abstract string Version { get; } - /// - /// 菜单名称 - /// - public abstract string MenuName { get; } - /// - /// Icon图标 - /// - public abstract string Icon { get; } - /// - /// 说明 - /// - public abstract string Description { get; } - ///// - ///// 注册方法,注册的顺序决定了界面中排列的顺序 - ///// - //public abstract IList Functions { get; } - - /// - /// 添加 AutoMap 映射 - /// - public virtual ConcurrentBag> AutoMapMappingConfigs { get; set; } - /// - /// 获取当前模块的已注册线程信息 - /// - public IEnumerable> RegisteredThreadInfo => Register.ThreadCollection.Where(z => z.Value.Name.StartsWith(Uid)); - - - #region 执行 Migrate 更新数据 MigrateDatabaseAsync() - /* - /// - /// 执行 Migrate 更新数据 - /// - /// - /// - /// 是否需要对 dbContextType 类型进行检查 - /// - protected virtual async Task MigrateDatabaseAsync(IServiceProvider serviceProvider, Type dbContextType, bool checkdbContextType = true) - { - if (checkdbContextType && !dbContextType.IsSubclassOf(typeof(DbContext))) - { - throw new NcfDatabaseException("dbContextType 参数必须继承自 DbContext", null, dbContextType); - } - - var mySenparcEntities = serviceProvider.GetService(dbContextType) as DbContext; - await mySenparcEntities.Database.MigrateAsync().ConfigureAwait(false);//更新数据库 - } - - - /// - /// 执行 Migrate 更新数据 - /// - /// - /// - protected virtual async Task MigrateDatabaseAsync(IServiceProvider serviceProvider) - { - //当前Register对应的数据库上下文(DbContext)类型 - var dbContextType = MultipleDatabasePool.Instance.GetXncfDbContextType(this.GetType()); - await MigrateDatabaseAsync(serviceProvider, dbContextType, false); - } - - /// - /// 执行 Migrate 更新数据 - /// - /// - /// - /// - protected virtual async Task MigrateDatabaseAsync(IServiceProvider serviceProvider) - where TSenparcEntities : DbContext - { - await MigrateDatabaseAsync(serviceProvider, typeof(TSenparcEntities)); - } - */ - #endregion - - /// - /// 安装代码 - /// - public virtual Task InstallOrUpdateAsync(IServiceProvider serviceProvider, InstallOrUpdate installOrUpdate) - { - return Task.CompletedTask; - } - /// - /// 卸载代码 - /// - public virtual async Task UninstallAsync(IServiceProvider serviceProvider, Func unsinstallFunc) - { - await unsinstallFunc().ConfigureAwait(false); - } - - /// - /// 删除表(此方法请慎重使用!) - /// - /// - /// - /// 需要删除的表所对应的实体类型 - /// - protected virtual async Task DropTablesAsync(IServiceProvider serviceProvider, DbContext databaseDbContext, Type[] entityType) - { - SenparcTrace.SendCustomLog("开始删除应用表格", MenuName + ", " + Name); - var appliedMigrations = databaseDbContext.Database.GetAppliedMigrations(); - if (appliedMigrations.Count() > 0) - { - using (await databaseDbContext.Database.BeginTransactionAsync()) - { - //mySenparcEntities.Database.GetService<> - } - //var databaseCreator = mySenparcEntities.Database.GetService(); - - - var currentDatabaseConfiguration = DatabaseConfigurationFactory.Instance.Current; - foreach (var type in entityType) - { - try - { - - var tableName = databaseDbContext.Model.FindEntityType(type).GetTableName(); - - #region 适用于 SQL Server - //SenparcTrace.SendCustomLog("开始删除表格", $"[schma].[tableName]:[{schma}].[{tableName}]"); - ////mySenparcEntities.Colors.FromSqlRaw($"DELETE FROM [{key}]"); - - //string fullTableName = $"[{tableName}]"; - //if (!schma.IsNullOrEmpty()) - //{ - // fullTableName = $"[{schma}].{fullTableName}"; - //} - #endregion - - SenparcTrace.SendCustomLog("开始删除表格", $"tableName:{tableName}"); - - string dropTableSql = currentDatabaseConfiguration.GetDropTableSql(databaseDbContext, tableName); - if (!dropTableSql.IsNullOrEmpty()) - { - int keyExeCount = await databaseDbContext.Database.ExecuteSqlRawAsync(dropTableSql); - SenparcTrace.SendCustomLog("影响行数", keyExeCount + " 行"); - } - else - { - SenparcTrace.SendCustomLog("执行结果", $"{currentDatabaseConfiguration.GetType().Name} 内部已处理,无需单独执行 SQL"); - } - } - catch (Exception ex) - { - SenparcTrace.BaseExceptionLog(new NcfDatabaseException(ex.Message, currentDatabaseConfiguration.GetType(), databaseDbContext.GetType(), ex)); - } - - } - - //删除 Migration 记录,如果为系统表,则不删除 - if (this is IXncfDatabase databaseRegister && databaseRegister.DatabaseUniquePrefix != NcfDatabaseMigrationHelper.SYSTEM_UNIQUE_PREFIX) - { - var migrationHistoryTableName = NcfDatabaseMigrationHelper.GetDatabaseMigrationHistoryTableName(databaseRegister); - SenparcTrace.SendCustomLog("开始删除 DatabaseMigrationHistory 表格", $"[{migrationHistoryTableName}]"); - string sqlStr = currentDatabaseConfiguration.GetDropTableSql(databaseDbContext, migrationHistoryTableName); - if (!sqlStr.IsNullOrEmpty()) - { - int historyExeCount = await databaseDbContext.Database.ExecuteSqlRawAsync(sqlStr); - SenparcTrace.SendCustomLog("影响行数", historyExeCount + " 行"); - } - else - { - SenparcTrace.SendCustomLog("执行结果", $"{currentDatabaseConfiguration.GetType().Name} 内部已处理,无需单独执行 SQL"); - } - - } - } - } - - /// - /// 获取首页Url - /// 仅限实现了 IAreaRegister 接口之后的 Register,否则将返回 null - /// - /// - public virtual string GetAreaHomeUrl() - { - if (this is IAreaRegister) - { - var homeUrl = (this as IAreaRegister).HomeUrl; - return GetAreaUrl(homeUrl); - } - return null; - } - /// - /// 获取指定网页的Url - /// 仅限实现了 IAreaRegister 接口之后的 Register,否则将返回 null - /// - /// - /// - public virtual string GetAreaUrl(string path) - { - if (this is IAreaRegister) - { - if (path == null) - { - return "/"; - } - - path += path.Contains("?") ? "&" : "?"; - path += $"uid={Uid}"; - return path; - } - return null; - } - - /// - /// 添加模块 - /// - /// - /// - /// - public virtual IServiceCollection AddXncfModule(IServiceCollection services, IConfiguration configuration, IHostEnvironment env) - { - if (this is IXncfDatabase databaseRegister) - { - //遍历所有Register中的数据库进行注册 - if (XncfDatabaseDbContextPool.Instance.ContainsKey(this.GetType())) - { - var dbContextTypes = XncfDatabaseDbContextPool.Instance[this.GetType()]; - foreach (var dbContextType in dbContextTypes.Values) - { - var dbOptionBuilderType = dbContextType.GetConstructors() - .First().GetParameters().First().ParameterType; - - //定义 XncfSenparcEntities 实例生成 - Func implementationFactory = s => - { - DbContextOptionsBuilder dbOptionBuilder; - if (dbOptionBuilderType.GenericTypeArguments.Length > 0) - { - //带泛型 - ////准备创建 DbContextOptionsBuilder 实例,定义类型 - dbOptionBuilderType = typeof(DbContextOptionsBuilder<>); - //dbOptionBuilderType = typeof(RelationalDbContextOptionsBuilder<,>); - //获取泛型对象类型,如:DbContextOptionsBuilder - dbOptionBuilderType = dbOptionBuilderType.MakeGenericType(dbContextType); - - //创建 DbContextOptionsBuilder 实例 - dbOptionBuilder = Activator.CreateInstance(dbOptionBuilderType) as DbContextOptionsBuilder; - } - else - { - //不带泛型 - dbOptionBuilder = new DbContextOptionsBuilder(); - } - - //获取当前数据库配置 - var currentDatabaseConfiguration = DatabaseConfigurationFactory.Instance.Current; - - //使用数据库 - currentDatabaseConfiguration.UseDatabase(dbOptionBuilder, Ncf.Core.Config.SenparcDatabaseConnectionConfigs.GetClientConnectionString(), new XncfDatabaseData(databaseRegister, null /*默认使用当前 Register 程序集*/), (b, xncfDatabaseData) => - { - ////进行附加配置 - //this.DbContextOptionsAction?.Invoke(b); - - //执行 DatabaseConfiguration 中的 DbContextOptionsActionBase,进行基础配置; - currentDatabaseConfiguration.DbContextOptionsActionBase(b, xncfDatabaseData); - - //其他需要进行的配置,如对于 SQL Server: - //b.EnableRetryOnFailure( - // maxRetryCount: 5, - // maxRetryDelay: TimeSpan.FromSeconds(5), - // errorNumbersToAdd: new int[] { 2 }); - }); - - //创建 SenparcEntities 实例 - var xncfSenparcEntities = Activator.CreateInstance(dbContextType, dbOptionBuilder.Options); - return xncfSenparcEntities as DbContext; - }; - //添加 XncfSenparcEntities 依赖注入配置 - services.AddScoped(dbContextType, implementationFactory); - - //注册当前数据库的对象(必须) - EntitySetKeys.TryLoadSetInfo(dbContextType); - - //任意一个 IxncfDatabase 的Register已经执行完成 - if (!Ncf.Core.Config.SiteConfig.NcfCoreState.AynDatabaseXncfLoaded) - { - Ncf.Core.Config.SiteConfig.NcfCoreState.AynDatabaseXncfLoaded = true; - } - - } - } - else - { - var errMsg = $"{databaseRegister.GetType().FullName} 未注册任何数据库 DbContext!"; - SenparcTrace.BaseExceptionLog(new NcfDatabaseException(errMsg, null, null)); - Console.WriteLine(errMsg); - } - - //添加数据库相关注册过程 - databaseRegister.AddXncfDatabaseModule(services); - - if (!Ncf.Core.Config.SiteConfig.NcfCoreState.AnyAddXncfDatabaseModuleApplied) - { - //任意一个 AddXncfDatabaseModule 方法已经执行完成 - Ncf.Core.Config.SiteConfig.NcfCoreState.AnyAddXncfDatabaseModuleApplied = true; - } - } - - return services; - } - - /// - /// 在 startup.cs 的 Configure() 方法中执行配置 - /// - /// - /// - /// - public virtual IApplicationBuilder UseXncfModule(IApplicationBuilder app, IRegisterService registerService) - { - return app; - } - - public static object AddAutoMapMappingLock = new object(); - public virtual void AddAutoMapMapping(Action mapping) - { - if (AutoMapMappingConfigs == null) - { - lock (AddAutoMapMappingLock) - { - if (AutoMapMappingConfigs == null) - { - AutoMapMappingConfigs = new ConcurrentBag>(); - } - } - } - AutoMapMappingConfigs.Add(mapping); - } - - /// - /// 执行 AutoMapper 映射 - /// - public virtual void OnAutoMapMapping(IServiceCollection services, IConfiguration configuration) - { - //在 Register.StartEngine() 中调用,早于 AddXncfModule() 方法 - } - - #region MCP - - protected string GetMcpServerName() - { - return $"ncf-mcp-server-{this.Name.Replace(".", "-")}"; - } - - public virtual void AddMcpServer(IServiceCollection services, IXncfRegister xncfRegister) - { - var serverName = GetMcpServerName(); - - var mcpServerBuilder = services.AddMcpServer(opt => - { - opt.ServerInfo = new Implementation() - { - Name = serverName, - Version = this.Version, - }; - }) - .WithHttpTransport() - .WithToolsFromAssembly(xncfRegister.GetType().Assembly); - - XncfRegisterManager.McpServerInfoCollection[serverName] = new MCP.McpServerInfo() - { - ServerName = serverName, - XncfName = Name, - XncfUid = Uid - }; - } - - public virtual void UseMcpServer(IApplicationBuilder app, IRegisterService registerService) - { - if (app is IEndpointRouteBuilder endpoints) - { - var routePattern = $"mcp-{Name.Replace(".", "-").ToLower()}"; - endpoints.MapMcp(routePattern); - - //注册 MCP 路由信息 - var mcpServerInfo = XncfRegisterManager.McpServerInfoCollection.Values.LastOrDefault(z => z.XncfUid == Uid); - if (mcpServerInfo == null) - { - var serverName = GetMcpServerName(); - mcpServerInfo = new MCP.McpServerInfo() - { - ServerName = serverName, - XncfName = Name, - XncfUid = Uid - }; - XncfRegisterManager.McpServerInfoCollection[serverName] = mcpServerInfo; - } - - mcpServerInfo.McpRoute = routePattern; - - //_logger.LogInformation($"启用 MCP 服务({this.Name}):{routePattern}"); - - Console.WriteLine($"启用 MCP 服务({this.Name}):{routePattern}"); - - } - } - - #endregion - - - ///// - ///// 数据库 DbContext 选项配置(附加配置) - ///// 第1个参数:IRelationalDbContextOptionsBuilderInfrastructure - ///// 第2个参数:AssemblyName - ///// - //public Action DbContextOptionsAction => (builder, xncfDatabaseData) => - //{ - // if (xncfDatabaseData!=null && !xncfDatabaseData.AssemblyName.IsNullOrEmpty()) - // { - - // } - //}; - } -} +using AutoMapper; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using ModelContextProtocol.Protocol; +using ModelContextProtocol.Server; +using Senparc.CO2NET.Extensions; +using Senparc.CO2NET.RegisterServices; +using Senparc.CO2NET.Trace; +using Senparc.Ncf.Core.Areas; +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.Core.Exceptions; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Database; +using Senparc.Ncf.XncfBase.Threads; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Senparc.Ncf.XncfBase +{ + /// + /// Base class for all XNCF module registrations + /// + public abstract class XncfRegisterBase : IXncfRegister + { + /// + /// Whether to ignore installation (but does not affect registration code execution), default is false + /// + public virtual bool IgnoreInstall { get; } + + public virtual bool EnableMcpServer => false; + + /// + /// Module name, must be globally unique + /// + public abstract string Name { get; } + /// + /// ID, must be globally unique + /// + public abstract string Uid { get; } + /// + /// Version number + /// + public abstract string Version { get; } + /// + /// Menu name + /// + public abstract string MenuName { get; } + /// + /// Icon + /// + public abstract string Icon { get; } + /// + /// Description + /// + public abstract string Description { get; } + ///// + ///// Registration method; the order of registration determines the display order in the UI + ///// + //public abstract IList Functions { get; } + + /// + /// Add AutoMap mapping + /// + public virtual ConcurrentBag> AutoMapMappingConfigs { get; set; } + /// + /// Get the registered thread info for the current module + /// + public IEnumerable> RegisteredThreadInfo => Register.ThreadCollection.Where(z => z.Value.Name.StartsWith(Uid)); + + + #region Execute Migrate to update data MigrateDatabaseAsync() + /* + /// + /// Execute Migrate to update data + /// + /// + /// + /// Whether to check the dbContextType + /// + protected virtual async Task MigrateDatabaseAsync(IServiceProvider serviceProvider, Type dbContextType, bool checkdbContextType = true) + { + if (checkdbContextType && !dbContextType.IsSubclassOf(typeof(DbContext))) + { + throw new NcfDatabaseException("dbContextType parameter must inherit from DbContext", null, dbContextType); + } + + var mySenparcEntities = serviceProvider.GetService(dbContextType) as DbContext; + await mySenparcEntities.Database.MigrateAsync().ConfigureAwait(false);//Update database + } + + + /// + /// Execute Migrate to update data + /// + /// + /// + protected virtual async Task MigrateDatabaseAsync(IServiceProvider serviceProvider) + { + //The database context (DbContext) type corresponding to the current Register + var dbContextType = MultipleDatabasePool.Instance.GetXncfDbContextType(this.GetType()); + await MigrateDatabaseAsync(serviceProvider, dbContextType, false); + } + + /// + /// Execute Migrate to update data + /// + /// + /// + /// + protected virtual async Task MigrateDatabaseAsync(IServiceProvider serviceProvider) + where TSenparcEntities : DbContext + { + await MigrateDatabaseAsync(serviceProvider, typeof(TSenparcEntities)); + } + */ + #endregion + + /// + /// Installation code + /// + public virtual Task InstallOrUpdateAsync(IServiceProvider serviceProvider, InstallOrUpdate installOrUpdate) + { + return Task.CompletedTask; + } + /// + /// Uninstallation code + /// + public virtual async Task UninstallAsync(IServiceProvider serviceProvider, Func unsinstallFunc) + { + await unsinstallFunc().ConfigureAwait(false); + } + + /// + /// Delete table (use this method with caution!) + /// + /// + /// + /// The entity type corresponding to the table to be deleted + /// + protected virtual async Task DropTablesAsync(IServiceProvider serviceProvider, DbContext databaseDbContext, Type[] entityType) + { + SenparcTrace.SendCustomLog("Start deleting application tables", MenuName + ", " + Name); + var appliedMigrations = databaseDbContext.Database.GetAppliedMigrations(); + if (appliedMigrations.Count() > 0) + { + using (await databaseDbContext.Database.BeginTransactionAsync()) + { + //mySenparcEntities.Database.GetService<> + } + //var databaseCreator = mySenparcEntities.Database.GetService(); + + + var currentDatabaseConfiguration = DatabaseConfigurationFactory.Instance.Current; + foreach (var type in entityType) + { + try + { + + var tableName = databaseDbContext.Model.FindEntityType(type).GetTableName(); + + #region Applicable to SQL Server + //SenparcTrace.SendCustomLog("Start deleting table", $"[schma].[tableName]: [{schma}].[{tableName}]"); + ////mySenparcEntities.Colors.FromSqlRaw($"DELETE FROM [{key}]"); + + //string fullTableName = $"[{tableName}]"; + //if (!schma.IsNullOrEmpty()) + //{ + // fullTableName = $"[{schma}].{fullTableName}"; + //} + #endregion + + SenparcTrace.SendCustomLog("Start deleting table", $"tableName: {tableName}"); + + string dropTableSql = currentDatabaseConfiguration.GetDropTableSql(databaseDbContext, tableName); + if (!dropTableSql.IsNullOrEmpty()) + { + int keyExeCount = await databaseDbContext.Database.ExecuteSqlRawAsync(dropTableSql); + SenparcTrace.SendCustomLog("Affected rows", keyExeCount + " rows"); + } + else + { + SenparcTrace.SendCustomLog("Execution result", $"{currentDatabaseConfiguration.GetType().Name} handled internally, no need to execute SQL separately"); + } + } + catch (Exception ex) + { + SenparcTrace.BaseExceptionLog(new NcfDatabaseException(ex.Message, currentDatabaseConfiguration.GetType(), databaseDbContext.GetType(), ex)); + } + + } + + //Delete Migration records; system tables will not be deleted + if (this is IXncfDatabase databaseRegister && databaseRegister.DatabaseUniquePrefix != NcfDatabaseMigrationHelper.SYSTEM_UNIQUE_PREFIX) + { + var migrationHistoryTableName = NcfDatabaseMigrationHelper.GetDatabaseMigrationHistoryTableName(databaseRegister); + SenparcTrace.SendCustomLog("Start deleting DatabaseMigrationHistory table", $"[{migrationHistoryTableName}]"); + string sqlStr = currentDatabaseConfiguration.GetDropTableSql(databaseDbContext, migrationHistoryTableName); + if (!sqlStr.IsNullOrEmpty()) + { + int historyExeCount = await databaseDbContext.Database.ExecuteSqlRawAsync(sqlStr); + SenparcTrace.SendCustomLog("Affected rows", historyExeCount + " rows"); + } + else + { + SenparcTrace.SendCustomLog("Execution result", $"{currentDatabaseConfiguration.GetType().Name} handled internally, no need to execute SQL separately"); + } + + } + } + } + + /// + /// Get the homepage URL + /// Only available for Registers that implement IAreaRegister; otherwise returns null + /// + /// + public virtual string GetAreaHomeUrl() + { + if (this is IAreaRegister) + { + var homeUrl = (this as IAreaRegister).HomeUrl; + return GetAreaUrl(homeUrl); + } + return null; + } + /// + /// Get the URL of a specified page + /// Only available for Registers that implement IAreaRegister; otherwise returns null + /// + /// + /// + public virtual string GetAreaUrl(string path) + { + if (this is IAreaRegister) + { + if (path == null) + { + return "/"; + } + + path += path.Contains("?") ? "&" : "?"; + path += $"uid={Uid}"; + return path; + } + return null; + } + + /// + /// Add module + /// + /// + /// + /// + public virtual IServiceCollection AddXncfModule(IServiceCollection services, IConfiguration configuration, IHostEnvironment env) + { + if (this is IXncfDatabase databaseRegister) + { + //Iterate all databases in Register for registration + if (XncfDatabaseDbContextPool.Instance.ContainsKey(this.GetType())) + { + var dbContextTypes = XncfDatabaseDbContextPool.Instance[this.GetType()]; + foreach (var dbContextType in dbContextTypes.Values) + { + var dbOptionBuilderType = dbContextType.GetConstructors() + .First().GetParameters().First().ParameterType; + + //Define XncfSenparcEntities instance generation + Func implementationFactory = s => + { + DbContextOptionsBuilder dbOptionBuilder; + if (dbOptionBuilderType.GenericTypeArguments.Length > 0) + { + //With generics + ////Prepare to create DbContextOptionsBuilder instance, define type + dbOptionBuilderType = typeof(DbContextOptionsBuilder<>); + //dbOptionBuilderType = typeof(RelationalDbContextOptionsBuilder<,>); + //Get generic object type, e.g.: DbContextOptionsBuilder + dbOptionBuilderType = dbOptionBuilderType.MakeGenericType(dbContextType); + + //Create DbContextOptionsBuilder instance + dbOptionBuilder = Activator.CreateInstance(dbOptionBuilderType) as DbContextOptionsBuilder; + } + else + { + //Without generics + dbOptionBuilder = new DbContextOptionsBuilder(); + } + + //Get current database configuration + var currentDatabaseConfiguration = DatabaseConfigurationFactory.Instance.Current; + + //Use database + currentDatabaseConfiguration.UseDatabase(dbOptionBuilder, Ncf.Core.Config.SenparcDatabaseConnectionConfigs.GetClientConnectionString(), new XncfDatabaseData(databaseRegister, null /*use current Register assembly by default*/), (b, xncfDatabaseData) => + { + ////Perform additional configuration + //this.DbContextOptionsAction?.Invoke(b); + + //Execute DbContextOptionsActionBase in DatabaseConfiguration for basic configuration; + currentDatabaseConfiguration.DbContextOptionsActionBase(b, xncfDatabaseData); + + //Other configurations needed, e.g. for SQL Server: + //b.EnableRetryOnFailure( + // maxRetryCount: 5, + // maxRetryDelay: TimeSpan.FromSeconds(5), + // errorNumbersToAdd: new int[] { 2 }); + }); + + //Create SenparcEntities instance + var xncfSenparcEntities = Activator.CreateInstance(dbContextType, dbOptionBuilder.Options); + return xncfSenparcEntities as DbContext; + }; + //Add XncfSenparcEntities dependency injection configuration + services.AddScoped(dbContextType, implementationFactory); + + //Register current database object (required) + EntitySetKeys.TryLoadSetInfo(dbContextType); + + //Any IxncfDatabase Register has completed execution + if (!Ncf.Core.Config.SiteConfig.NcfCoreState.AynDatabaseXncfLoaded) + { + Ncf.Core.Config.SiteConfig.NcfCoreState.AynDatabaseXncfLoaded = true; + } + + } + } + else + { + var errMsg = $"{databaseRegister.GetType().FullName} has not registered any database DbContext!"; + SenparcTrace.BaseExceptionLog(new NcfDatabaseException(errMsg, null, null)); + Console.WriteLine(errMsg); + } + + //Add database-related registration process + databaseRegister.AddXncfDatabaseModule(services); + + if (!Ncf.Core.Config.SiteConfig.NcfCoreState.AnyAddXncfDatabaseModuleApplied) + { + //Any AddXncfDatabaseModule method has completed execution + Ncf.Core.Config.SiteConfig.NcfCoreState.AnyAddXncfDatabaseModuleApplied = true; + } + } + + return services; + } + + /// + /// Execute configuration in the Configure() method of startup.cs + /// + /// + /// + /// + public virtual IApplicationBuilder UseXncfModule(IApplicationBuilder app, IRegisterService registerService) + { + return app; + } + + public static object AddAutoMapMappingLock = new object(); + public virtual void AddAutoMapMapping(Action mapping) + { + if (AutoMapMappingConfigs == null) + { + lock (AddAutoMapMappingLock) + { + if (AutoMapMappingConfigs == null) + { + AutoMapMappingConfigs = new ConcurrentBag>(); + } + } + } + AutoMapMappingConfigs.Add(mapping); + } + + /// + /// Execute AutoMapper mapping + /// + public virtual void OnAutoMapMapping(IServiceCollection services, IConfiguration configuration) + { + //Called in Register.StartEngine(), before AddXncfModule() method + } + + #region MCP + + protected string GetMcpServerName() + { + return $"ncf-mcp-server-{this.Name.Replace(".", "-")}"; + } + + public virtual void AddMcpServer(IServiceCollection services, IXncfRegister xncfRegister) + { + var serverName = GetMcpServerName(); + + var mcpServerBuilder = services.AddMcpServer(opt => + { + opt.ServerInfo = new Implementation() + { + Name = serverName, + Version = this.Version, + }; + }) + .WithHttpTransport() + .WithToolsFromAssembly(xncfRegister.GetType().Assembly); + + XncfRegisterManager.McpServerInfoCollection[serverName] = new MCP.McpServerInfo() + { + ServerName = serverName, + XncfName = Name, + XncfUid = Uid + }; + } + + public virtual void UseMcpServer(IApplicationBuilder app, IRegisterService registerService) + { + if (app is IEndpointRouteBuilder endpoints) + { + var routePattern = $"mcp-{Name.Replace(".", "-").ToLower()}"; + endpoints.MapMcp(routePattern); + + //Register MCP route information + var mcpServerInfo = XncfRegisterManager.McpServerInfoCollection.Values.LastOrDefault(z => z.XncfUid == Uid); + if (mcpServerInfo == null) + { + var serverName = GetMcpServerName(); + mcpServerInfo = new MCP.McpServerInfo() + { + ServerName = serverName, + XncfName = Name, + XncfUid = Uid + }; + XncfRegisterManager.McpServerInfoCollection[serverName] = mcpServerInfo; + } + + mcpServerInfo.McpRoute = routePattern; + + //_logger.LogInformation($"Enable MCP service ({this.Name}): {routePattern}"); + + Console.WriteLine($"Enable MCP service ({this.Name}): {routePattern}"); + + } + } + + #endregion + + + ///// + ///// Database DbContext options configuration (additional configuration) + ///// Parameter 1: IRelationalDbContextOptionsBuilderInfrastructure + ///// Parameter 2: AssemblyName + ///// + //public Action DbContextOptionsAction => (builder, xncfDatabaseData) => + //{ + // if (xncfDatabaseData!=null && !xncfDatabaseData.AssemblyName.IsNullOrEmpty()) + // { + + // } + //}; + } +} From 30712fb9532547c479734ccf55ad261df84b5521 Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 23:11:36 -0700 Subject: [PATCH 17/21] translate: Chinese comments to English in Senparc.Ncf.Core batch 1 --- src/Basic/Senparc.Ncf.Core/Area/AreaData.cs | 1150 ++++++++-------- .../Config/SiteConfig.Core.cs | 68 +- .../Utility/CommonWebParts.cs | 78 +- .../Utility/XmlDataContext.cs | 972 ++++++------- .../Senparc.Ncf.Core/Validator/Validator.cs | 1216 ++++++++--------- 5 files changed, 1742 insertions(+), 1742 deletions(-) diff --git a/src/Basic/Senparc.Ncf.Core/Area/AreaData.cs b/src/Basic/Senparc.Ncf.Core/Area/AreaData.cs index ebe985bcf..040977b01 100644 --- a/src/Basic/Senparc.Ncf.Core/Area/AreaData.cs +++ b/src/Basic/Senparc.Ncf.Core/Area/AreaData.cs @@ -1,575 +1,575 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Xml.Linq; -using Senparc.Ncf.Core.Cache; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Core.Models.VD; -using Microsoft.AspNetCore.Http; - -namespace Senparc.Ncf.Core.Area -{ - /// - /// 地区信息 - /// - public class AreaData - { - private const string PROVINCES_XML_PATH = "~/App_Data/AreaData/Provinces.xml"; - private const string CITIES_XML_PATH = "~/App_Data/AreaData/Cities.xml"; - private const string DISTRICTS_XML_PATH = "~/App_Data/AreaData/Districts.xml"; - - private XElement GetXmlElement(string xmlApplicationPath) - { - return XElement.Load(SenparcHttpContext.MapPath(xmlApplicationPath)); - } - - #region 省 - - /// - /// 获取所有省份数据(从缓存中获取) - /// - /// - public List GetProvincesData() - { - //检查缓存 - AreaDataCache_Province cacheData = new AreaDataCache_Province(); - - if (cacheData.Data == null) - { - XElement doc = this.GetXmlElement(PROVINCES_XML_PATH); - List dataList = (from p in doc.Elements() - //orderby p.Attribute("ID").Value - select new AreaXML_Provinces(int.Parse(p.Attribute("ID").Value), - p.Attribute("ProvinceName").Value, - p.Attribute("DivisionsCode").Value, - p.Attribute("ShortName").Value) - ).ToList(); - - cacheData.Data = dataList; - } - return cacheData.Data; - } - - /// - /// 获取单个省份信息 - /// - /// - /// - public AreaXML_Provinces GetProvinceData(string provinceName) - { - return GetProvincesData().FirstOrDefault(z => z.ProvinceName == provinceName); - } - /// - /// 获取单个省份信息 - /// - /// - /// - public AreaXML_Provinces GetProvinceData(int provinceID) - { - return GetProvincesData().FirstOrDefault(z => z.ID == provinceID); - } - - /// - /// 获取指定CityName(城市)所属的省份信息 - /// - /// 城市名称 - /// - public AreaXML_Provinces GetProvinceDataByCityName(string cityName) - { - var cityInfo = this.GetCityData(cityName); - return GetProvincesData().FirstOrDefault(z => z.ID == cityInfo.PID); - } - - #endregion - - - #region 市 - /// - /// 获取所有市区数据(从缓存中获取) - /// - /// - public List GetCitiesData() - { - //检查缓存 - AreaDataCache_City cacheData = new AreaDataCache_City(); - if (cacheData.Data == null) - { - XElement doc = this.GetXmlElement(CITIES_XML_PATH); - List dataList = (from p in doc.Elements() - select new AreaXML_Cities(int.Parse(p.Attribute("ID").Value), - int.Parse(p.Attribute("PID").Value), - p.Attribute("CityName").Value, - p.Attribute("ZipCode").Value, - p.Attribute("CityCode").Value, - int.Parse(p.Attribute("MaxShopId").Value)) - ).ToList(); - - cacheData.Data = dataList; //更新缓存 - } - return cacheData.Data; - } - - /// - /// 获取指定PID城市数据 - /// - /// - /// - public List GetCitiesData(int pID) - { - List fullCitiesData = GetCitiesData(); - - return (from p in fullCitiesData - where (pID > 0 ? p.PID == pID : true) - select p).ToList(); - } - - /// - /// 获取制定省份下面的所有市区名称 - /// - /// - /// - public List GetCitiesData(string provinceName) - { - if (!string.IsNullOrEmpty(provinceName)) - { - List fullProvincesData = GetProvincesData(); - var provinceData = fullProvincesData.Where(p => p.ProvinceName == provinceName).FirstOrDefault();// (from p in doc.Elements() where select p.Attribute("PID").Value).First(); - - if (provinceData != null) - return GetCitiesData(provinceData.ID);//省份存在,查询其下城市 - else - return new List(); - } - else - { - return new List(); - } - } - - - private AreaXML_Cities GetCityData(string attributeName, string value) - { - List fullCitiesData = GetCitiesData(); - - AreaXML_Cities city = (from c in fullCitiesData - where c.GetType().GetProperty(attributeName).GetValue(c, null).ToString() == value//应用到反射 - select c).FirstOrDefault(); - return city; - } - - public AreaXML_Cities GetCityData(string cityName) - { - return this.GetCityData("CityName", cityName); - } - - public AreaXML_Cities GetCityData(int cityCode) - { - return this.GetCityData("CityCode", cityCode.ToString()); - } - - public AreaXML_Cities GetCityDataById(int id) - { - return this.GetCityData("ID", id.ToString()); - } - - //public AreaXML_Cities GetCityData(string procinceName,string cityName) - //{ - // return this.GetCitiesData(procinceName).FirstOrDefault(z => z.CityName == cityName); - //} - - - /// - /// 更新城市的区号、邮编 - /// - /// 城市名称 - /// 区号 - /// 邮编 - /// - public bool UpdateCityCodeAndZipCode(string cityName, int cityCode, int zipCode) - { - try - { - //TODO:最好能应用单件模式 - XElement doc = this.GetXmlElement(CITIES_XML_PATH);//获取XML文档 - string xmlFilePath = SenparcHttpContext.MapPath(CITIES_XML_PATH);//路径 - string backUpXmlFilePath = xmlFilePath + "." + DateTime.Now.ToString().Replace(":", "_") + ".更新区号邮编(" + cityName + ").bak";//备份文件路径 - - var cityData = (from c in doc.Elements() where c.Attribute("CityName").Value == cityName select c).Single(); - //备份当前数据 - //File.Copy(xmlFilePath, backUpXmlFilePath); - cityData.Save(backUpXmlFilePath);//备份单条信息 - - cityData.SetAttributeValue("CityCode", cityCode.ToString());//更新区号 - cityData.SetAttributeValue("ZipCode", zipCode.ToString());//更新邮编 - - //保存 - doc.Save(xmlFilePath); - - //更新缓存 - AreaDataCache_City areaData = new AreaDataCache_City(); - areaData.Data.Clear();//清空 - GetCitiesData();//使用调用,更新缓存 - - return true; - } - catch { return false; } - - - } - - /// - /// 察看未定义邮编的城市 - /// - /// - public List GetWrongCityCode() - { - var cities = this.GetCitiesData(); - List wrongCodeList = new List(); - int outint = 0; - foreach (var city in cities) - { - if (!int.TryParse(city.CityCode, out outint)) - { - wrongCodeList.Add(city); - } - } - - return wrongCodeList; - } - - #endregion - - - #region 区 - public List GetDistrictsData() - { - //检查缓存 - AreaDataCache_District cacheData = new AreaDataCache_District(); - if (cacheData.Data == null) - { - XElement doc = this.GetXmlElement(DISTRICTS_XML_PATH); - List dataList = (from p in doc.Elements() - select new AreaXML_Districts( - int.Parse(p.Attribute("ID").Value), - int.Parse(p.Attribute("CID").Value), - p.Attribute("DistrictName").Value - ) - ).ToList(); - - cacheData.Data = dataList; - } - return cacheData.Data; - } - - /// - /// 获取制定城市CID下面所有的区县数据 - /// - /// - /// - public List GetDistrictsData(int cID) - { - List fullDictrictsData = GetDistrictsData(); - - List dictData = (from d in fullDictrictsData - where (cID > 0 ? d.CID == cID : true) - //orderby p.Attribute("ID") - select d).ToList(); - - //TODO:添加“其他区”,可以在XML中添加,但又似乎没有必要,再议。 ——2008.5.25 By TNT2 - if (dictData.Count > 0) - dictData.Add(new AreaXML_Districts(-1, cID, "其他区")); - - return dictData; - } - - /// - /// 获取制定城市名称CityName下面所有的区县数据 - /// - /// - /// - public List GetDistrictsData(string cityName) - { - if (!string.IsNullOrEmpty(cityName)) - { - List fullCitiesData = GetCitiesData(); - AreaXML_Cities cityDta = fullCitiesData.Where(c => c.CityName == cityName).FirstOrDefault(); - - if (cityDta != null) - return GetDistrictsData(cityDta.ID); - else - return new List(); - } - else - { - return new List(); - } - } - - public AreaXML_Districts GetDistrictData(string cityName, string districtName) - { - return this.GetDistrictsData(cityName).FirstOrDefault(z => z.DistrictName == districtName); - } - - #endregion - - /// - /// 获取默认,或者用户所在地区的列表,不设置第一项默认项 - /// - /// - /// - /// - public Base_AreaXmlVD GetAreaDataByProvinceAndCity(string provinceName, string cityName, string districtName) - { - return GetAreaDataByProvinceAndCity(provinceName, cityName, districtName, null, null, null); - } - - - /// - /// 获取默认,或者用户所在地区的列表 - /// - /// - /// - /// Province第一项 - /// Cities第一项 - /// Districts第一项 - /// - public Base_AreaXmlVD GetAreaDataByProvinceAndCity(string provinceName, string cityName, string districtName, AreaXML_Provinces TopProvince, AreaXML_Cities TopCities, AreaXML_Districts TopDistricts) - { - var vd = new Base_AreaXmlVD() - { - Provinces = this.GetProvincesData(), - Cities = this.GetCitiesData(provinceName), - Districts = this.GetDistrictsData(cityName), - - CurrentProvince = provinceName ?? "", - CurrentCity = cityName ?? "", - CurrentDistrict = districtName ?? "" - }; - - //加入第一行提示 - if (string.IsNullOrEmpty(provinceName)) - { - vd.Cities = this.GetCitiesData("北京市"); - vd.Districts = this.GetDistrictsData("北京市"); - } - - //新增第一项 - if (TopProvince != null) - vd.Provinces.Insert(0, TopProvince); - - if (TopCities != null) - vd.Cities.Insert(0, TopCities); - - if (TopDistricts != null) - vd.Districts.Insert(0, TopDistricts); - - - return vd; - } - - - /// - /// 增加区号属性 - /// - //public void AddCityCodeAttrbuite() - //{ - // var doc = this.GetXmlElement(CITIES_XML_PATH); - - // var cityList = doc.Elements(); - - // GLJKDataContext ctx=new GLJKDataContext(); - // var codeList = ctx.AreaCityCodes.ToList(); - // foreach (var item in cityList) - // { - // ////增加属性 - // //item.SetAttributeValue("CityCode", ""); - // //item.SetAttributeValue("MaxShopId", "100000"); - - // //城市名称配对 - // //var citys = codeList.Where(s => item.CityName.Contains(s.City.Replace("自治区", "").Replace("县", "").Replace("市", ""))).ToList(); - - - // var citys = codeList.Where(s => item.Attribute("CityName").Value.Contains(s.City.Replace("自治区",""))); - - // if (citys.Count() != 0) - // { - // var city = citys.Last(); - - // item.Attribute("CityCode").Value = city.CityCode; - // } - // else - // { - // //没有找到区号的城市 - // System.Web.HttpContext.Current.Response.Write( - // "省:"+this.GetProvincesData().Single(p=>p.ID==int.Parse(item.Attribute("PID").Value)).ProvinceName+" ,市:"+ item.Attribute("CityName").Value+"
"); - // } - // } - // doc.Save(System.Web.HttpContext.Current.Server.MapPath(CITIES_XML_PATH+".bak")); - //} - - //public string GetCityCode(string provinceName, string cityName) - //{ - // var pro=GetAreaDataAll - //} - - - /// - /// 获取城市区号 - /// - /// - /// 是否格式化(前面加0) - /// - public string GetCityCode(/*string provinceName,*/ string cityName, bool format) - { - string cityCode = this.GetCityData(cityName).CityCode; - return format ? "0" + cityCode : cityCode;//需要格式化,则在区号前加0 - } - - //#region ShopId相关 - - ///// - ///// 获取一个可用的ShopId - ///// - ///// - ///// - //public int GetUseableMaxShopId(string cityName) - //{ - // //获得Shop的XML设置 - // var shopConfig = Config.XmlConfig.GetShopConfig(); - // //获得当前ShopId - // var cityData = this.GetCityData(cityName); - // int currentMaxShopId = cityData.MaxShopId;//当前XML的MaxShopId - // int cityCode = int.Parse(cityData.CityCode);//区号 - // int maxShopId = currentMaxShopId; - - // //比较可用的ShopId - // bool findUseableShopId = false; - // while (!findUseableShopId) - // { - // if (shopConfig.ForbidShopIds.Contains(maxShopId.ToString())) - // { - // //存在于禁用ID列表中,退出,尝试下一个Id - // maxShopId++; - // continue; - // } - // else - // { - // //查找是否有相同ID存在 - // GLJKDataContext ctx = new GLJKDataContext(); - // if (ctx.Shop_GetShopByCityCodeAndShopId(cityCode, maxShopId) == null) - // { - // //ID可用 - // findUseableShopId = true; - // } - // else - // { - // maxShopId++; - // continue; - // } - // } - // } - - // return maxShopId; - //} - ///// - ///// 更新XML中对应城市的MaxShopId - ///// - ///// 城市名称 - ///// 当前使用掉的MaxShopId - ///// - //public bool UpdateMaxShopId(string cityName, int newMaxShopId) - //{ - // try - // { - // //TODO:最好能应用单件模式 - // XElement doc = this.GetXmlElement(CITIES_XML_PATH);//获取XML文档 - // string xmlFilePath = System.Web.HttpContext.Current.Server.MapPath(CITIES_XML_PATH);//路径 - // string backUpXmlFilePath = xmlFilePath + "." + DateTime.Now.ToString().Replace(":", "_") + ".更新MaxShopId(" + cityName + ").bak";//备份文件路径 - - // var cityData = (from c in doc.Elements() where c.Attribute("CityName").Value == cityName select c).Single(); - // //int shopId = int.Parse(cityData.Attribute("MaxShopId").Value); - - // //备份当前数据 - // //File.Copy(xmlFilePath, backUpXmlFilePath); - // cityData.Save(backUpXmlFilePath);//备份单条数据 - - // cityData.SetAttributeValue("MaxShopId", newMaxShopId + 1); - // //保存 - // doc.Save(xmlFilePath); - - // //更新缓存 - // CacheData.AreaData_City areaData = new AreaDataCache_City(); - // areaData.Clear();//清空 - // GetCitiesData();//使用调用,更新缓存 - - // return true; - // } - // catch { return false; } - //} - - - - - //#endregion - - - /// - /// 通过IP数据的Country(省、市、区名称),分割省、市、区信息以便查询 - /// - /// - /// - /// 为null时连同不获取, - /// districtName不获取 - public static void GetProvinceCityNameFromIPCountry(string ipCountry, ref string provinceName, ref string cityName, ref string districtName) - { - int areaStartIndex = 0; - //省 - if (provinceName == null) - return; - else - provinceName = GetProvinceAreaNameFromIPCountry(ipCountry, new string[] { "省", "自治区" }, ref areaStartIndex); - - //市 - if (cityName == null) - { - return; - } - else - { - if (ipCountry.IndexOf("市") != -1 && areaStartIndex == 0) - provinceName = cityName; //直辖市 - - cityName = GetProvinceAreaNameFromIPCountry(ipCountry, new string[] { "市", "自治州" }, ref areaStartIndex); - } - - //区 - if (districtName == null) - return; - else - districtName = GetProvinceAreaNameFromIPCountry(ipCountry, new string[] { "区", "县", "市", "自治县" }, ref areaStartIndex); - - } - /// - /// 通过IP数据的Country(省、市、区名称) - /// - /// 从IP数据库中得到的ipCountry字符串 - /// 地区级别称谓,如"省""市""区" - /// 开始搜索字符串位置的游标 - /// - private static string GetProvinceAreaNameFromIPCountry(string ipCountry, string[] areaNameList, ref int areaStartIndex) - { - string areaNameResult = string.Empty; - foreach (var area in areaNameList) - { - if (ipCountry.IndexOf(area) != -1) - { - areaNameResult = ipCountry.Substring(areaStartIndex, ipCountry.IndexOf(area) - areaStartIndex + area.Length);//获得地区名称 - areaStartIndex += areaNameResult.Length;//字符串游标,搜索“区”的时候可忽略 - break; - } - } - return areaNameResult; - } - - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using Senparc.Ncf.Core.Cache; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Core.Models.VD; +using Microsoft.AspNetCore.Http; + +namespace Senparc.Ncf.Core.Area +{ + /// + /// Area information + /// + public class AreaData + { + private const string PROVINCES_XML_PATH = "~/App_Data/AreaData/Provinces.xml"; + private const string CITIES_XML_PATH = "~/App_Data/AreaData/Cities.xml"; + private const string DISTRICTS_XML_PATH = "~/App_Data/AreaData/Districts.xml"; + + private XElement GetXmlElement(string xmlApplicationPath) + { + return XElement.Load(SenparcHttpContext.MapPath(xmlApplicationPath)); + } + + #region Province + + /// + /// Get all province data (from cache) + /// + /// + public List GetProvincesData() + { + // Check cache + AreaDataCache_Province cacheData = new AreaDataCache_Province(); + + if (cacheData.Data == null) + { + XElement doc = this.GetXmlElement(PROVINCES_XML_PATH); + List dataList = (from p in doc.Elements() + //orderby p.Attribute("ID").Value + select new AreaXML_Provinces(int.Parse(p.Attribute("ID").Value), + p.Attribute("ProvinceName").Value, + p.Attribute("DivisionsCode").Value, + p.Attribute("ShortName").Value) + ).ToList(); + + cacheData.Data = dataList; + } + return cacheData.Data; + } + + /// + /// Get a single province record + /// + /// + /// + public AreaXML_Provinces GetProvinceData(string provinceName) + { + return GetProvincesData().FirstOrDefault(z => z.ProvinceName == provinceName); + } + /// + /// Get a single province record + /// + /// + /// + public AreaXML_Provinces GetProvinceData(int provinceID) + { + return GetProvincesData().FirstOrDefault(z => z.ID == provinceID); + } + + /// + /// Get province information for the specified city name + /// + /// City name + /// + public AreaXML_Provinces GetProvinceDataByCityName(string cityName) + { + var cityInfo = this.GetCityData(cityName); + return GetProvincesData().FirstOrDefault(z => z.ID == cityInfo.PID); + } + + #endregion + + + #region City + /// + /// Get all city records (from cache) + /// + /// + public List GetCitiesData() + { + // Check cache + AreaDataCache_City cacheData = new AreaDataCache_City(); + if (cacheData.Data == null) + { + XElement doc = this.GetXmlElement(CITIES_XML_PATH); + List dataList = (from p in doc.Elements() + select new AreaXML_Cities(int.Parse(p.Attribute("ID").Value), + int.Parse(p.Attribute("PID").Value), + p.Attribute("CityName").Value, + p.Attribute("ZipCode").Value, + p.Attribute("CityCode").Value, + int.Parse(p.Attribute("MaxShopId").Value)) + ).ToList(); + + cacheData.Data = dataList; // Update cache + } + return cacheData.Data; + } + + /// + /// Get city data by specified PID + /// + /// + /// + public List GetCitiesData(int pID) + { + List fullCitiesData = GetCitiesData(); + + return (from p in fullCitiesData + where (pID > 0 ? p.PID == pID : true) + select p).ToList(); + } + + /// + /// Get all city names under the specified province + /// + /// + /// + public List GetCitiesData(string provinceName) + { + if (!string.IsNullOrEmpty(provinceName)) + { + List fullProvincesData = GetProvincesData(); + var provinceData = fullProvincesData.Where(p => p.ProvinceName == provinceName).FirstOrDefault();// (from p in doc.Elements() where select p.Attribute("PID").Value).First(); + + if (provinceData != null) + return GetCitiesData(provinceData.ID);//Province exists, query its cities + else + return new List(); + } + else + { + return new List(); + } + } + + + private AreaXML_Cities GetCityData(string attributeName, string value) + { + List fullCitiesData = GetCitiesData(); + + AreaXML_Cities city = (from c in fullCitiesData + where c.GetType().GetProperty(attributeName).GetValue(c, null).ToString() == value//Applied through reflection + select c).FirstOrDefault(); + return city; + } + + public AreaXML_Cities GetCityData(string cityName) + { + return this.GetCityData("CityName", cityName); + } + + public AreaXML_Cities GetCityData(int cityCode) + { + return this.GetCityData("CityCode", cityCode.ToString()); + } + + public AreaXML_Cities GetCityDataById(int id) + { + return this.GetCityData("ID", id.ToString()); + } + + //public AreaXML_Cities GetCityData(string procinceName,string cityName) + //{ + // return this.GetCitiesData(procinceName).FirstOrDefault(z => z.CityName == cityName); + //} + + + /// + /// Update city area code and zip code + /// + /// City name + /// Area code + /// Zip code + /// + public bool UpdateCityCodeAndZipCode(string cityName, int cityCode, int zipCode) + { + try + { + //TODO: ideally use singleton pattern + XElement doc = this.GetXmlElement(CITIES_XML_PATH);//Get XML document + string xmlFilePath = SenparcHttpContext.MapPath(CITIES_XML_PATH);//Path + string backUpXmlFilePath = xmlFilePath + "." + DateTime.Now.ToString().Replace(":", "_") + ".更新区号邮编(" + cityName + ").bak";//Backup file path + + var cityData = (from c in doc.Elements() where c.Attribute("CityName").Value == cityName select c).Single(); + //Backup current data + //File.Copy(xmlFilePath, backUpXmlFilePath); + cityData.Save(backUpXmlFilePath);//Backup single record + + cityData.SetAttributeValue("CityCode", cityCode.ToString());//Update area code + cityData.SetAttributeValue("ZipCode", zipCode.ToString());//Update zip code + + //Save + doc.Save(xmlFilePath); + + // Update cache + AreaDataCache_City areaData = new AreaDataCache_City(); + areaData.Data.Clear();//Clear + GetCitiesData();//Invoke to refresh cache + + return true; + } + catch { return false; } + + + } + + /// + /// Find cities with undefined zip code + /// + /// + public List GetWrongCityCode() + { + var cities = this.GetCitiesData(); + List wrongCodeList = new List(); + int outint = 0; + foreach (var city in cities) + { + if (!int.TryParse(city.CityCode, out outint)) + { + wrongCodeList.Add(city); + } + } + + return wrongCodeList; + } + + #endregion + + + #region District + public List GetDistrictsData() + { + // Check cache + AreaDataCache_District cacheData = new AreaDataCache_District(); + if (cacheData.Data == null) + { + XElement doc = this.GetXmlElement(DISTRICTS_XML_PATH); + List dataList = (from p in doc.Elements() + select new AreaXML_Districts( + int.Parse(p.Attribute("ID").Value), + int.Parse(p.Attribute("CID").Value), + p.Attribute("DistrictName").Value + ) + ).ToList(); + + cacheData.Data = dataList; + } + return cacheData.Data; + } + + /// + /// Get all district records under specified city CID + /// + /// + /// + public List GetDistrictsData(int cID) + { + List fullDictrictsData = GetDistrictsData(); + + List dictData = (from d in fullDictrictsData + where (cID > 0 ? d.CID == cID : true) + //orderby p.Attribute("ID") + select d).ToList(); + + //TODO: add "Other District". Could be added in XML, but may not be necessary. Revisit. --2008.5.25 By TNT2 + if (dictData.Count > 0) + dictData.Add(new AreaXML_Districts(-1, cID, "其他区")); + + return dictData; + } + + /// + /// Get all district records under specified city name + /// + /// + /// + public List GetDistrictsData(string cityName) + { + if (!string.IsNullOrEmpty(cityName)) + { + List fullCitiesData = GetCitiesData(); + AreaXML_Cities cityDta = fullCitiesData.Where(c => c.CityName == cityName).FirstOrDefault(); + + if (cityDta != null) + return GetDistrictsData(cityDta.ID); + else + return new List(); + } + else + { + return new List(); + } + } + + public AreaXML_Districts GetDistrictData(string cityName, string districtName) + { + return this.GetDistrictsData(cityName).FirstOrDefault(z => z.DistrictName == districtName); + } + + #endregion + + /// + /// Get default or user-area list without setting a default first item + /// + /// + /// + /// + public Base_AreaXmlVD GetAreaDataByProvinceAndCity(string provinceName, string cityName, string districtName) + { + return GetAreaDataByProvinceAndCity(provinceName, cityName, districtName, null, null, null); + } + + + /// + /// Get default or user-area list + /// + /// + /// + /// First province item + /// First city item + /// First district item + /// + public Base_AreaXmlVD GetAreaDataByProvinceAndCity(string provinceName, string cityName, string districtName, AreaXML_Provinces TopProvince, AreaXML_Cities TopCities, AreaXML_Districts TopDistricts) + { + var vd = new Base_AreaXmlVD() + { + Provinces = this.GetProvincesData(), + Cities = this.GetCitiesData(provinceName), + Districts = this.GetDistrictsData(cityName), + + CurrentProvince = provinceName ?? "", + CurrentCity = cityName ?? "", + CurrentDistrict = districtName ?? "" + }; + + //Add first-row prompt + if (string.IsNullOrEmpty(provinceName)) + { + vd.Cities = this.GetCitiesData("北京市"); + vd.Districts = this.GetDistrictsData("北京市"); + } + + //Insert first item + if (TopProvince != null) + vd.Provinces.Insert(0, TopProvince); + + if (TopCities != null) + vd.Cities.Insert(0, TopCities); + + if (TopDistricts != null) + vd.Districts.Insert(0, TopDistricts); + + + return vd; + } + + + /// + /// Add area code attribute + /// + //public void AddCityCodeAttrbuite() + //{ + // var doc = this.GetXmlElement(CITIES_XML_PATH); + + // var cityList = doc.Elements(); + + // GLJKDataContext ctx=new GLJKDataContext(); + // var codeList = ctx.AreaCityCodes.ToList(); + // foreach (var item in cityList) + // { + // ////Add attributes + // //item.SetAttributeValue("CityCode", ""); + // //item.SetAttributeValue("MaxShopId", "100000"); + + // //Match city name + // //var citys = codeList.Where(s => item.CityName.Contains(s.City.Replace("自治区", "").Replace("县", "").Replace("市", ""))).ToList(); + + + // var citys = codeList.Where(s => item.Attribute("CityName").Value.Contains(s.City.Replace("自治区",""))); + + // if (citys.Count() != 0) + // { + // var city = citys.Last(); + + // item.Attribute("CityCode").Value = city.CityCode; + // } + // else + // { + // //City with no area code found + // System.Web.HttpContext.Current.Response.Write( + // "省:"+this.GetProvincesData().Single(p=>p.ID==int.Parse(item.Attribute("PID").Value)).ProvinceName+" ,市:"+ item.Attribute("CityName").Value+"
"); + // } + // } + // doc.Save(System.Web.HttpContext.Current.Server.MapPath(CITIES_XML_PATH+".bak")); + //} + + //public string GetCityCode(string provinceName, string cityName) + //{ + // var pro=GetAreaDataAll + //} + + + /// + /// Get city area code + /// + /// + /// Whether to format (prefix with 0) + /// + public string GetCityCode(/*string provinceName,*/ string cityName, bool format) + { + string cityCode = this.GetCityData(cityName).CityCode; + return format ? "0" + cityCode : cityCode;//If formatting is required, prefix area code with 0 + } + + //#region ShopId related + + ///// + ///// Get an available ShopId + ///// + ///// + ///// + //public int GetUseableMaxShopId(string cityName) + //{ + // //Get Shop XML settings + // var shopConfig = Config.XmlConfig.GetShopConfig(); + // //Get current ShopId + // var cityData = this.GetCityData(cityName); + // int currentMaxShopId = cityData.MaxShopId;//Current MaxShopId in XML + // int cityCode = int.Parse(cityData.CityCode);//Area code + // int maxShopId = currentMaxShopId; + + // //Find available ShopId + // bool findUseableShopId = false; + // while (!findUseableShopId) + // { + // if (shopConfig.ForbidShopIds.Contains(maxShopId.ToString())) + // { + // //Exists in forbidden ID list, skip and try next Id + // maxShopId++; + // continue; + // } + // else + // { + // //Check whether same ID exists + // GLJKDataContext ctx = new GLJKDataContext(); + // if (ctx.Shop_GetShopByCityCodeAndShopId(cityCode, maxShopId) == null) + // { + // //ID is available + // findUseableShopId = true; + // } + // else + // { + // maxShopId++; + // continue; + // } + // } + // } + + // return maxShopId; + //} + ///// + ///// Update MaxShopId for corresponding city in XML + ///// + ///// City name + ///// Current consumed MaxShopId + ///// + //public bool UpdateMaxShopId(string cityName, int newMaxShopId) + //{ + // try + // { + // //TODO: ideally use singleton pattern + // XElement doc = this.GetXmlElement(CITIES_XML_PATH);//Get XML document + // string xmlFilePath = System.Web.HttpContext.Current.Server.MapPath(CITIES_XML_PATH);//Path + // string backUpXmlFilePath = xmlFilePath + "." + DateTime.Now.ToString().Replace(":", "_") + ".更新MaxShopId(" + cityName + ").bak";//Backup file path + + // var cityData = (from c in doc.Elements() where c.Attribute("CityName").Value == cityName select c).Single(); + // //int shopId = int.Parse(cityData.Attribute("MaxShopId").Value); + + // //Backup current data + // //File.Copy(xmlFilePath, backUpXmlFilePath); + // cityData.Save(backUpXmlFilePath);//Backup single record + + // cityData.SetAttributeValue("MaxShopId", newMaxShopId + 1); + // //Save + // doc.Save(xmlFilePath); + + // // Update cache + // CacheData.AreaData_City areaData = new AreaDataCache_City(); + // areaData.Clear();//Clear + // GetCitiesData();//Invoke to refresh cache + + // return true; + // } + // catch { return false; } + //} + + + + + //#endregion + + + /// + /// Split province/city/district information from IP Country field for lookup + /// + /// + /// + /// If null, do not parse city + /// If null, do not parse district + public static void GetProvinceCityNameFromIPCountry(string ipCountry, ref string provinceName, ref string cityName, ref string districtName) + { + int areaStartIndex = 0; + //Province + if (provinceName == null) + return; + else + provinceName = GetProvinceAreaNameFromIPCountry(ipCountry, new string[] { "省", "自治区" }, ref areaStartIndex); + + //City + if (cityName == null) + { + return; + } + else + { + if (ipCountry.IndexOf("市") != -1 && areaStartIndex == 0) + provinceName = cityName; //Municipality directly under central government + + cityName = GetProvinceAreaNameFromIPCountry(ipCountry, new string[] { "市", "自治州" }, ref areaStartIndex); + } + + //District + if (districtName == null) + return; + else + districtName = GetProvinceAreaNameFromIPCountry(ipCountry, new string[] { "区", "县", "市", "自治县" }, ref areaStartIndex); + + } + /// + /// Parse area name from IP Country field + /// + /// ipCountry string from IP database + /// Area level tokens, such as "省" "市" "区" + /// Cursor position to start searching + /// + private static string GetProvinceAreaNameFromIPCountry(string ipCountry, string[] areaNameList, ref int areaStartIndex) + { + string areaNameResult = string.Empty; + foreach (var area in areaNameList) + { + if (ipCountry.IndexOf(area) != -1) + { + areaNameResult = ipCountry.Substring(areaStartIndex, ipCountry.IndexOf(area) - areaStartIndex + area.Length);//Get area name + areaStartIndex += areaNameResult.Length;//String cursor; can be ignored when searching district + break; + } + } + return areaNameResult; + } + + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Config/SiteConfig.Core.cs b/src/Basic/Senparc.Ncf.Core/Config/SiteConfig.Core.cs index 47e00ebb9..16af446e5 100644 --- a/src/Basic/Senparc.Ncf.Core/Config/SiteConfig.Core.cs +++ b/src/Basic/Senparc.Ncf.Core/Config/SiteConfig.Core.cs @@ -16,14 +16,14 @@ namespace Senparc.Ncf.Core.Config public static partial class SiteConfig { /// - /// 网站物理路径 + /// Website physical path /// public static string ApplicationPath { get; set; } public static string WebRootPath { get; set; } /// - /// 设置 - /// 需要在系统启动时注入 + /// Settings + /// Must be injected when system starts /// public static SenparcCoreSetting SenparcCoreSetting { get; set; } = new SenparcCoreSetting(); //{ @@ -36,23 +36,23 @@ public static partial class SiteConfig //} /// - /// 是否处于Debug状态(人为手动定义) + /// Whether in Debug mode (manually defined) /// public static bool IsDebug => SenparcCoreSetting.IsDebug; /// - /// 是否是测试站 + /// Whether this is a test site /// public static bool IsTestSite => SenparcCoreSetting.IsTestSite; public static Dictionary _memcachedAddressesDic; public const string WEIXIN_FILTER_IGNORE = "senparcnofilter1"; - public const string WEIXIN_OFFICIAL_AVATAR_KEY = "WXoDOkC8A"; //将取前8位 - public const string WEIXIN_OFFICIAL_QR_CODE_KEY = "WX631IC8A"; //将取前8位 - public const string WEIXIN_APP_TOKEN_KEY = "WEIXIN_APP_TOKEN_KEY_FOR_NCF"; //微信APP Token加密 - public const long MIN_WEIXINUSERINFO_ID = 10000000000000; //最小自定义WeixinUserInfo的Id - public const decimal PROJECTDMANDDEPOSIT = 1000; //任务默认押金 - public const string CERT_P12_ADDRESS = @"E:\";//微信支付数字证书存放地址 + public const string WEIXIN_OFFICIAL_AVATAR_KEY = "WXoDOkC8A"; //Take first 8 chars + public const string WEIXIN_OFFICIAL_QR_CODE_KEY = "WX631IC8A"; //Take first 8 chars + public const string WEIXIN_APP_TOKEN_KEY = "WEIXIN_APP_TOKEN_KEY_FOR_NCF"; //WeChat APP Token encryption + public const long MIN_WEIXINUSERINFO_ID = 10000000000000; //Minimum custom WeixinUserInfo Id + public const decimal PROJECTDMANDDEPOSIT = 1000; //Default project deposit + public const string CERT_P12_ADDRESS = @"E:\";//Storage path for WeChat Pay certificate /* UID 建议格式: NCF公司ID-项目类型-保留字段(可随机)-内部类别1-内部类别2*/ public const string SYSTEM_XNCF_TANENT_UID = "00000000-0000-0000-0000-000000000001"; @@ -67,15 +67,15 @@ public static partial class SiteConfig public const string SYSTEM_XNCF_MODULE_AREAS_ADMIN_UID = "00000000-0000-0001-0001-000000000001"; public const string SYSTEM_XNCF_MODULE_ACCOUNTS_UID = "00000000-0000-0001-0001-000000000002"; - public const string TENANT_DEFAULT_NAME = "DEFAULT";//多租户 TenantName 默认值(不作为任何一个特殊租户) + public const string TENANT_DEFAULT_NAME = "DEFAULT";//Default multi-tenant TenantName (not treated as any special tenant) /// - /// 开发者收入比例 + /// Developer income ratio /// public static readonly long DeveloperIncomRate = (long)0.5; /// - /// 缓存类型 + /// Cache type /// public static CacheType CacheType { @@ -90,38 +90,38 @@ public static CacheType CacheType public const string VERSION = "0.0.1"; public static string SenparcConfigDirctory = "~/App_Data/Database/"; public const string AntiForgeryTokenSalt = "SOUIDEA__SENPARC"; - public const string WEIXIN_USER_AVATAR_KEY = "SENPARC_"; //将取前8位 + public const string WEIXIN_USER_AVATAR_KEY = "SENPARC_"; //Take first 8 chars public const string DomainName = "https://ncf.senparc.com"; public const string DefaultTemplate = "default"; - public const int SMSSENDWAITSECONDS = 60; //手机验证持续时间 - public const string DEFAULT_AVATAR = "/Content/Images/userinfonopic.png"; //默认头像 + public const int SMSSENDWAITSECONDS = 60; //Phone verification duration + public const string DEFAULT_AVATAR = "/Content/Images/userinfonopic.png"; //Default avatar public const string DEFAULT_MEMCACHED_ADDRESS_1 = "192.168.184.91"; public const int DEFAULT_MEMCACHED_PORT_1 = 11210; /// - /// WBS格式 + /// WBS format /// public static readonly string WBSFormat = "000"; /// - /// 用户在线不活动过期时间(分钟) + /// User online inactivity timeout (minutes) /// public static readonly int UserOnlineTimeoutMinutes = 10; /// - /// 最多免验证码尝试登录次数 + /// Max login attempts without verification code /// public static readonly int TryLoginTimes = 1; /// - /// 最多免验证码尝试登录次数 + /// Max user login attempts without verification code /// public static readonly int TryUserLoginTimes = 3; /// - /// 最大数据库备份文件个数 + /// Maximum number of database backup files /// public static readonly int MaxBackupDatabaseCount = 200; /// - /// 是否是单元测试 + /// Whether running unit test /// public static readonly bool IsUnitTest = false; ///// @@ -132,7 +132,7 @@ public static CacheType CacheType private static bool _isInstalling = false; /// - /// 是否正在进行安装,如果是,则不抛出监测安装的异常 + /// Whether installation is in progress; if true, installation-check exception is not thrown /// public static bool IsInstalling { @@ -147,7 +147,7 @@ public static bool IsInstalling } /// - /// 检查安装完成状态文件是否存在 + /// Check whether installation-finished status file exists /// /// public static bool CheckInstallFinishedFileExisted() @@ -157,7 +157,7 @@ public static bool CheckInstallFinishedFileExisted() } /// - /// 手动设置安装状态-结束 + /// Manually set installation status to finished /// public static async void SetInstallFinished() { @@ -167,33 +167,33 @@ public static async void SetInstallFinished() { var text = @$"After this file is successfully installed by the system, it should only be modified or removed when you need to reinstall the entire system! Please operate with caution!! -此文件由系统成功安装后,仅在您需要全部重新安装系统时将其修改或移除!请谨慎操作!!"; +This file is created after successful system installation. Modify or remove it only when you need to reinstall the whole system. Please operate with caution!!"; await File.WriteAllTextAsync(filePath, text); } } - public static int PageViewCount { get; set; } //网站启动后前台页面浏览量 + public static int PageViewCount { get; set; } //Frontend page views after site startup /// - /// 是否应有数据库模块载入 + /// Whether database module should be loaded /// public static bool DatabaseXncfLoaded { get; set; } /// - /// 系统状态 + /// System state /// public static NcfCoreState NcfCoreState { get; } = NcfCoreState.Instance; - //异步线程 - public static Dictionary AsynThread = new Dictionary(); //后台运行线程 + //Async threads + public static Dictionary AsynThread = new Dictionary(); //Background running threads /// - /// Admin 管理员的 Cookie 登录 Scheme + /// Cookie login scheme for Admin /// public readonly static string NcfAdminAuthorizeScheme = "NcfAdminAuthorizeScheme"; /// - /// User 管理员的 Cookie 登录 Scheme + /// Cookie login scheme for User /// public readonly static string NcfUserAuthorizeScheme = "NcfUserAuthorizeScheme"; } diff --git a/src/Basic/Senparc.Ncf.Core/Utility/CommonWebParts.cs b/src/Basic/Senparc.Ncf.Core/Utility/CommonWebParts.cs index 4c2e86504..73ffbb28a 100644 --- a/src/Basic/Senparc.Ncf.Core/Utility/CommonWebParts.cs +++ b/src/Basic/Senparc.Ncf.Core/Utility/CommonWebParts.cs @@ -9,10 +9,10 @@ namespace Senparc.Ncf.Core.Utility { public static class CommonWebParts { - //#region 转换HTML代码 public static string exHTML(string ntext) + //#region Convert HTML code public static string exHTML(string ntext) /////// - /////// 转换HTML代码——TNT2 - /////// 已实现:回车,空格 + /////// Convert HTML code -- TNT2 + /////// Implemented: newline, space /////// ////public static string exHTML(string ntext) ////{ @@ -21,8 +21,8 @@ public static class CommonWebParts ////} ///// - ///// 转换HTML代码——TNT2 - ///// 已实现:回车,空格 + ///// Convert HTML code -- TNT2 + ///// Implemented: newline, space ///// //public static string ExHTML(this string ntext) //{ @@ -38,7 +38,7 @@ public static class CommonWebParts //#endregion /// - /// 删除所有HTML标记 + /// Remove all HTML tags /// /// /// @@ -51,9 +51,9 @@ public static string DelHtml(this string str) { /// - /// 获取Form值,Handler用 + /// Get Form value, for Handler usage /// - /// Form键 + /// Form key /// HttpContext /// public static string GetFormValue(string key, HttpContext context) @@ -63,48 +63,48 @@ public static string GetFormValue(string key, HttpContext context) /// - /// 获取格式化后的文件名 + /// Get formatted file name /// - /// 文件格式(在Config.UpLoadFileFormat中) - /// 当前文件名(可以包含路径) + /// File format (in Config.UpLoadFileFormat) + /// Current file name (path can be included) /// public static string GetFormattedUpLoadFileName(string fileFormat, string currentFileName) { return string.Format(fileFormat, currentFileName, Path.GetExtension(currentFileName)); } - #region 货币大小写转换 + #region Currency uppercase conversion /// - /// 货币大小写转换 + /// Currency uppercase conversion /// - /// 货币金额 + /// Currency amount /// public static string CmycurD(decimal num) { - string str1 = "零壹贰叁肆伍陆柒捌玖"; //0-9所对应的汉字 - string str2 = "万仟佰拾亿仟佰拾万仟佰拾元角分"; //数字位所对应的汉字 - string str3 = ""; //从原num值中取出的值 - string str4 = ""; //数字的字符串形式 - string str5 = ""; //人民币大写金额形式 - int i; //循环变量 - int j; //num的值乘以100的字符串长度 - string ch1 = ""; //数字的汉语读法 - string ch2 = ""; //数字位的汉字读法 - int nzero = 0; //用来计算连续的零值是几个 - int temp; //从原num值中取出的值 - num = Math.Round(Math.Abs(num), 2); //将num取绝对值并四舍五入取2位小数 - str4 = ((long)(num * 100)).ToString(); //将num乘100并转换成字符串形式 - j = str4.Length; //找出最高位 + string str1 = "零壹贰叁肆伍陆柒捌玖"; //Chinese characters corresponding to 0-9 + string str2 = "万仟佰拾亿仟佰拾万仟佰拾元角分"; //Chinese characters for digit places + string str3 = ""; //Value extracted from original num + string str4 = ""; //String representation of number + string str5 = ""; //Uppercase RMB amount format + int i; //Loop variable + int j; //Length of num*100 string + string ch1 = ""; //Chinese reading of digit + string ch2 = ""; //Chinese reading of digit place + int nzero = 0; //Count consecutive zero values + int temp; //Value extracted from original num + num = Math.Round(Math.Abs(num), 2); //Take absolute value and round to 2 decimals + str4 = ((long)(num * 100)).ToString(); //Multiply num by 100 and convert to string + j = str4.Length; //Find highest digit if (j > 15) { return "溢出"; } - str2 = str2.Substring(15 - j); //取出对应位数的str2的值。如:200.55,j为5所以str2=佰拾元角分 - //循环取出每一位需要转换的值 + str2 = str2.Substring(15 - j); //Extract corresponding part of str2, e.g. 200.55 -> j=5 -> str2=佰拾元角分 + //Loop through digits to convert for (i = 0; i < j; i++) { - str3 = str4.Substring(i, 1); //取出需转换的某一位的值 - temp = Convert.ToInt32(str3); //转换为数字 + str3 = str4.Substring(i, 1); //Get one digit to convert + temp = Convert.ToInt32(str3); //Convert to number if (i != (j - 3) && i != (j - 7) && i != (j - 11) && i != (j - 15)) { - //当所取位数不为元、万、亿、万亿上的数字时 + //When current digit place is not yuan/ten-thousand/hundred-million/trillion if (str3 == "0") { ch1 = ""; @@ -129,7 +129,7 @@ public static string CmycurD(decimal num) } else { - //该位是万亿,亿,万,元位等关键位 + //Current place is a key place: trillion/hundred-million/ten-thousand/yuan if (str3 != "0" && nzero != 0) { ch1 = "零" + str1.Substring(temp * 1, 1); @@ -171,13 +171,13 @@ public static string CmycurD(decimal num) } if (i == (j - 11) || i == (j - 3)) { - //如果该位是亿位或元位,则必须写上 + //If this place is hundred-million or yuan, it must be written ch2 = str2.Substring(i, 1); } str5 = str5 + ch1 + ch2; if (i == j - 1 && str3 == "0") { - //最后一位(分)为0时,加上“整” + //If the last digit (fen) is 0, append "整" str5 = str5 + '整'; } } @@ -189,9 +189,9 @@ public static string CmycurD(decimal num) } /// - /// 货币大小写转换 + /// Currency uppercase conversion /// - /// 货币金额 + /// Currency amount /// public static string CmycurD(string numstr) { @@ -202,7 +202,7 @@ public static string CmycurD(string numstr) } catch { - return "非数字形式!"; + return "Non-numeric format!"; } } diff --git a/src/Basic/Senparc.Ncf.Core/Utility/XmlDataContext.cs b/src/Basic/Senparc.Ncf.Core/Utility/XmlDataContext.cs index 9668064d0..bf35ac633 100644 --- a/src/Basic/Senparc.Ncf.Core/Utility/XmlDataContext.cs +++ b/src/Basic/Senparc.Ncf.Core/Utility/XmlDataContext.cs @@ -1,487 +1,487 @@ -using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; -using Senparc.CO2NET.Extensions; -using Senparc.CO2NET.Utilities; -using Senparc.Ncf.Core.Extensions; -using Senparc.Ncf.Core.Models; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Xml.Linq; - -namespace Senparc.Ncf.Core.Utility -{ - public partial class XmlDataContext - { - public string DatabaseDictionary { get; set; } - - //private const string RESET_PASSWORD_CODE = "ResetPasswordCode.xml"; - - /// - /// 文件名,不包含扩展名 - /// - public string FileName { get; set; } - - public XmlDataContext() - : this(null) - { - } - - /// - /// - /// - /// 数据库路径,必须是~/开头 - /// - public XmlDataContext(string databaseDictionary, string fileName = null) - { - DatabaseDictionary = string.IsNullOrEmpty(databaseDictionary) ? "~/App_Data/Database/" : databaseDictionary; - if (!DatabaseDictionary.EndsWith("/")) - { - DatabaseDictionary += "/"; - } - if (!string.IsNullOrEmpty(fileName)) - { - FileName = fileName; - } - } - - /// - /// 载入xml文档 - /// - /// - /// - private XElement GetXElement(string path) - { - return XElement.Load(this.GetMapPath(path));//path.Replace("~/", HttpRuntime.AppDomainAppPath)); - } - - /// - /// 获取文件路径 - /// - /// - /// - private string GetMapPath(string path) - { - return ServerUtility.ContentRootMapPath(path);//path.Replace("~/", HttpRuntime.AppDomainAppPath);// _context.Server.MapPath(path); - } - - - - /// - /// 获取完整路径 - /// - /// - /// - private string GetXmlFullApplicationPath(string entityName) - { - Func getFilePath = (string fileName) => DatabaseDictionary + fileName + ".config";//xml文件规则为 “{实例名称}.config” - - var fileName = FileName.IsNullOrEmpty() ? entityName : FileName; - var origionalPath = getFilePath(fileName); - - //TODO: 添加对环境变量的识别 - if (System.Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development") - { - var tryEntityName = entityName + ".Development"; - var tryPath = getFilePath(tryEntityName); - - if (File.Exists(this.GetMapPath(tryPath))) - { - origionalPath = tryPath; - } - - //TODO:暂时不支持其他环境变量 - } - - return origionalPath; - } - - /// - /// 获取xml数据,并返回TEntity实体 - /// - /// - /// - public List GetXmlList() where TEntity : class, new() - { - string entityName = typeof(TEntity).Name; - string filePath = GetXmlFullApplicationPath(entityName); - - XElement xml = this.GetXElement(filePath); - List results = new List(); - - foreach (var x in xml.Elements(entityName)) - { - TEntity result = ConvertXmlToEntity(x); - results.Add(result); - } - - return results; - } - - /// - /// 将Xml Item节点内容专为实体类型 - /// - /// - /// - /// - private TEntity ConvertXmlToEntity(XElement x) where TEntity : class, new() - { - TEntity result = new TEntity(); - foreach (var prop in result.GetType().GetProperties()) - { - string value = x.Element(prop.Name).Value; - //prop.SetValue(result, value, null); - switch (prop.PropertyType.Name) - { - case "DateTime": - prop.SetValue(result, DateTime.Parse(value), null); - break; - case "TimeOnly": - prop.SetValue(result, TimeOnly.Parse(value), null); - break; - case "DateOnly": - prop.SetValue(result, DateOnly.Parse(value), null); - break; - case "Int32": - prop.SetValue(result, int.Parse(value), null); - break; - case "Int64": - prop.SetValue(result, long.Parse(value), null); - break; - case "Single": - case "float": - prop.SetValue(result, float.Parse(value), null); - break; - case "Double": - prop.SetValue(result, double.Parse(value), null); - break; - case "Boolean": - prop.SetValue(result, bool.Parse(value), null); - break; - default: - //判断是否为枚举类型 - if (prop.PropertyType.IsEnum) - { - prop.SetValue(result, Enum.Parse(prop.PropertyType, value), null); - } - else - { - prop.SetValue(result, value, null); - } - break; - } - } - return result; - } - - - ///// - ///// 重设密码 - ///// - //public List ResetPasswordCodes - //{ - // get - // { - // return this.GetXmlList(); - // } - //} - } - - - public partial class XmlDataContext - { - //public bool Save(IEnumerable entityList) where TEntity : class - //{ - // try - // { - // string entityName = typeof(TEntity).Name; - // XElement xml = new XElement(entityName + "s"); - // var props = typeof(TEntity).GetProperties(); - - // foreach (var entity in entityList) - // { - // XElement xe = new XElement(entityName); - - // foreach (var prop in props) - // { - // switch (prop.PropertyType.Name) - // { - // case "DateTime": - // xe.Add(new XElement(prop.Name, prop.GetValue(entity, null).ToString())); - // break; - // default: - // xe.Add(new XElement(prop.Name, prop.GetValue(entity, null))); - // break; - // } - // } - - // xml.Add(xe); - // } - - // xml.Save(GetMapPath(GetXmlFullApplicationPath(entityName))); - - // return true; - // } - // catch - // { - // return false; - // } - //} - - /// - /// 保存记录,需要有ID - /// - /// - /// 需要保存的完整对象列表 - /// - public bool Save(IEnumerable entities) where TEntity : class - { - try - { - string entityName = typeof(TEntity).Name; - XElement xml = this.GetXElement(GetXmlFullApplicationPath(entityName)); - - var props = typeof(TEntity).GetProperties(); - var idProp = typeof(TEntity).GetProperty("Id"); - if (idProp == null) - { - throw new Exception("Id属性不存在!"); - } - - foreach (var entity in entities) - { - int id = (int)idProp.GetValue(entity, null); - - //查找原纪录 - XElement oldElement = xml.Elements(entityName).FirstOrDefault(z => z.Element("Id").Value == id.ToString()); - if (oldElement == null) - { - throw new Exception("待更新数据不存在!"); - } - - //开始更新 - foreach (var prop in props) - { - if (prop.Name == "Id") - { - continue; - } - XElement valueElement = oldElement.Element(prop.Name); - if (valueElement == null) - { - continue; - } - - switch (prop.PropertyType.Name) - { - case "DateTime": - case "Int32": - valueElement.Value = prop.GetValue(entity, null).ToString(); - break; - default: - valueElement.RemoveAll(); - valueElement.Add(new XCData(prop.GetValue(entity, null).ToString())); - break; - } - } - } - - //保存更新 - xml.Save(this.GetMapPath(this.GetXmlFullApplicationPath(entityName))); - return true; - } - catch - { - return false; - } - } - - /// - /// 插入记录 - /// - /// - /// - /// - public bool Insert(TEntity entity) where TEntity : class - { - try - { - string entityName = typeof(TEntity).Name; - XElement xml = this.GetXElement(GetXmlFullApplicationPath(entityName)); - - //判断主键 - int maxId = -1; - XAttribute maxIdAttr = xml.Attribute("maxId"); - if (maxIdAttr != null) - { - maxId = Convert.ToInt32(maxIdAttr.Value); - maxId++; - maxIdAttr.Value = maxId.ToString(); - - } - - var props = typeof(TEntity).GetProperties(); - XElement xe = new XElement(entityName); - foreach (var prop in props) - { - string name = prop.Name; - object value = prop.GetValue(entity, null).ToString(); - if (maxId >= 0 && name == "Id") - { - value = maxId; - maxIdAttr.Value = maxId.ToString(); - prop.SetValue(entity, maxId, null); //把最新的Id设置到实体 - } - - switch (prop.PropertyType.Name) - { - case "DateTime": - case "Int32": - xe.Add(new XElement(name, value)); - break; - default: - xe.Add(new XElement(name, new XCData(value.ToString()))); - break; - } - } - xml.Add(xe); - xml.Save(this.GetMapPath(this.GetXmlFullApplicationPath(entityName))); - return true; - } - catch { return false; } - } - - /// - /// 使用此方法须确定数据库中没有重复项(或有主键) - /// - /// - /// - /// - public bool Delete(TEntity entity) where TEntity : class - { - try - { - string entityName = typeof(TEntity).Name; - XElement xml = this.GetXElement(GetXmlFullApplicationPath(entityName));//载入文档 - - var idProp = typeof(TEntity).GetProperty("Id"); - if (idProp == null) - { - throw new Exception("Id属性不存在!"); - } - - int id = (int)idProp.GetValue(entity, null); - //查找原纪录 - XElement delElement = xml.Elements(entityName).FirstOrDefault(z => z.Element("Id").Value == id.ToString()); - if (delElement == null) - { - return true;//throw new Exception("待更新数据不存在!"); - } - delElement.Remove();//删除节点 - - xml.Save(this.GetMapPath(this.GetXmlFullApplicationPath(entityName))); - return true; - } - catch //(Exception e) - { - //XmlDataContext ctx = new XmlDataContext(_context); - //AutoSendEmail error = new AutoSendEmail - //{ - // Subject = "发送出错记录", - // Body = e.Message + "\r\n" + e.StackTrace, - // LastSendTime = DateTime.Now, - // UserName = "System", - // SendCount = 505, - // Address = "szw2003@163.com", - //}; - //ctx.Insert(error); - - return false; - } - } - - /// - /// 使用此方法须确定数据库中没有重复项(或有主键) - /// - /// - /// - /// - public TEntity GetItem(object id) where TEntity : class, new() - { - try - { - string entityName = typeof(TEntity).Name; - XElement xml = this.GetXElement(GetXmlFullApplicationPath(entityName));//载入文档 - - var idProp = typeof(TEntity).GetProperty("Id"); - if (idProp == null) - { - throw new Exception("Id属性不存在!"); - } - - //查找原纪录 - XElement item = xml.Elements(entityName).FirstOrDefault(z => z.Element("Id").Value == id.ToString()); - if (item == null) - { - return null;//throw new Exception("待更新数据不存在!"); - } - - TEntity result = ConvertXmlToEntity(item); - return result; - } - catch //(Exception e) - { - return null; - } - } - - /// - /// 仅保留指定项目 - /// - /// 保留不删除的项目数量 - public void RetainItems(int retainItemCount) where TEntity : class, new() - { - string entityName = typeof(TEntity).Name; - XElement xml = this.GetXElement(GetXmlFullApplicationPath(entityName));//载入文档 - - var elements = xml.Elements(entityName); - if (elements.Count() >= retainItemCount) - { - elements.Take(elements.Count() - retainItemCount).Remove(); - } - xml.Save(this.GetMapPath(this.GetXmlFullApplicationPath(entityName))); - } - - /// - /// 尝试创建数据库 - /// - /// - public void TryCreateDataBase() - { - string entityName = typeof(TEntity).Name; - - //判断文件是否存在,如果不存在则新建 - var filePath = GetXmlFullApplicationPath(entityName); - if (!File.Exists(filePath)) - { - var xml = new XElement(entityName + "s", new XAttribute("maxId", 0)); - xml.Save(this.GetMapPath(this.GetXmlFullApplicationPath(entityName))); - } - } - } - - - #region XML DataBase格式 - - ///// - ///// 重设密码 - ///// - //public partial class ResetPasswordCode - //{ - // public string UserName { get; set; } - // public string Code { get; set; } - // public DateTime CreateTime { get; set; } - //} - - #endregion +using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; +using Senparc.CO2NET.Extensions; +using Senparc.CO2NET.Utilities; +using Senparc.Ncf.Core.Extensions; +using Senparc.Ncf.Core.Models; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; + +namespace Senparc.Ncf.Core.Utility +{ + public partial class XmlDataContext + { + public string DatabaseDictionary { get; set; } + + //private const string RESET_PASSWORD_CODE = "ResetPasswordCode.xml"; + + /// + /// File name, without extension + /// + public string FileName { get; set; } + + public XmlDataContext() + : this(null) + { + } + + /// + /// + /// + /// Database path, must start with ~/ + /// + public XmlDataContext(string databaseDictionary, string fileName = null) + { + DatabaseDictionary = string.IsNullOrEmpty(databaseDictionary) ? "~/App_Data/Database/" : databaseDictionary; + if (!DatabaseDictionary.EndsWith("/")) + { + DatabaseDictionary += "/"; + } + if (!string.IsNullOrEmpty(fileName)) + { + FileName = fileName; + } + } + + /// + /// Load XML document + /// + /// + /// + private XElement GetXElement(string path) + { + return XElement.Load(this.GetMapPath(path));//path.Replace("~/", HttpRuntime.AppDomainAppPath)); + } + + /// + /// Get file path + /// + /// + /// + private string GetMapPath(string path) + { + return ServerUtility.ContentRootMapPath(path);//path.Replace("~/", HttpRuntime.AppDomainAppPath);// _context.Server.MapPath(path); + } + + + + /// + /// Get full path + /// + /// + /// + private string GetXmlFullApplicationPath(string entityName) + { + Func getFilePath = (string fileName) => DatabaseDictionary + fileName + ".config";//XML file naming rule: "{InstanceName}.config" + + var fileName = FileName.IsNullOrEmpty() ? entityName : FileName; + var origionalPath = getFilePath(fileName); + + //TODO: Add environment variable recognition + if (System.Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development") + { + var tryEntityName = entityName + ".Development"; + var tryPath = getFilePath(tryEntityName); + + if (File.Exists(this.GetMapPath(tryPath))) + { + origionalPath = tryPath; + } + + //TODO: other environment variables are not supported yet + } + + return origionalPath; + } + + /// + /// Get XML data and return TEntity list + /// + /// + /// + public List GetXmlList() where TEntity : class, new() + { + string entityName = typeof(TEntity).Name; + string filePath = GetXmlFullApplicationPath(entityName); + + XElement xml = this.GetXElement(filePath); + List results = new List(); + + foreach (var x in xml.Elements(entityName)) + { + TEntity result = ConvertXmlToEntity(x); + results.Add(result); + } + + return results; + } + + /// + /// Convert XML item node content to entity type + /// + /// + /// + /// + private TEntity ConvertXmlToEntity(XElement x) where TEntity : class, new() + { + TEntity result = new TEntity(); + foreach (var prop in result.GetType().GetProperties()) + { + string value = x.Element(prop.Name).Value; + //prop.SetValue(result, value, null); + switch (prop.PropertyType.Name) + { + case "DateTime": + prop.SetValue(result, DateTime.Parse(value), null); + break; + case "TimeOnly": + prop.SetValue(result, TimeOnly.Parse(value), null); + break; + case "DateOnly": + prop.SetValue(result, DateOnly.Parse(value), null); + break; + case "Int32": + prop.SetValue(result, int.Parse(value), null); + break; + case "Int64": + prop.SetValue(result, long.Parse(value), null); + break; + case "Single": + case "float": + prop.SetValue(result, float.Parse(value), null); + break; + case "Double": + prop.SetValue(result, double.Parse(value), null); + break; + case "Boolean": + prop.SetValue(result, bool.Parse(value), null); + break; + default: + //Check whether it is enum type + if (prop.PropertyType.IsEnum) + { + prop.SetValue(result, Enum.Parse(prop.PropertyType, value), null); + } + else + { + prop.SetValue(result, value, null); + } + break; + } + } + return result; + } + + + ///// + ///// 重设密码 + ///// + //public List ResetPasswordCodes + //{ + // get + // { + // return this.GetXmlList(); + // } + //} + } + + + public partial class XmlDataContext + { + //public bool Save(IEnumerable entityList) where TEntity : class + //{ + // try + // { + // string entityName = typeof(TEntity).Name; + // XElement xml = new XElement(entityName + "s"); + // var props = typeof(TEntity).GetProperties(); + + // foreach (var entity in entityList) + // { + // XElement xe = new XElement(entityName); + + // foreach (var prop in props) + // { + // switch (prop.PropertyType.Name) + // { + // case "DateTime": + // xe.Add(new XElement(prop.Name, prop.GetValue(entity, null).ToString())); + // break; + // default: + // xe.Add(new XElement(prop.Name, prop.GetValue(entity, null))); + // break; + // } + // } + + // xml.Add(xe); + // } + + // xml.Save(GetMapPath(GetXmlFullApplicationPath(entityName))); + + // return true; + // } + // catch + // { + // return false; + // } + //} + + /// + /// Save records, requires ID + /// + /// + /// Complete object list to save + /// + public bool Save(IEnumerable entities) where TEntity : class + { + try + { + string entityName = typeof(TEntity).Name; + XElement xml = this.GetXElement(GetXmlFullApplicationPath(entityName)); + + var props = typeof(TEntity).GetProperties(); + var idProp = typeof(TEntity).GetProperty("Id"); + if (idProp == null) + { + throw new Exception("Id property does not exist!"); + } + + foreach (var entity in entities) + { + int id = (int)idProp.GetValue(entity, null); + + // Find original record + XElement oldElement = xml.Elements(entityName).FirstOrDefault(z => z.Element("Id").Value == id.ToString()); + if (oldElement == null) + { + throw new Exception("待更新数据不存在!"); + } + + //Start update + foreach (var prop in props) + { + if (prop.Name == "Id") + { + continue; + } + XElement valueElement = oldElement.Element(prop.Name); + if (valueElement == null) + { + continue; + } + + switch (prop.PropertyType.Name) + { + case "DateTime": + case "Int32": + valueElement.Value = prop.GetValue(entity, null).ToString(); + break; + default: + valueElement.RemoveAll(); + valueElement.Add(new XCData(prop.GetValue(entity, null).ToString())); + break; + } + } + } + + //Save updates + xml.Save(this.GetMapPath(this.GetXmlFullApplicationPath(entityName))); + return true; + } + catch + { + return false; + } + } + + /// + /// Insert record + /// + /// + /// + /// + public bool Insert(TEntity entity) where TEntity : class + { + try + { + string entityName = typeof(TEntity).Name; + XElement xml = this.GetXElement(GetXmlFullApplicationPath(entityName)); + + //Check primary key + int maxId = -1; + XAttribute maxIdAttr = xml.Attribute("maxId"); + if (maxIdAttr != null) + { + maxId = Convert.ToInt32(maxIdAttr.Value); + maxId++; + maxIdAttr.Value = maxId.ToString(); + + } + + var props = typeof(TEntity).GetProperties(); + XElement xe = new XElement(entityName); + foreach (var prop in props) + { + string name = prop.Name; + object value = prop.GetValue(entity, null).ToString(); + if (maxId >= 0 && name == "Id") + { + value = maxId; + maxIdAttr.Value = maxId.ToString(); + prop.SetValue(entity, maxId, null); //把最新的Id设置到实体 + } + + switch (prop.PropertyType.Name) + { + case "DateTime": + case "Int32": + xe.Add(new XElement(name, value)); + break; + default: + xe.Add(new XElement(name, new XCData(value.ToString()))); + break; + } + } + xml.Add(xe); + xml.Save(this.GetMapPath(this.GetXmlFullApplicationPath(entityName))); + return true; + } + catch { return false; } + } + + /// + /// 使用此方法须确定数据库中没有重复项(或有主键) + /// + /// + /// + /// + public bool Delete(TEntity entity) where TEntity : class + { + try + { + string entityName = typeof(TEntity).Name; + XElement xml = this.GetXElement(GetXmlFullApplicationPath(entityName));//载入文档 + + var idProp = typeof(TEntity).GetProperty("Id"); + if (idProp == null) + { + throw new Exception("Id property does not exist!"); + } + + int id = (int)idProp.GetValue(entity, null); + // Find original record + XElement delElement = xml.Elements(entityName).FirstOrDefault(z => z.Element("Id").Value == id.ToString()); + if (delElement == null) + { + return true;//throw new Exception("待更新数据不存在!"); + } + delElement.Remove();//删除节点 + + xml.Save(this.GetMapPath(this.GetXmlFullApplicationPath(entityName))); + return true; + } + catch //(Exception e) + { + //XmlDataContext ctx = new XmlDataContext(_context); + //AutoSendEmail error = new AutoSendEmail + //{ + // Subject = "发送出错记录", + // Body = e.Message + "\r\n" + e.StackTrace, + // LastSendTime = DateTime.Now, + // UserName = "System", + // SendCount = 505, + // Address = "szw2003@163.com", + //}; + //ctx.Insert(error); + + return false; + } + } + + /// + /// 使用此方法须确定数据库中没有重复项(或有主键) + /// + /// + /// + /// + public TEntity GetItem(object id) where TEntity : class, new() + { + try + { + string entityName = typeof(TEntity).Name; + XElement xml = this.GetXElement(GetXmlFullApplicationPath(entityName));//载入文档 + + var idProp = typeof(TEntity).GetProperty("Id"); + if (idProp == null) + { + throw new Exception("Id property does not exist!"); + } + + // Find original record + XElement item = xml.Elements(entityName).FirstOrDefault(z => z.Element("Id").Value == id.ToString()); + if (item == null) + { + return null;//throw new Exception("待更新数据不存在!"); + } + + TEntity result = ConvertXmlToEntity(item); + return result; + } + catch //(Exception e) + { + return null; + } + } + + /// + /// 仅保留指定项目 + /// + /// 保留不删除的项目数量 + public void RetainItems(int retainItemCount) where TEntity : class, new() + { + string entityName = typeof(TEntity).Name; + XElement xml = this.GetXElement(GetXmlFullApplicationPath(entityName));//载入文档 + + var elements = xml.Elements(entityName); + if (elements.Count() >= retainItemCount) + { + elements.Take(elements.Count() - retainItemCount).Remove(); + } + xml.Save(this.GetMapPath(this.GetXmlFullApplicationPath(entityName))); + } + + /// + /// 尝试创建数据库 + /// + /// + public void TryCreateDataBase() + { + string entityName = typeof(TEntity).Name; + + //判断文件是否存在,如果不存在则新建 + var filePath = GetXmlFullApplicationPath(entityName); + if (!File.Exists(filePath)) + { + var xml = new XElement(entityName + "s", new XAttribute("maxId", 0)); + xml.Save(this.GetMapPath(this.GetXmlFullApplicationPath(entityName))); + } + } + } + + + #region XML DataBase格式 + + ///// + ///// 重设密码 + ///// + //public partial class ResetPasswordCode + //{ + // public string UserName { get; set; } + // public string Code { get; set; } + // public DateTime CreateTime { get; set; } + //} + + #endregion } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Core/Validator/Validator.cs b/src/Basic/Senparc.Ncf.Core/Validator/Validator.cs index 0930ad48f..b824cf5b9 100644 --- a/src/Basic/Senparc.Ncf.Core/Validator/Validator.cs +++ b/src/Basic/Senparc.Ncf.Core/Validator/Validator.cs @@ -1,608 +1,608 @@ -using System; -using System.Text.RegularExpressions; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Mvc; -using Senparc.Ncf.Core.Utility; -using Senparc.Ncf.Core.Models; - -namespace Senparc.Ncf.Core.Validator -{ - public class ValidatorContainer - { - //private List _errorList; - - public T ValidatorObject { get; set; } - public string ValueName { get; set; } - public bool Stopped { get; set; } - public bool IsValid { get; set; } - public string HtmlName { get; set; } - public ModelStateDictionary ModelState { get; set; } - public IValidatorEnvironment ValidatorEnvironment { get; set; } - - //public List ErrorList - //{ - // get { return _errorList; } - // set - // { - // _errorList = value; - // this.IsValid = false; - // } - //} - - public ValidatorContainer(IValidatorEnvironment validatorEnvironment, T validatorEnvironmentObject, string valueName, string htmlName) - { - ValidatorObject = validatorEnvironmentObject; - ValueName = valueName; - HtmlName = htmlName; - ModelState = validatorEnvironment.ModelState; - ValidatorEnvironment = validatorEnvironment; - IsValid = true; - //ErrorList = new List(); - } - - /// - /// 此方法暂时不能自动让ModelState.IsValid变为false - /// - /// - public void AddError(string errorMessage) - { - this.ModelState.AddModelError(this.HtmlName, errorMessage); - this.IsValid = false; - } - } - - public static class ValidatorExtension - { - public static ValidatorContainer Validator(this IValidatorEnvironment validatorEnvironment, T validatorEnvironmentObject, string valueName, string htmlName) - { - return Validator(validatorEnvironment, validatorEnvironmentObject, valueName, htmlName, null); - } - - /// - /// - /// - /// - /// - /// - /// - /// 是否允许为null或为空。输入null默认为不判断。首尾连续空格将被过滤(.Trim()) - /// - public static ValidatorContainer Validator(this IValidatorEnvironment validatorEnvironment, T validatorEnvironmentObject, string valueName, string htmlName, bool? nullOrEmptyable = null) - { - if (validatorEnvironment.ModelState[htmlName] != null - && validatorEnvironment.ModelState[htmlName].Errors.Count > 0) - { - //移除原有的ModelState中的"The value '' is invalid."错误 - //TODO:可以使用Resource自行配置 - validatorEnvironment.ModelState[htmlName].Errors.Clear(); - } - if (nullOrEmptyable != null && (validatorEnvironmentObject == null || string.IsNullOrEmpty(validatorEnvironmentObject.ToString().Trim()))) - { - if (nullOrEmptyable == true)//可为空 - { - return null;//为空,返回,但是不记录错误 - } - else//不可为空 - { - var container = new ValidatorContainer(validatorEnvironment, validatorEnvironmentObject, valueName, htmlName); - return container.NotNullOrEmpty(true); - } - } - else - { - return new ValidatorContainer(validatorEnvironment, validatorEnvironmentObject, valueName, htmlName);//不判断是否为空 - } - } - - - public static ValidatorContainer NotNullOrEmpty(this ValidatorContainer container) - { - return NotNullOrEmpty(container, true); - } - - public static ValidatorContainer NotNullOrEmpty(this ValidatorContainer container, bool stopWhileFail) - { - if (container == null) - { - return null; - } - - if (container.ValidatorObject == null || string.IsNullOrEmpty(container.ValidatorObject.ToString().Trim())) - { - string errorMessage = string.Format("提示: {0}!", container.ValueName); - //container.ErrorList.Add(errorMessage); - container.AddError(errorMessage); - - if (stopWhileFail) - { - return null; - } - } - return container; - } - - - public static ValidatorContainer IsTrue(this ValidatorContainer container, Func func, string failMessageFormat, bool stopWhileFail) - { - if (container == null) - { - return null; - } - - if (!func.Invoke(container.ValidatorObject)) - { - string errorMessage = string.Format(failMessageFormat, container.ValueName); - container.AddError(errorMessage); - if (stopWhileFail) - { - return null; - } - } - - return container; - } - - public static ValidatorContainer IsFalse(this ValidatorContainer container, Func func, string failMessageFormat, bool stopWhileFail) - { - if (container == null) - { - return null; - } - - if (func.Invoke(container.ValidatorObject)) - { - string errorMessage = string.Format(failMessageFormat, container.ValueName); - container.AddError(errorMessage); - if (stopWhileFail) - { - return null; - } - } - - return container; - } - - /// - /// 确认发生错误 - /// - /// - /// - /// - /// - /// - public static ValidatorContainer Fail(this ValidatorContainer container, string failMessageFormat, bool stopWhileFail) - { - if (container == null) - { - return null; - } - - string errorMessage = string.Format(failMessageFormat, container.ValueName); - container.AddError(errorMessage); - if (stopWhileFail) - { - return null; - } - - return container; - } - - public static ValidatorContainer Regex(this ValidatorContainer container, string expression, string failMessageFormat, bool stopWhileFail) - { - if (container == null) - { - return null; - } - - Regex emailExpression = new Regex(expression, RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase); - if (!emailExpression.IsMatch((container.ValidatorObject.ToString()))) - { - string errorMessage = string.Format(failMessageFormat, container.ValueName); - container.AddError(errorMessage); - if (stopWhileFail) - { - return null; - } - } - return container; - } - - /// - /// 检测是否有Sql危险字符 - /// - /// - /// - /// - /// - public static ValidatorContainer IsSafeSqlString(this ValidatorContainer container, bool stopWhileFail) - { - if (container == null) - { - return null; - } - - Regex emailExpression = new Regex(@"[-|;|,|\/|\(|\)|\[|\]|\}|\{|%|@|\*|!|\']", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase); - if (emailExpression.IsMatch((container.ValidatorObject.ToString()))) - { - string errorMessage = string.Format("{0}中存在非法字符!", container.ValueName); - container.AddError(errorMessage); - if (stopWhileFail) - { - return null; - } - } - return container; - } - - /// - /// 检测是否有危险的可能用于链接的字符串 - /// - /// - /// - /// - /// - public static ValidatorContainer IsSafeUserInfoString(this ValidatorContainer container, bool stopWhileFail) - { - if (container == null) - { - return null; - } - - Regex emailExpression = new Regex(@"^\s*$|^c:\\con\\con$|[%,\*" + "\"" + @"\s\t\<\>\&]|游客|管理员|^Guest|admin", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase); - if (emailExpression.IsMatch((container.ValidatorObject.ToString()))) - { - string errorMessage = string.Format("{0}中存在非法字符!", container.ValueName); - container.AddError(errorMessage); - if (stopWhileFail) - { - return null; - } - } - return container; - } - - public static ValidatorContainer IsValidateUserInfoName(this ValidatorContainer container, bool stopWhileFail) - { - if (container == null) - { - return null; - } - string userName = container.ValidatorObject.ToString(); - if (userName.IndexOf(" ") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1) - { - container.AddError("用户名中不允许包含全角空格符"); - if (stopWhileFail) - { - return null; - } - } - if (userName.IndexOf(" ") != -1) - { - container.AddError("用户名中不允许包含空格"); - if (stopWhileFail) - { - return null; - } - } - if (userName.IndexOf(":") != -1) - { - container.AddError("用户名中不允许包含冒号"); - if (stopWhileFail) - { - return null; - } - } - - string invalidateUserName = "`~!@#$%^&*()+-=;':\",./<>?|\\";//TODO:可以用正则判断 - foreach (var item in invalidateUserName) - { - if (userName.Contains(item)) - { - container.AddError("用户名中可以使用中文、英文或数字及下划线_,但不允许包含特殊符号:" + invalidateUserName); - if (stopWhileFail) - { - return null; - } - } - } - - return container; - } - - public static ValidatorContainer IsEmail(this ValidatorContainer container) - { - return IsEmail(container, true); - } - public static ValidatorContainer IsEmail(this ValidatorContainer container, bool stopWhileFail) - { - return Regex(container, - @"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$", - "请在{0}中填写正确的Email地址!", stopWhileFail); - } - - public static ValidatorContainer IsMobile(this ValidatorContainer container, bool stopWhileFail) - { - //return Regex(container, @"^1[358]\d{9}$", "请在{0}中填写正确的电话号码!", stopWhileFail); - //return Regex(container, @"^1[358]\d{9}$", "请填写正确的电话号码!", stopWhileFail); - - //电信手机号码正则 string dianxin = @"^1[3578][01379]\d{8}$"; Regex dReg = new Regex(dianxin); //联通手机号正则 string liantong = @"^1[34578][01256]\d{8}$"; Regex tReg = new Regex(liantong); //移动手机号正则 string yidong = @"^(134[012345678]\d{7}|1[34578][012356789]\d{8})$"; Regex yReg = new Regex(yidong); - - return Regex(container, @"^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$", "请填写正确的电话号码!", stopWhileFail); - } - - public static ValidatorContainer IsIPAddress(this ValidatorContainer container) - { - return IsIPAddress(container, true); - } - - public static ValidatorContainer IsIPAddress(this ValidatorContainer container, bool stopWhileFail) - { - return Regex(container, - @"(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])", - "请在{0}中填写正确的IP地址!", stopWhileFail); - } - - public static ValidatorContainer IsAvailableUrl(this ValidatorContainer container, bool stopWhileFail) - { - return Regex(container, @"HTTP(S)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?", "请在{0}中填写正确的Url地址!", stopWhileFail); - } - - - public static ValidatorContainer MaxByte(this ValidatorContainer container, int maxByte) - { - return MaxByte(container, maxByte, true); - } - public static ValidatorContainer MaxByte(this ValidatorContainer container, int maxByte, bool stopWhileFail) - { - if (container == null) - { - return null; - } - - if (System.Text.Encoding.Default.GetByteCount(container.ValidatorObject.ToString()) > maxByte) - { - string errorMessage = string.Format("{0}中最多只能输入{1}字节的内容(对应{2}汉字)", container.ValueName, maxByte, Convert.ToInt32(maxByte / 2)); - container.AddError(errorMessage); - if (stopWhileFail) - { - return null; - } - } - return container; - } - - public static ValidatorContainer MaxLength(this ValidatorContainer container, int maxLength) - { - return MaxLength(container, maxLength, true); - } - public static ValidatorContainer MaxLength(this ValidatorContainer container, int maxLength, bool stopWhileFail) - { - if (container == null) - { - return null; - } - - if (container.ValidatorObject.ToString().Length > maxLength) - { - string errorMessage = string.Format("{0}中最多只能输入{1}个字符", container.ValueName, maxLength); - container.AddError(errorMessage); - if (stopWhileFail) - { - return null; - } - } - return container; - } - - - public static ValidatorContainer MinByte(this ValidatorContainer container, int minByte) - { - return MinByte(container, minByte, true); - } - public static ValidatorContainer MinByte(this ValidatorContainer container, int minByte, bool stopWhileFail) - { - if (container == null) - { - return null; - } - - if (System.Text.Encoding.Default.GetByteCount(container.ValidatorObject.ToString()) < minByte) - { - string errorMessage = string.Format("{0}中至少需要入{1}字节的内容(对应{2}汉字)", container.ValueName, minByte, Convert.ToInt32(minByte / 2)); - container.AddError(errorMessage); - if (stopWhileFail) - { - return null; - } - } - return container; - } - - - public static ValidatorContainer MinLength(this ValidatorContainer container, int minLength) - { - return MinLength(container, minLength, true); - } - public static ValidatorContainer MinLength(this ValidatorContainer container, int minLength, bool stopWhileFail) - { - if (container == null) - { - return null; - } - - if (container.ValidatorObject.ToString().Length < minLength) - { - string errorMessage = string.Format("{0}中至少需要输入{1}个字符", container.ValueName, minLength); - container.AddError(errorMessage); - if (stopWhileFail) - { - return null; - } - } - return container; - } - - - public static ValidatorContainer IsEqual(this ValidatorContainer container, T obj, string failMessageFormat, bool stopWhileFail) - { - if (container == null) - { - return null; - } - - if (!container.ValidatorObject.Equals(obj)) - { - string errorMessage = string.Format(failMessageFormat, container.ValueName); - container.AddError(errorMessage); - if (stopWhileFail) - { - return null; - } - } - return container; - } - - public static ValidatorContainer IsNotEqual(this ValidatorContainer container, T obj, string failMessageFormat, bool stopWhileFail) - { - if (container == null) - { - return null; - } - - if (container.ValidatorObject.Equals(obj)) - { - string errorMessage = string.Format(failMessageFormat, container.ValueName); - container.AddError(errorMessage); - if (stopWhileFail) - { - return null; - } - } - return container; - } - - public static ValidatorContainer Exclude(this ValidatorContainer container, string[] excludeStrings) - { - return Exclude(container, excludeStrings, true); - } - public static ValidatorContainer Exclude(this ValidatorContainer container, string[] excludeStrings, bool stopWhileFail) - { - if (container == null) - { - return null; - } - - foreach (var str in excludeStrings) - { - if (container.ValidatorObject.ToString().Contains(str)) - { - string charts = string.Join(",", excludeStrings).Replace(" ", "空格").Replace(" ", "全角空格"); - string errorMessage = string.Format("{0}中包含了不允许使用的特殊字符:{1}", container.ValueName, str); - container.AddError(errorMessage); - if (stopWhileFail) - { - return null; - } - } - return container; - } - return container; - } - - public static ValidatorContainer IsNumber(this ValidatorContainer container, float? min, float? max, bool stopWhileFail) - { - if (container == null) - { - return null; - } - min = min ?? float.MinValue; - max = max ?? float.MaxValue; - float tryFloat; - if (!float.TryParse(container.ValidatorObject.ToString(), out tryFloat) || tryFloat < (float)min || tryFloat > (float)max) - { - string errorMessage = string.Format("请输入正确的{0}", container.ValueName); - container.AddError(errorMessage); - if (stopWhileFail) - { - return null; - } - } - return container; - } - - /// - /// 校验验证码,建议Validator构造函数中的nullOrEmptyable为false - /// - /// - /// - /// - /// - public static ValidatorContainer ValidateCheckCode(this ValidatorContainer container, CheckCodeKind checkCodeKind, bool stopWhileFail) - { - if (container == null) - { - return null; - } - - if (container.ValidatorObject == null) - { - container.AddError("请输入验证码"); - if (stopWhileFail) - { - return null; - } - } - - CheckCodeHandle checkCodeHandle = new CheckCodeHandle(checkCodeKind, container.ValidatorEnvironment.HttpContext); - if (!checkCodeHandle.ValidateCheckCode(container.ValidatorObject.ToString())) - { - container.AddError("请输入正确的验证码"); - if (stopWhileFail) - { - return null; - } - } - - return container; - } - - - public static ValidatorContainer IsNull(this ValidatorContainer container, object obj, string failMessageFormat, bool stopWhileFail) - { - if (container == null) - { - return null; - } - - if (obj != null) - { - string errorMessage = string.Format(failMessageFormat, container.ValueName); - container.AddError(errorMessage); - if (stopWhileFail) - { - return null; - } - } - return container; - } - - public static ValidatorContainer IsNotNull(this ValidatorContainer container, object obj, string failMessageFormat, bool stopWhileFail) - { - if (container == null) - { - return null; - } - - if (obj == null) - { - string errorMessage = string.Format(failMessageFormat, container.ValueName); - container.AddError(errorMessage); - if (stopWhileFail) - { - return null; - } - } - return container; - } - } -} +using System; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc; +using Senparc.Ncf.Core.Utility; +using Senparc.Ncf.Core.Models; + +namespace Senparc.Ncf.Core.Validator +{ + public class ValidatorContainer + { + //private List _errorList; + + public T ValidatorObject { get; set; } + public string ValueName { get; set; } + public bool Stopped { get; set; } + public bool IsValid { get; set; } + public string HtmlName { get; set; } + public ModelStateDictionary ModelState { get; set; } + public IValidatorEnvironment ValidatorEnvironment { get; set; } + + //public List ErrorList + //{ + // get { return _errorList; } + // set + // { + // _errorList = value; + // this.IsValid = false; + // } + //} + + public ValidatorContainer(IValidatorEnvironment validatorEnvironment, T validatorEnvironmentObject, string valueName, string htmlName) + { + ValidatorObject = validatorEnvironmentObject; + ValueName = valueName; + HtmlName = htmlName; + ModelState = validatorEnvironment.ModelState; + ValidatorEnvironment = validatorEnvironment; + IsValid = true; + //ErrorList = new List(); + } + + /// + /// This method currently cannot automatically set ModelState.IsValid to false + /// + /// + public void AddError(string errorMessage) + { + this.ModelState.AddModelError(this.HtmlName, errorMessage); + this.IsValid = false; + } + } + + public static class ValidatorExtension + { + public static ValidatorContainer Validator(this IValidatorEnvironment validatorEnvironment, T validatorEnvironmentObject, string valueName, string htmlName) + { + return Validator(validatorEnvironment, validatorEnvironmentObject, valueName, htmlName, null); + } + + /// + /// + /// + /// + /// + /// + /// + /// Whether null or empty is allowed. Null means no validation. Leading/trailing spaces are trimmed (.Trim()). + /// + public static ValidatorContainer Validator(this IValidatorEnvironment validatorEnvironment, T validatorEnvironmentObject, string valueName, string htmlName, bool? nullOrEmptyable = null) + { + if (validatorEnvironment.ModelState[htmlName] != null + && validatorEnvironment.ModelState[htmlName].Errors.Count > 0) + { + //Remove existing "The value '' is invalid." error in ModelState + //TODO: configurable via Resource + validatorEnvironment.ModelState[htmlName].Errors.Clear(); + } + if (nullOrEmptyable != null && (validatorEnvironmentObject == null || string.IsNullOrEmpty(validatorEnvironmentObject.ToString().Trim()))) + { + if (nullOrEmptyable == true)//Nullable + { + return null;//Return without error when empty + } + else//Not nullable + { + var container = new ValidatorContainer(validatorEnvironment, validatorEnvironmentObject, valueName, htmlName); + return container.NotNullOrEmpty(true); + } + } + else + { + return new ValidatorContainer(validatorEnvironment, validatorEnvironmentObject, valueName, htmlName);//Skip null/empty check + } + } + + + public static ValidatorContainer NotNullOrEmpty(this ValidatorContainer container) + { + return NotNullOrEmpty(container, true); + } + + public static ValidatorContainer NotNullOrEmpty(this ValidatorContainer container, bool stopWhileFail) + { + if (container == null) + { + return null; + } + + if (container.ValidatorObject == null || string.IsNullOrEmpty(container.ValidatorObject.ToString().Trim())) + { + string errorMessage = string.Format("提示: {0}!", container.ValueName); + //container.ErrorList.Add(errorMessage); + container.AddError(errorMessage); + + if (stopWhileFail) + { + return null; + } + } + return container; + } + + + public static ValidatorContainer IsTrue(this ValidatorContainer container, Func func, string failMessageFormat, bool stopWhileFail) + { + if (container == null) + { + return null; + } + + if (!func.Invoke(container.ValidatorObject)) + { + string errorMessage = string.Format(failMessageFormat, container.ValueName); + container.AddError(errorMessage); + if (stopWhileFail) + { + return null; + } + } + + return container; + } + + public static ValidatorContainer IsFalse(this ValidatorContainer container, Func func, string failMessageFormat, bool stopWhileFail) + { + if (container == null) + { + return null; + } + + if (func.Invoke(container.ValidatorObject)) + { + string errorMessage = string.Format(failMessageFormat, container.ValueName); + container.AddError(errorMessage); + if (stopWhileFail) + { + return null; + } + } + + return container; + } + + /// + /// Force failure + /// + /// + /// + /// + /// + /// + public static ValidatorContainer Fail(this ValidatorContainer container, string failMessageFormat, bool stopWhileFail) + { + if (container == null) + { + return null; + } + + string errorMessage = string.Format(failMessageFormat, container.ValueName); + container.AddError(errorMessage); + if (stopWhileFail) + { + return null; + } + + return container; + } + + public static ValidatorContainer Regex(this ValidatorContainer container, string expression, string failMessageFormat, bool stopWhileFail) + { + if (container == null) + { + return null; + } + + Regex emailExpression = new Regex(expression, RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase); + if (!emailExpression.IsMatch((container.ValidatorObject.ToString()))) + { + string errorMessage = string.Format(failMessageFormat, container.ValueName); + container.AddError(errorMessage); + if (stopWhileFail) + { + return null; + } + } + return container; + } + + /// + /// Check whether string contains SQL-dangerous characters + /// + /// + /// + /// + /// + public static ValidatorContainer IsSafeSqlString(this ValidatorContainer container, bool stopWhileFail) + { + if (container == null) + { + return null; + } + + Regex emailExpression = new Regex(@"[-|;|,|\/|\(|\)|\[|\]|\}|\{|%|@|\*|!|\']", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase); + if (emailExpression.IsMatch((container.ValidatorObject.ToString()))) + { + string errorMessage = string.Format("{0} contains illegal characters!", container.ValueName); + container.AddError(errorMessage); + if (stopWhileFail) + { + return null; + } + } + return container; + } + + /// + /// Check whether string contains dangerous characters that may be used in links + /// + /// + /// + /// + /// + public static ValidatorContainer IsSafeUserInfoString(this ValidatorContainer container, bool stopWhileFail) + { + if (container == null) + { + return null; + } + + Regex emailExpression = new Regex(@"^\s*$|^c:\\con\\con$|[%,\*" + "\"" + @"\s\t\<\>\&]|游客|管理员|^Guest|admin", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase); + if (emailExpression.IsMatch((container.ValidatorObject.ToString()))) + { + string errorMessage = string.Format("{0} contains illegal characters!", container.ValueName); + container.AddError(errorMessage); + if (stopWhileFail) + { + return null; + } + } + return container; + } + + public static ValidatorContainer IsValidateUserInfoName(this ValidatorContainer container, bool stopWhileFail) + { + if (container == null) + { + return null; + } + string userName = container.ValidatorObject.ToString(); + if (userName.IndexOf(" ") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1 || userName.IndexOf("") != -1) + { + container.AddError("Username cannot contain full-width spaces"); + if (stopWhileFail) + { + return null; + } + } + if (userName.IndexOf(" ") != -1) + { + container.AddError("Username cannot contain spaces"); + if (stopWhileFail) + { + return null; + } + } + if (userName.IndexOf(":") != -1) + { + container.AddError("Username cannot contain colon"); + if (stopWhileFail) + { + return null; + } + } + + string invalidateUserName = "`~!@#$%^&*()+-=;':\",./<>?|\\";//TODO:可以用正则判断 + foreach (var item in invalidateUserName) + { + if (userName.Contains(item)) + { + container.AddError("Username can contain Chinese/English letters, digits, and underscore _, but cannot contain special symbols: " + invalidateUserName); + if (stopWhileFail) + { + return null; + } + } + } + + return container; + } + + public static ValidatorContainer IsEmail(this ValidatorContainer container) + { + return IsEmail(container, true); + } + public static ValidatorContainer IsEmail(this ValidatorContainer container, bool stopWhileFail) + { + return Regex(container, + @"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$", + "Please enter a valid Email address in {0}!", stopWhileFail); + } + + public static ValidatorContainer IsMobile(this ValidatorContainer container, bool stopWhileFail) + { + //return Regex(container, @"^1[358]\d{9}$", "Please enter a valid phone number in {0}!", stopWhileFail); + //return Regex(container, @"^1[358]\d{9}$", "Please enter a valid phone number!", stopWhileFail); + + //电信手机号码正则 string dianxin = @"^1[3578][01379]\d{8}$"; Regex dReg = new Regex(dianxin); //联通手机号正则 string liantong = @"^1[34578][01256]\d{8}$"; Regex tReg = new Regex(liantong); //移动手机号正则 string yidong = @"^(134[012345678]\d{7}|1[34578][012356789]\d{8})$"; Regex yReg = new Regex(yidong); + + return Regex(container, @"^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$", "Please enter a valid phone number!", stopWhileFail); + } + + public static ValidatorContainer IsIPAddress(this ValidatorContainer container) + { + return IsIPAddress(container, true); + } + + public static ValidatorContainer IsIPAddress(this ValidatorContainer container, bool stopWhileFail) + { + return Regex(container, + @"(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])", + "Please enter a valid IP address in {0}!", stopWhileFail); + } + + public static ValidatorContainer IsAvailableUrl(this ValidatorContainer container, bool stopWhileFail) + { + return Regex(container, @"HTTP(S)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?", "Please enter a valid URL in {0}!", stopWhileFail); + } + + + public static ValidatorContainer MaxByte(this ValidatorContainer container, int maxByte) + { + return MaxByte(container, maxByte, true); + } + public static ValidatorContainer MaxByte(this ValidatorContainer container, int maxByte, bool stopWhileFail) + { + if (container == null) + { + return null; + } + + if (System.Text.Encoding.Default.GetByteCount(container.ValidatorObject.ToString()) > maxByte) + { + string errorMessage = string.Format("{0}中最多只能输入{1}字节的内容(对应{2}汉字)", container.ValueName, maxByte, Convert.ToInt32(maxByte / 2)); + container.AddError(errorMessage); + if (stopWhileFail) + { + return null; + } + } + return container; + } + + public static ValidatorContainer MaxLength(this ValidatorContainer container, int maxLength) + { + return MaxLength(container, maxLength, true); + } + public static ValidatorContainer MaxLength(this ValidatorContainer container, int maxLength, bool stopWhileFail) + { + if (container == null) + { + return null; + } + + if (container.ValidatorObject.ToString().Length > maxLength) + { + string errorMessage = string.Format("{0}中最多只能输入{1}个字符", container.ValueName, maxLength); + container.AddError(errorMessage); + if (stopWhileFail) + { + return null; + } + } + return container; + } + + + public static ValidatorContainer MinByte(this ValidatorContainer container, int minByte) + { + return MinByte(container, minByte, true); + } + public static ValidatorContainer MinByte(this ValidatorContainer container, int minByte, bool stopWhileFail) + { + if (container == null) + { + return null; + } + + if (System.Text.Encoding.Default.GetByteCount(container.ValidatorObject.ToString()) < minByte) + { + string errorMessage = string.Format("{0}中至少需要入{1}字节的内容(对应{2}汉字)", container.ValueName, minByte, Convert.ToInt32(minByte / 2)); + container.AddError(errorMessage); + if (stopWhileFail) + { + return null; + } + } + return container; + } + + + public static ValidatorContainer MinLength(this ValidatorContainer container, int minLength) + { + return MinLength(container, minLength, true); + } + public static ValidatorContainer MinLength(this ValidatorContainer container, int minLength, bool stopWhileFail) + { + if (container == null) + { + return null; + } + + if (container.ValidatorObject.ToString().Length < minLength) + { + string errorMessage = string.Format("{0}中至少需要输入{1}个字符", container.ValueName, minLength); + container.AddError(errorMessage); + if (stopWhileFail) + { + return null; + } + } + return container; + } + + + public static ValidatorContainer IsEqual(this ValidatorContainer container, T obj, string failMessageFormat, bool stopWhileFail) + { + if (container == null) + { + return null; + } + + if (!container.ValidatorObject.Equals(obj)) + { + string errorMessage = string.Format(failMessageFormat, container.ValueName); + container.AddError(errorMessage); + if (stopWhileFail) + { + return null; + } + } + return container; + } + + public static ValidatorContainer IsNotEqual(this ValidatorContainer container, T obj, string failMessageFormat, bool stopWhileFail) + { + if (container == null) + { + return null; + } + + if (container.ValidatorObject.Equals(obj)) + { + string errorMessage = string.Format(failMessageFormat, container.ValueName); + container.AddError(errorMessage); + if (stopWhileFail) + { + return null; + } + } + return container; + } + + public static ValidatorContainer Exclude(this ValidatorContainer container, string[] excludeStrings) + { + return Exclude(container, excludeStrings, true); + } + public static ValidatorContainer Exclude(this ValidatorContainer container, string[] excludeStrings, bool stopWhileFail) + { + if (container == null) + { + return null; + } + + foreach (var str in excludeStrings) + { + if (container.ValidatorObject.ToString().Contains(str)) + { + string charts = string.Join(",", excludeStrings).Replace(" ", "空格").Replace(" ", "全角空格"); + string errorMessage = string.Format("{0}中包含了不允许使用的特殊字符:{1}", container.ValueName, str); + container.AddError(errorMessage); + if (stopWhileFail) + { + return null; + } + } + return container; + } + return container; + } + + public static ValidatorContainer IsNumber(this ValidatorContainer container, float? min, float? max, bool stopWhileFail) + { + if (container == null) + { + return null; + } + min = min ?? float.MinValue; + max = max ?? float.MaxValue; + float tryFloat; + if (!float.TryParse(container.ValidatorObject.ToString(), out tryFloat) || tryFloat < (float)min || tryFloat > (float)max) + { + string errorMessage = string.Format("请输入正确的{0}", container.ValueName); + container.AddError(errorMessage); + if (stopWhileFail) + { + return null; + } + } + return container; + } + + /// + /// 校验验证码,建议Validator构造函数中的nullOrEmptyable为false + /// + /// + /// + /// + /// + public static ValidatorContainer ValidateCheckCode(this ValidatorContainer container, CheckCodeKind checkCodeKind, bool stopWhileFail) + { + if (container == null) + { + return null; + } + + if (container.ValidatorObject == null) + { + container.AddError("请输入验证码"); + if (stopWhileFail) + { + return null; + } + } + + CheckCodeHandle checkCodeHandle = new CheckCodeHandle(checkCodeKind, container.ValidatorEnvironment.HttpContext); + if (!checkCodeHandle.ValidateCheckCode(container.ValidatorObject.ToString())) + { + container.AddError("请输入正确的验证码"); + if (stopWhileFail) + { + return null; + } + } + + return container; + } + + + public static ValidatorContainer IsNull(this ValidatorContainer container, object obj, string failMessageFormat, bool stopWhileFail) + { + if (container == null) + { + return null; + } + + if (obj != null) + { + string errorMessage = string.Format(failMessageFormat, container.ValueName); + container.AddError(errorMessage); + if (stopWhileFail) + { + return null; + } + } + return container; + } + + public static ValidatorContainer IsNotNull(this ValidatorContainer container, object obj, string failMessageFormat, bool stopWhileFail) + { + if (container == null) + { + return null; + } + + if (obj == null) + { + string errorMessage = string.Format(failMessageFormat, container.ValueName); + container.AddError(errorMessage); + if (stopWhileFail) + { + return null; + } + } + return container; + } + } +} From b5adcf86767368763137dc1e7d2ad34e9bbe9d17 Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Thu, 2 Apr 2026 23:16:45 -0700 Subject: [PATCH 18/21] chore(i18n): translate Core comments batch 2 --- .../AssembleScan/AssembleScanHelper.cs | 362 ++++----- .../Cache/BaseCache/BaseCache.cs | 384 +++++----- .../BaseDictionary/BaseDictionaryCache.cs | 712 +++++++++--------- .../BaseCacheBindable.cs | 168 ++--- .../ObjectCacheStrategyExtensions.cs | 64 +- .../Cache/Interface/IBaseCache.cs | 86 +-- .../Senparc.Ncf.Core/Cache/MethodCache.cs | 248 +++--- .../Cache/QueueCache/DemoLoginKeyCache.cs | 136 ++-- .../Cache/QueueCache/OAuthCodeCache.cs | 114 +-- .../Cache/QueueCache/PhoneCheckCodeCache.cs | 108 +-- .../Cache/QueueCache/QrCodeGroupCache.cs | 142 ++-- .../Cache/QueueCache/QrCodeLoginCache.cs | 168 ++--- .../Cache/QueueCache/QrCodeRegCache.cs | 138 ++-- .../Cache/QueueCache/QueueCache.cs | 346 ++++----- .../Cache/QueueCache/WeixinCheckCodeCache.cs | 60 +- .../Senparc.Ncf.Core/Config/AIPluginHub.cs | 204 ++--- .../Senparc.Ncf.Core/Config/NcfCoreState.cs | 160 ++-- .../DI/AutoDITypeAttribute.cs | 2 +- src/Basic/Senparc.Ncf.Core/DI/IAutoDI.cs | 4 +- src/Basic/Senparc.Ncf.Core/Enums.Core.cs | 24 +- .../EventBus/EventBusExtensions.cs | 20 +- .../EventBus/EventBusHostedService.cs | 50 +- .../EventBus/InMemoryEventBus.cs | 30 +- .../Extensions/ObjectQueryExtensions.cs | 20 +- .../DatabaseConfigurationFactory.cs | 120 +-- .../SenparcEntitiesDbContextBase.cs | 532 ++++++------- src/Basic/Senparc.Ncf.Core/Register.cs | 298 ++++---- .../Utility/XmlDataContext.cs | 16 +- src/Basic/Senparc.Ncf.Core/VersionManager.cs | 164 ++-- 29 files changed, 2440 insertions(+), 2440 deletions(-) diff --git a/src/Basic/Senparc.Ncf.Core/AssembleScan/AssembleScanHelper.cs b/src/Basic/Senparc.Ncf.Core/AssembleScan/AssembleScanHelper.cs index 92f3c3947..0dbdbed0b 100644 --- a/src/Basic/Senparc.Ncf.Core/AssembleScan/AssembleScanHelper.cs +++ b/src/Basic/Senparc.Ncf.Core/AssembleScan/AssembleScanHelper.cs @@ -1,181 +1,181 @@ -using Senparc.CO2NET.Trace; -using Senparc.Ncf.Core.Config; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace Senparc.Ncf.Core.AssembleScan -{ - public static class AssembleScanHelper - { - /// - /// 所有扫描方法的集合 - /// - public static List ScanAssamblesActions { get; set; } = new List(); - - private static object _scanLock = new object(); - - /// - /// 添加扫描项目 - /// - /// 扫描过程 - /// 是否立即扫描 - /// 被包含的 dll 的文件名,“.Xncf.”会被必定包含在里面 - public static void AddAssembleScanItem(Action action, bool runScanNow, string[] dllFilePatterns = null) - { - ScanAssamblesActions.Add(new AssembleScanItem(action)); - - if (runScanNow) - { - RunScan(dllFilePatterns);//立即扫描 - } - } - - - /// - /// 执行扫描 - /// - /// 被包含的 dll 的文件名,“.Xncf.”会被必定包含在里面 - public static void RunScan(string[] dllFilePatterns) - { - var dt1 = SystemTime.Now; - - lock (_scanLock) - { - //查找所有扩展缓存B - var scanTypesCount = 0; - - var assemblies = GetAssembiles(dynamicLoadAllDlls: true, dllFilePatterns: dllFilePatterns); - var toScanItems = ScanAssamblesActions.Where(z => z.ScanFinished == false).ToList(); - - //搜索所有未被引用的项目 - - foreach (var assembly in assemblies) - { - try - { - scanTypesCount++; - foreach (var scanItem in toScanItems) - { - scanItem.Run(assembly);//执行扫描过程 - } - } - catch (Exception ex) - { - SenparcTrace.SendCustomLog("ScanAssambles() 自动扫描程序集报告(非程序异常):" + assembly.FullName, ex.ToString()); - } - } - - toScanItems.ForEach(z => z.Finished());//标记结束 - - var dt2 = SystemTime.Now; - SenparcTrace.SendCustomLog("ScanAssambles", $"RegisterAllAreas 用时:{(dt2 - dt1).TotalMilliseconds}ms"); - } - - - } - - #region 获取程序集 - - - private static List AllAssemblies = null; - private static object AllAssembliesLock = new object(); - - /// - /// 获取程序集 - /// - /// 是否从 dll 目录加载未被程序引用的其他程序集,默认为 true - /// 是否使用已缓存的数据,默认为true - /// 强制重新获取并更新缓存,此时会忽略 的设置 - /// 被包含的 dll 的文件名,“.Xncf.”会被必定包含在里面 - public static List GetAssembiles(bool dynamicLoadAllDlls = true, bool useCachedData = true, bool forceUpdateCache = true, string[] dllFilePatterns = null) - { - lock (AllAssembliesLock) - { - if (!forceUpdateCache && useCachedData && AllAssemblies != null) - { - return AllAssemblies; - } - - var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList(); - - #region 补全未被引用的程序集 - - if (dynamicLoadAllDlls) - { - // 使用 AppDomain 或环境变量来动态获取路径 - string directoryPath = AppDomain.CurrentDomain.BaseDirectory; - // 如果需要,也可以考虑使用环境变量 - // string directoryPath = Environment.GetEnvironmentVariable("MY_APP_PATH"); - - // 其余的步骤与之前相同,遍历和尝试加载 DLL - var loadedAssemblies = assemblies.Select(a => a.GetName().Name).ToList(); - - foreach (var filePath in Directory.GetFiles(directoryPath, "*.dll")) - { - try - { - var fileName = Path.GetFileName(filePath); - - List fileNamePatternList = new List() { ".Xncf." }; - if (dllFilePatterns != null) - { - fileNamePatternList.AddRange(dllFilePatterns); - } - - if (fileNamePatternList.Any(z => fileName.Contains(z, StringComparison.OrdinalIgnoreCase))) - { - var assemblyName = Path.GetFileNameWithoutExtension(fileName); - if (!loadedAssemblies.Contains(assemblyName)) - { - Assembly assembly = Assembly.LoadFrom(filePath); - assemblies.Add(assembly); - Console.WriteLine($"Dynamic Loaded: {assembly.FullName}"); - } - else - { - //获取当前文件dll版本 - // 获取文件版本信息 - FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(filePath); - - // 获取版本号 - string version = fvi.FileVersion; - - Console.WriteLine($"Already loaded: {assemblyName} ({version})"); - } - } - } - catch (Exception ex) - { - Console.WriteLine($"Error loading assembly from {filePath}: {ex.Message}"); - } - } - } - - - #endregion - - //更新缓存 - if (forceUpdateCache - || AllAssemblies == null - || assemblies.Count() > AllAssemblies.Count()) - { - AllAssemblies = assemblies; - } - - if (SiteConfig.NcfCoreState.DllFilePatterns == null) - { - SiteConfig.NcfCoreState.DllFilePatterns = dllFilePatterns?.ToList(); - } - - return assemblies; - } - } - - #endregion - } -} +using Senparc.CO2NET.Trace; +using Senparc.Ncf.Core.Config; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Senparc.Ncf.Core.AssembleScan +{ + public static class AssembleScanHelper + { + /// + /// Collection of all scan methods + /// + public static List ScanAssamblesActions { get; set; } = new List(); + + private static object _scanLock = new object(); + + /// + /// Add scan item + /// + /// Scan process + /// Whether to scan immediately + /// Included dll file names; ".Xncf." is always included + public static void AddAssembleScanItem(Action action, bool runScanNow, string[] dllFilePatterns = null) + { + ScanAssamblesActions.Add(new AssembleScanItem(action)); + + if (runScanNow) + { + RunScan(dllFilePatterns);//Scan immediately + } + } + + + /// + /// Execute scan + /// + /// Included dll file names; ".Xncf." is always included + public static void RunScan(string[] dllFilePatterns) + { + var dt1 = SystemTime.Now; + + lock (_scanLock) + { + //Find all extension cache B + var scanTypesCount = 0; + + var assemblies = GetAssembiles(dynamicLoadAllDlls: true, dllFilePatterns: dllFilePatterns); + var toScanItems = ScanAssamblesActions.Where(z => z.ScanFinished == false).ToList(); + + //Search all unreferenced items + + foreach (var assembly in assemblies) + { + try + { + scanTypesCount++; + foreach (var scanItem in toScanItems) + { + scanItem.Run(assembly);//Execute scan process + } + } + catch (Exception ex) + { + SenparcTrace.SendCustomLog("ScanAssambles() auto assembly scan report (non-program exception): " + assembly.FullName, ex.ToString()); + } + } + + toScanItems.ForEach(z => z.Finished());//Mark as finished + + var dt2 = SystemTime.Now; + SenparcTrace.SendCustomLog("ScanAssambles", $"RegisterAllAreas elapsed: {(dt2 - dt1).TotalMilliseconds}ms"); + } + + + } + + #region 获取程序集 + + + private static List AllAssemblies = null; + private static object AllAssembliesLock = new object(); + + /// + /// Get assemblies + /// + /// Whether to load unreferenced assemblies from dll directory, default true + /// Whether to use cached data, default true + /// Force refresh cache; ignores when true + /// Included dll file names; ".Xncf." is always included + public static List GetAssembiles(bool dynamicLoadAllDlls = true, bool useCachedData = true, bool forceUpdateCache = true, string[] dllFilePatterns = null) + { + lock (AllAssembliesLock) + { + if (!forceUpdateCache && useCachedData && AllAssemblies != null) + { + return AllAssemblies; + } + + var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList(); + + #region Complete unreferenced assemblies + + if (dynamicLoadAllDlls) + { + // 使用 AppDomain 或环境变量来动态获取路径 + string directoryPath = AppDomain.CurrentDomain.BaseDirectory; + // If needed, environment variables can also be used + // string directoryPath = Environment.GetEnvironmentVariable("MY_APP_PATH"); + + // The remaining steps are the same as before: iterate and try loading DLLs + var loadedAssemblies = assemblies.Select(a => a.GetName().Name).ToList(); + + foreach (var filePath in Directory.GetFiles(directoryPath, "*.dll")) + { + try + { + var fileName = Path.GetFileName(filePath); + + List fileNamePatternList = new List() { ".Xncf." }; + if (dllFilePatterns != null) + { + fileNamePatternList.AddRange(dllFilePatterns); + } + + if (fileNamePatternList.Any(z => fileName.Contains(z, StringComparison.OrdinalIgnoreCase))) + { + var assemblyName = Path.GetFileNameWithoutExtension(fileName); + if (!loadedAssemblies.Contains(assemblyName)) + { + Assembly assembly = Assembly.LoadFrom(filePath); + assemblies.Add(assembly); + Console.WriteLine($"Dynamic Loaded: {assembly.FullName}"); + } + else + { + //Get current DLL version for this file + // Get file version info + FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(filePath); + + // Get version number + string version = fvi.FileVersion; + + Console.WriteLine($"Already loaded: {assemblyName} ({version})"); + } + } + } + catch (Exception ex) + { + Console.WriteLine($"Error loading assembly from {filePath}: {ex.Message}"); + } + } + } + + + #endregion + + // Update cache + if (forceUpdateCache + || AllAssemblies == null + || assemblies.Count() > AllAssemblies.Count()) + { + AllAssemblies = assemblies; + } + + if (SiteConfig.NcfCoreState.DllFilePatterns == null) + { + SiteConfig.NcfCoreState.DllFilePatterns = dllFilePatterns?.ToList(); + } + + return assemblies; + } + } + + #endregion + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseCache.cs index 90c4b09d4..ddfab3d56 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseCache.cs @@ -1,192 +1,192 @@ -using Senparc.CO2NET; -using Senparc.CO2NET.Cache; -using Senparc.Ncf.Core.DI; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Utility; -using System; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Core.Cache -{ - public abstract class BaseCache : IBaseCache where T : class, new() - { - protected virtual bool UpdateToCache(string key, T obj) - { - Cache.Set(key, obj); - return true; - } - - protected virtual async Task UpdateToCacheAsync(string key, T obj) - { - await Cache.SetAsync(key, obj).ConfigureAwait(false); - return true; - } - - public BaseCache() { } - - public delegate void UpdateWithBataBase(T obj); - - protected INcfDbData _db; - - protected string CacheKey; - private T _data; - - public string CacheSetKey { get; private set; } - - public DateTime CacheTime { get; set; } - public DateTime CacheTimeOut { get; set; } - - //private ICacheStrategy _cache; - - /// - /// 缓存策略。 - /// 请尽量不要再BaseCache以外调用这个对象的方法,尤其Cache的Key在DictionaryCache中是会被重新定义的 - /// - public IBaseObjectCacheStrategy Cache { get; set; } - /// - /// 超时时间,1400分钟为1天。 - /// - public int TimeOut { get; set; } - - public BaseCache(string cacheKey) - : this(cacheKey, null) - { } - - public BaseCache(string cacheKey, INcfDbData db) - { - CacheKey = cacheKey; - - _db = db; - if (TimeOut == 0) - { - TimeOut = 1440; - } - - Cache = CacheStrategyFactory.GetObjectCacheStrategyInstance(); - this.CacheSetKey = cacheKey;//设置缓存集合键,必须提供 - } - - #region 同步方法 - - /// - /// Data不能在Update()方法中调用,否则会引发循环调用。Update()方法中应该使用SetData()方法 - /// Data只适用于简单类型,如果缓存类型为列表,则不适用 - /// - public virtual T Data - { - get - { - if (_data != null) - { - return _data; - } - - if (Cache == null) - { - var msg = "Cache==null! 系统调试记录cache一个bug(101)。"; - throw new Exception(msg); - } - - if (Cache.Get(CacheKey) == null) - { - _data = Update(); - } - return Cache.Get(CacheKey); - } - set => Cache.Set(CacheKey, value, TimeSpan.FromMinutes(TimeOut)); - } - - /// - /// 设置整个缓存数据 - /// - /// - /// - /// - public virtual void SetData(T value, int timeOut, UpdateWithBataBase updateWithDatabases) - { - Cache.Set(CacheKey, value, TimeSpan.FromMinutes(timeOut)); - - //记录缓存时间 - this.CacheTime = DateTime.Now; - this.CacheTimeOut = this.CacheTime.AddMinutes(timeOut); - - updateWithDatabases?.Invoke(value); - } - - public virtual void RemoveCache() - { - Cache.RemoveFromCache(CacheKey); - } - - public virtual T Update() - { - return null; - } - - public virtual void UpdateToDatabase(T obj) - { - } - - #endregion - - #region 异步方法 - /// - /// 获取全部缓存数据 - /// Data不能在Update()方法中调用,否则会引发循环调用。Update()方法中应该使用SetData()方法 - /// Data只适用于简单类型,如果缓存类型为列表,则不适用 - /// - public virtual async Task GetDataAsync() - { - if (_data != null) - { - return _data; - } - - if (Cache == null) - { - var msg = "Cache==null! 系统调试记录cache一个bug(101)。"; - throw new Exception(msg); - } - - if (await Cache.GetAsync(CacheKey).ConfigureAwait(false) == null) - { - _data = await UpdateAsync(); - } - return await Cache.GetAsync(CacheKey).ConfigureAwait(false); - } - - /// - /// 设置整个缓存数据 - /// - /// - /// - /// - public virtual async Task SetDataAsync(T value, int timeOut, UpdateWithBataBase updateWithDatabases) - { - await Cache.SetAsync(CacheKey, value, TimeSpan.FromMinutes(timeOut)).ConfigureAwait(false); - - //记录缓存时间 - this.CacheTime = DateTime.Now; - this.CacheTimeOut = this.CacheTime.AddMinutes(timeOut); - - updateWithDatabases?.Invoke(value); - } - - public virtual async Task RemoveCacheAsync() - { - await Cache.RemoveFromCacheAsync(CacheKey).ConfigureAwait(false); - } - - public virtual Task UpdateAsync() - { - return null; - } - - public virtual Task UpdateToDatabaseAsync(T obj) - { - return Task.CompletedTask; - } - - #endregion - } -} +using Senparc.CO2NET; +using Senparc.CO2NET.Cache; +using Senparc.Ncf.Core.DI; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Utility; +using System; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Core.Cache +{ + public abstract class BaseCache : IBaseCache where T : class, new() + { + protected virtual bool UpdateToCache(string key, T obj) + { + Cache.Set(key, obj); + return true; + } + + protected virtual async Task UpdateToCacheAsync(string key, T obj) + { + await Cache.SetAsync(key, obj).ConfigureAwait(false); + return true; + } + + public BaseCache() { } + + public delegate void UpdateWithBataBase(T obj); + + protected INcfDbData _db; + + protected string CacheKey; + private T _data; + + public string CacheSetKey { get; private set; } + + public DateTime CacheTime { get; set; } + public DateTime CacheTimeOut { get; set; } + + //private ICacheStrategy _cache; + + /// + /// 缓存策略。 + /// 请尽量不要再BaseCache以外调用这个对象的方法,尤其Cache的Key在DictionaryCache中是会被重新定义的 + /// + public IBaseObjectCacheStrategy Cache { get; set; } + /// + /// 超时时间,1400分钟为1天。 + /// + public int TimeOut { get; set; } + + public BaseCache(string cacheKey) + : this(cacheKey, null) + { } + + public BaseCache(string cacheKey, INcfDbData db) + { + CacheKey = cacheKey; + + _db = db; + if (TimeOut == 0) + { + TimeOut = 1440; + } + + Cache = CacheStrategyFactory.GetObjectCacheStrategyInstance(); + this.CacheSetKey = cacheKey;//设置缓存集合键,必须提供 + } + + #region Synchronous Methods + + /// + /// Data不能在Update()方法中调用,否则会引发循环调用。Update()方法中应该使用SetData()方法 + /// Data只适用于简单类型,如果缓存类型为列表,则不适用 + /// + public virtual T Data + { + get + { + if (_data != null) + { + return _data; + } + + if (Cache == null) + { + var msg = "Cache==null! 系统调试记录cache一个bug(101)。"; + throw new Exception(msg); + } + + if (Cache.Get(CacheKey) == null) + { + _data = Update(); + } + return Cache.Get(CacheKey); + } + set => Cache.Set(CacheKey, value, TimeSpan.FromMinutes(TimeOut)); + } + + /// + /// 设置整个缓存数据 + /// + /// + /// + /// + public virtual void SetData(T value, int timeOut, UpdateWithBataBase updateWithDatabases) + { + Cache.Set(CacheKey, value, TimeSpan.FromMinutes(timeOut)); + + // Record cache time + this.CacheTime = DateTime.Now; + this.CacheTimeOut = this.CacheTime.AddMinutes(timeOut); + + updateWithDatabases?.Invoke(value); + } + + public virtual void RemoveCache() + { + Cache.RemoveFromCache(CacheKey); + } + + public virtual T Update() + { + return null; + } + + public virtual void UpdateToDatabase(T obj) + { + } + + #endregion + + #region Asynchronous Methods + /// + /// 获取全部缓存数据 + /// Data不能在Update()方法中调用,否则会引发循环调用。Update()方法中应该使用SetData()方法 + /// Data只适用于简单类型,如果缓存类型为列表,则不适用 + /// + public virtual async Task GetDataAsync() + { + if (_data != null) + { + return _data; + } + + if (Cache == null) + { + var msg = "Cache==null! 系统调试记录cache一个bug(101)。"; + throw new Exception(msg); + } + + if (await Cache.GetAsync(CacheKey).ConfigureAwait(false) == null) + { + _data = await UpdateAsync(); + } + return await Cache.GetAsync(CacheKey).ConfigureAwait(false); + } + + /// + /// 设置整个缓存数据 + /// + /// + /// + /// + public virtual async Task SetDataAsync(T value, int timeOut, UpdateWithBataBase updateWithDatabases) + { + await Cache.SetAsync(CacheKey, value, TimeSpan.FromMinutes(timeOut)).ConfigureAwait(false); + + // Record cache time + this.CacheTime = DateTime.Now; + this.CacheTimeOut = this.CacheTime.AddMinutes(timeOut); + + updateWithDatabases?.Invoke(value); + } + + public virtual async Task RemoveCacheAsync() + { + await Cache.RemoveFromCacheAsync(CacheKey).ConfigureAwait(false); + } + + public virtual Task UpdateAsync() + { + return null; + } + + public virtual Task UpdateToDatabaseAsync(T obj) + { + return Task.CompletedTask; + } + + #endregion + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseDictionary/BaseDictionaryCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseDictionary/BaseDictionaryCache.cs index 835bbc6ac..ea8a09f6a 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseDictionary/BaseDictionaryCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseDictionary/BaseDictionaryCache.cs @@ -1,356 +1,356 @@ -using System; -using System.Collections.Generic; -using Senparc.Ncf.Core.Extensions; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Log; -using System.IO; -using Senparc.Ncf.Core.Utility; -using System.Threading.Tasks; -using Newtonsoft.Json.Linq; - -namespace Senparc.Ncf.Core.Cache -{ - public interface IBaseDictionaryCache : IBaseDictionaryCache - where TValue : class, new() - { - } - - public interface IBaseDictionaryCache : IBaseCache - where TValue : class, new() - { - #region 同步方法 - - TValue InsertObjectToCache(TKey key); - TValue InsertObjectToCache(TKey key, TEntity obj); - TValue GetObject(TKey key); - void RemoveObject(TKey key); - bool UpdateToCache(TKey key, TValue obj); - - #endregion - - #region 异步方法 - - Task InsertObjectToCacheAsync(TKey key); - Task InsertObjectToCacheAsync(TKey key, TEntity obj); - Task GetObjectAsync(TKey key); - Task RemoveObjectAsync(TKey key); - Task UpdateToCacheAsync(TKey key, TValue obj); - - #endregion - } - - - public abstract class BaseDictionaryCache : BaseDictionaryCache, - IBaseDictionaryCache - where TValue : class, new() - { - - /// - /// - /// - /// - /// - /// 单位:分钟。1440为一天。 - public BaseDictionaryCache(string CACHE_KEY, INcfDbData db, int timeOut) - : base(CACHE_KEY, db, timeOut) - { - base.TimeOut = timeOut; - } - } - - public abstract class BaseDictionaryCache : - BaseCache, IBaseDictionaryCache - where TValue : class, new() - where TEntity : class/*, new()*/ - { - /// - /// 获取缓存中最终的Key - /// - /// - /// - protected string GetFinalCacheKey(TKey key) - { - //TODO:判断Key类型 - TypeCode code = key == null ? TypeCode.DBNull : Type.GetTypeCode(key.GetType()); - string keyCode = null; - switch (code) - { - case TypeCode.DBNull: throw new Exception("Key不允许为空!"); - case TypeCode.String: - case TypeCode.Boolean: - case TypeCode.SByte: - case TypeCode.Byte: - case TypeCode.Int16: - case TypeCode.Int32: - case TypeCode.Int64: - case TypeCode.UInt16: - case TypeCode.UInt32: - case TypeCode.UInt64: - case TypeCode.Char: - case TypeCode.DateTime: - case TypeCode.Double: - case TypeCode.Single: - keyCode = key.ToString(); - break; - default: - code = TypeCode.Object; - string keyStr = System.Text.Json.JsonSerializer.Serialize(key); - keyCode = MD5.GetMD5Code(keyStr, "");//将对象序列化,然后拼接成字符串并转成MD5,确保唯一性。性能上可能会有一些损失,所以尽量不要太复杂的类型做Key - break; - } - - string finalKey = null; - - if (base.Cache is Senparc.CO2NET.Cache.CsRedis.BaseRedisObjectCacheStrategy) - { - finalKey = $"{keyCode}"; - } - else - { - finalKey = $"{CacheKey}@@@{keyCode}";//有的缓存策略可能不允许:作为分隔符[LocalCache 存在问题] - } - - return finalKey; - } - - //protected virtual ArraySegment SerializeObject(object value) - //{ - // using (var ms = new MemoryStream()) - // { - // //.NET 8.0 中已标记为过时且无法编译 - // new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Serialize(ms, value); - - // return new ArraySegment(ms.GetBuffer(), 0, (int)ms.Length); - // } - //} - - ///// - ///// 获取指定Key下所有的子Key的Value - ///// - ///// xxx* - ///// - //protected virtual IList GetObjectList(TKey key) - //{ - // if (key == null) - // { - // return null; - // } - // var finalCacheKey = GetFinalCacheKey(key); - // var allData = base.Cache.GetAll(); - - // if (allData.ContainsKey(finalCacheKey)) - // { - // return allData[finalCacheKey] - // } - // return - //} - - /// - /// - /// - /// - /// - /// 单位:分钟。1440为一天。 - public BaseDictionaryCache(string CACHE_KEY, INcfDbData db, int timeOut) - : base(CACHE_KEY, db) - { - base.TimeOut = timeOut; - - //强制初始化 - //var load = base.Data; - //var finalCacheKey = GetFinalCacheKey(key); - //if (base.Cache.CheckExisted(finalCacheKey)) - //{ - - //} - } - - #region 同步方法 - - public override TValue Update() - { - return base.Update(); - } - - public abstract TValue InsertObjectToCache(TKey key); - - public virtual TValue InsertObjectToCache(TKey key, TEntity obj) - { - if (obj == null) - { - return null; - } - - TValue fullObj = new TValue(); - var finalCacheKey = GetFinalCacheKey(key); - if (fullObj is IBaseFullEntity) - { - try - { - (fullObj as BaseFullEntity).CreateEntity(obj); - - //if (base.Data == null) - //{ - // throw new Exception("base.Data=null"); - //} - base.Cache.Set(finalCacheKey, fullObj); - return fullObj; - } - catch (Exception ex) - { - //var msg = "系统调试记录cache的一个bug。发生错误:{0}。当前参数:base.Data:{1}(Count:{4}),key:{2},obj:{3}。Null情况分别是:{4},{5},{6}" - // .With(ex.Message, base.Data, key, obj, base.Data == null, key == null, obj == null, base.Data.Count); - - var msg = $"系统调试记录cache的一个bug。发生错误:{ex.Message}。再次访问base.Data=null:{base.Data == null}";//实际上这里base.Data还是为null - LogUtility.SystemLogger.Debug(msg, ex); - throw new Exception(msg, ex); - } - } - else if (obj as TValue != null) - { - base.Cache.Set(finalCacheKey, obj as TValue); - return obj as TValue; - } - - base.Cache.Set(finalCacheKey, fullObj); - return fullObj; - } - - public virtual TValue GetObject(TKey key) - { - if (key == null) - { - return null; - } - - var finalCacheKey = GetFinalCacheKey(key); - - if (base.Cache.CheckExisted(finalCacheKey)) - { - return base.Cache.Get(finalCacheKey); - } - else - { - return InsertObjectToCache(key); - } - } - - public virtual void RemoveObject(TKey key) - { - var finalCacheKey = GetFinalCacheKey(key); - - if (base.Cache.CheckExisted(finalCacheKey)) - { - base.Cache.RemoveFromCache(finalCacheKey); - } - } - - public virtual bool UpdateToCache(TKey key, TValue obj) - { - var finalKey = GetFinalCacheKey(key); - return base.UpdateToCache(finalKey, obj); - } - - - public override void RemoveCache() - { - throw new Exception("不可以使用此方法"); - } - - #endregion - - #region 异步方法 - - public override async Task UpdateAsync() - { - return await base.UpdateAsync().ConfigureAwait(false); - } - - public abstract Task InsertObjectToCacheAsync(TKey key); - - public virtual async Task InsertObjectToCacheAsync(TKey key, TEntity obj) - { - if (obj == null) - { - return null; - } - - TValue fullObj = new TValue(); - var finalCacheKey = GetFinalCacheKey(key); - if (fullObj is IBaseFullEntity) - { - try - { - (fullObj as BaseFullEntity).CreateEntity(obj); - - //if (base.Data == null) - //{ - // throw new Exception("base.Data=null"); - //} - await base.Cache.SetAsync(finalCacheKey, fullObj).ConfigureAwait(false); - return fullObj; - } - catch (Exception ex) - { - //var msg = "系统调试记录cache的一个bug。发生错误:{0}。当前参数:base.Data:{1}(Count:{4}),key:{2},obj:{3}。Null情况分别是:{4},{5},{6}" - // .With(ex.Message, base.Data, key, obj, base.Data == null, key == null, obj == null, base.Data.Count); - - var msg = $"系统调试记录cache的一个bug。发生错误:{ex.Message}。再次访问base.Data=null:{base.Data == null}";//实际上这里base.Data还是为null - LogUtility.SystemLogger.Debug(msg, ex); - throw new Exception(msg, ex); - } - } - else if (obj as TValue != null) - { - await base.Cache.SetAsync(finalCacheKey, obj as TValue).ConfigureAwait(false); - return obj as TValue; - } - - await base.Cache.SetAsync(finalCacheKey, fullObj).ConfigureAwait(false); - return fullObj; - } - - public virtual async Task GetObjectAsync(TKey key) - { - if (key == null) - { - return null; - } - - var finalCacheKey = GetFinalCacheKey(key); - - if (base.Cache.CheckExisted(finalCacheKey)) - { - return await base.Cache.GetAsync(finalCacheKey).ConfigureAwait(false); - } - else - { - return await InsertObjectToCacheAsync(key).ConfigureAwait(false); - } - } - - public virtual async Task RemoveObjectAsync(TKey key) - { - var finalCacheKey = GetFinalCacheKey(key); - - if (await base.Cache.CheckExistedAsync(finalCacheKey).ConfigureAwait(false)) - { - await base.Cache.RemoveFromCacheAsync(finalCacheKey).ConfigureAwait(false); - } - } - - public virtual async Task UpdateToCacheAsync(TKey key, TValue obj) - { - var finalKey = GetFinalCacheKey(key); - return await base.UpdateToCacheAsync(finalKey, obj).ConfigureAwait(false); - } - - - public override Task RemoveCacheAsync() - { - throw new Exception("不可以使用此方法"); - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using Senparc.Ncf.Core.Extensions; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Log; +using System.IO; +using Senparc.Ncf.Core.Utility; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; + +namespace Senparc.Ncf.Core.Cache +{ + public interface IBaseDictionaryCache : IBaseDictionaryCache + where TValue : class, new() + { + } + + public interface IBaseDictionaryCache : IBaseCache + where TValue : class, new() + { + #region Synchronous Methods + + TValue InsertObjectToCache(TKey key); + TValue InsertObjectToCache(TKey key, TEntity obj); + TValue GetObject(TKey key); + void RemoveObject(TKey key); + bool UpdateToCache(TKey key, TValue obj); + + #endregion + + #region Asynchronous Methods + + Task InsertObjectToCacheAsync(TKey key); + Task InsertObjectToCacheAsync(TKey key, TEntity obj); + Task GetObjectAsync(TKey key); + Task RemoveObjectAsync(TKey key); + Task UpdateToCacheAsync(TKey key, TValue obj); + + #endregion + } + + + public abstract class BaseDictionaryCache : BaseDictionaryCache, + IBaseDictionaryCache + where TValue : class, new() + { + + /// + /// + /// + /// + /// + /// 单位:分钟。1440为一天。 + public BaseDictionaryCache(string CACHE_KEY, INcfDbData db, int timeOut) + : base(CACHE_KEY, db, timeOut) + { + base.TimeOut = timeOut; + } + } + + public abstract class BaseDictionaryCache : + BaseCache, IBaseDictionaryCache + where TValue : class, new() + where TEntity : class/*, new()*/ + { + /// + /// 获取缓存中最终的Key + /// + /// + /// + protected string GetFinalCacheKey(TKey key) + { + //TODO:判断Key类型 + TypeCode code = key == null ? TypeCode.DBNull : Type.GetTypeCode(key.GetType()); + string keyCode = null; + switch (code) + { + case TypeCode.DBNull: throw new Exception("Key不允许为空!"); + case TypeCode.String: + case TypeCode.Boolean: + case TypeCode.SByte: + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + case TypeCode.Char: + case TypeCode.DateTime: + case TypeCode.Double: + case TypeCode.Single: + keyCode = key.ToString(); + break; + default: + code = TypeCode.Object; + string keyStr = System.Text.Json.JsonSerializer.Serialize(key); + keyCode = MD5.GetMD5Code(keyStr, "");//将对象序列化,然后拼接成字符串并转成MD5,确保唯一性。性能上可能会有一些损失,所以尽量不要太复杂的类型做Key + break; + } + + string finalKey = null; + + if (base.Cache is Senparc.CO2NET.Cache.CsRedis.BaseRedisObjectCacheStrategy) + { + finalKey = $"{keyCode}"; + } + else + { + finalKey = $"{CacheKey}@@@{keyCode}";//有的缓存策略可能不允许:作为分隔符[LocalCache 存在问题] + } + + return finalKey; + } + + //protected virtual ArraySegment SerializeObject(object value) + //{ + // using (var ms = new MemoryStream()) + // { + // //.NET 8.0 中已标记为过时且无法编译 + // new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Serialize(ms, value); + + // return new ArraySegment(ms.GetBuffer(), 0, (int)ms.Length); + // } + //} + + ///// + ///// 获取指定Key下所有的子Key的Value + ///// + ///// xxx* + ///// + //protected virtual IList GetObjectList(TKey key) + //{ + // if (key == null) + // { + // return null; + // } + // var finalCacheKey = GetFinalCacheKey(key); + // var allData = base.Cache.GetAll(); + + // if (allData.ContainsKey(finalCacheKey)) + // { + // return allData[finalCacheKey] + // } + // return + //} + + /// + /// + /// + /// + /// + /// 单位:分钟。1440为一天。 + public BaseDictionaryCache(string CACHE_KEY, INcfDbData db, int timeOut) + : base(CACHE_KEY, db) + { + base.TimeOut = timeOut; + + //强制初始化 + //var load = base.Data; + //var finalCacheKey = GetFinalCacheKey(key); + //if (base.Cache.CheckExisted(finalCacheKey)) + //{ + + //} + } + + #region Synchronous Methods + + public override TValue Update() + { + return base.Update(); + } + + public abstract TValue InsertObjectToCache(TKey key); + + public virtual TValue InsertObjectToCache(TKey key, TEntity obj) + { + if (obj == null) + { + return null; + } + + TValue fullObj = new TValue(); + var finalCacheKey = GetFinalCacheKey(key); + if (fullObj is IBaseFullEntity) + { + try + { + (fullObj as BaseFullEntity).CreateEntity(obj); + + //if (base.Data == null) + //{ + // throw new Exception("base.Data=null"); + //} + base.Cache.Set(finalCacheKey, fullObj); + return fullObj; + } + catch (Exception ex) + { + //var msg = "System debug record cache a bug。发生错误:{0}。当前参数:base.Data:{1}(Count:{4}),key:{2},obj:{3}。Null情况分别是:{4},{5},{6}" + // .With(ex.Message, base.Data, key, obj, base.Data == null, key == null, obj == null, base.Data.Count); + + var msg = $"System debug record cache a bug。发生错误:{ex.Message}。再次访问base.Data=null:{base.Data == null}";//实际上这里base.Data还是为null + LogUtility.SystemLogger.Debug(msg, ex); + throw new Exception(msg, ex); + } + } + else if (obj as TValue != null) + { + base.Cache.Set(finalCacheKey, obj as TValue); + return obj as TValue; + } + + base.Cache.Set(finalCacheKey, fullObj); + return fullObj; + } + + public virtual TValue GetObject(TKey key) + { + if (key == null) + { + return null; + } + + var finalCacheKey = GetFinalCacheKey(key); + + if (base.Cache.CheckExisted(finalCacheKey)) + { + return base.Cache.Get(finalCacheKey); + } + else + { + return InsertObjectToCache(key); + } + } + + public virtual void RemoveObject(TKey key) + { + var finalCacheKey = GetFinalCacheKey(key); + + if (base.Cache.CheckExisted(finalCacheKey)) + { + base.Cache.RemoveFromCache(finalCacheKey); + } + } + + public virtual bool UpdateToCache(TKey key, TValue obj) + { + var finalKey = GetFinalCacheKey(key); + return base.UpdateToCache(finalKey, obj); + } + + + public override void RemoveCache() + { + throw new Exception("This method cannot be used"); + } + + #endregion + + #region Asynchronous Methods + + public override async Task UpdateAsync() + { + return await base.UpdateAsync().ConfigureAwait(false); + } + + public abstract Task InsertObjectToCacheAsync(TKey key); + + public virtual async Task InsertObjectToCacheAsync(TKey key, TEntity obj) + { + if (obj == null) + { + return null; + } + + TValue fullObj = new TValue(); + var finalCacheKey = GetFinalCacheKey(key); + if (fullObj is IBaseFullEntity) + { + try + { + (fullObj as BaseFullEntity).CreateEntity(obj); + + //if (base.Data == null) + //{ + // throw new Exception("base.Data=null"); + //} + await base.Cache.SetAsync(finalCacheKey, fullObj).ConfigureAwait(false); + return fullObj; + } + catch (Exception ex) + { + //var msg = "System debug record cache a bug。发生错误:{0}。当前参数:base.Data:{1}(Count:{4}),key:{2},obj:{3}。Null情况分别是:{4},{5},{6}" + // .With(ex.Message, base.Data, key, obj, base.Data == null, key == null, obj == null, base.Data.Count); + + var msg = $"System debug record cache a bug。发生错误:{ex.Message}。再次访问base.Data=null:{base.Data == null}";//实际上这里base.Data还是为null + LogUtility.SystemLogger.Debug(msg, ex); + throw new Exception(msg, ex); + } + } + else if (obj as TValue != null) + { + await base.Cache.SetAsync(finalCacheKey, obj as TValue).ConfigureAwait(false); + return obj as TValue; + } + + await base.Cache.SetAsync(finalCacheKey, fullObj).ConfigureAwait(false); + return fullObj; + } + + public virtual async Task GetObjectAsync(TKey key) + { + if (key == null) + { + return null; + } + + var finalCacheKey = GetFinalCacheKey(key); + + if (base.Cache.CheckExisted(finalCacheKey)) + { + return await base.Cache.GetAsync(finalCacheKey).ConfigureAwait(false); + } + else + { + return await InsertObjectToCacheAsync(key).ConfigureAwait(false); + } + } + + public virtual async Task RemoveObjectAsync(TKey key) + { + var finalCacheKey = GetFinalCacheKey(key); + + if (await base.Cache.CheckExistedAsync(finalCacheKey).ConfigureAwait(false)) + { + await base.Cache.RemoveFromCacheAsync(finalCacheKey).ConfigureAwait(false); + } + } + + public virtual async Task UpdateToCacheAsync(TKey key, TValue obj) + { + var finalKey = GetFinalCacheKey(key); + return await base.UpdateToCacheAsync(finalKey, obj).ConfigureAwait(false); + } + + + public override Task RemoveCacheAsync() + { + throw new Exception("This method cannot be used"); + } + + #endregion + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/BaseCacheBindable.cs b/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/BaseCacheBindable.cs index adc2b0b78..ba4338644 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/BaseCacheBindable.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/BaseCacheBindable.cs @@ -1,84 +1,84 @@ -using System; -using System.Runtime.CompilerServices; -using Senparc.CO2NET.Cache; -using Senparc.Ncf.Core.Entities; -using Senparc.Ncf.Log; - -namespace Senparc.Ncf.Core.Cache -{ - /// - /// 所有需要分布式缓存的实体基类 - /// - [Serializable] - public abstract class BaseCacheBindable : BindableBase where T : class, ICacheData, new() - { - ///// - ///// 缓存键 - ///// - //public abstract object Key { get; } - - protected string GenerateKey_Name { get; set; } - protected string GenerateKey_ActionName { get; set; } - - - protected void BaseCacheBindable_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) - { - if (sender == null) - { - LogUtility.Cache.ErrorFormat("BaseCacheBindable发生错误,不是BaseCacheBindable类型。当前参数类型:{0}", sender.GetType()); - return; - } - - var objCacheData = sender as ICacheData; - if (objCacheData == null) - { - LogUtility.Cache.ErrorFormat("BaseCacheBindable发生错误,没有实现ICacheData接口。当前参数类型:{0}", sender.GetType()); - return; - } - - if (objCacheData.Key == null) - { - LogUtility.Cache.ErrorFormat("BaseCacheBindable发生错误,Key为空。当前参数类型:{0}", sender.GetType()); - return; - } - - var mqKey = CacheDataMessageQueue.GenerateKey("SenparcCache", sender.GetType(), objCacheData.Key as string, "UpdateCache"); - - //获取对应Container的缓存相关 - - //加入消息列队,每过一段时间进行自动更新,防止属性连续被编辑,短时间内反复更新缓存。 - CacheDataMessageQueue mq = new CacheDataMessageQueue(); - mq.Add(mqKey, () => - { - var cacheStragegy = CacheStrategyFactory.GetObjectCacheStrategyInstance(); - //var cacheKey = objCacheData.Key; - objCacheData.CacheTime = DateTime.Now;//记录缓存时间 - cacheStragegy.Set(objCacheData.Key, objCacheData as T); - - //var cacheKey = ContainerHelper.GetCacheKey(this.GetType()); - //containerBag.CacheTime = DateTime.Now;//记录缓存时间 - //containerCacheStragegy.UpdateContainerBag(cacheKey, containerBag); - }); - } - - - /// - /// 设置Container属性 - /// - /// - /// - /// - /// - /// - protected bool SetContainerProperty(ref T storage, T value, [CallerMemberName] string propertyName = null) - { - var result = base.SetProperty(ref storage, value, propertyName); - return result; - } - - public BaseCacheBindable() - { - base.PropertyChanged += BaseCacheBindable_PropertyChanged; - } - } -} +using System; +using System.Runtime.CompilerServices; +using Senparc.CO2NET.Cache; +using Senparc.Ncf.Core.Entities; +using Senparc.Ncf.Log; + +namespace Senparc.Ncf.Core.Cache +{ + /// + /// 所有需要分布式缓存的实体基类 + /// + [Serializable] + public abstract class BaseCacheBindable : BindableBase where T : class, ICacheData, new() + { + ///// + ///// 缓存键 + ///// + //public abstract object Key { get; } + + protected string GenerateKey_Name { get; set; } + protected string GenerateKey_ActionName { get; set; } + + + protected void BaseCacheBindable_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (sender == null) + { + LogUtility.Cache.ErrorFormat("BaseCacheBindable发生错误,不是BaseCacheBindable类型。当前参数类型:{0}", sender.GetType()); + return; + } + + var objCacheData = sender as ICacheData; + if (objCacheData == null) + { + LogUtility.Cache.ErrorFormat("BaseCacheBindable发生错误,没有实现ICacheData接口。当前参数类型:{0}", sender.GetType()); + return; + } + + if (objCacheData.Key == null) + { + LogUtility.Cache.ErrorFormat("BaseCacheBindable发生错误,Key为空。当前参数类型:{0}", sender.GetType()); + return; + } + + var mqKey = CacheDataMessageQueue.GenerateKey("SenparcCache", sender.GetType(), objCacheData.Key as string, "UpdateCache"); + + //获取对应Container的缓存相关 + + //加入消息列队,每过一段时间进行自动更新,防止属性连续被编辑,短时间内反复更新缓存。 + CacheDataMessageQueue mq = new CacheDataMessageQueue(); + mq.Add(mqKey, () => + { + var cacheStragegy = CacheStrategyFactory.GetObjectCacheStrategyInstance(); + //var cacheKey = objCacheData.Key; + objCacheData.CacheTime = DateTime.Now;// Record cache time + cacheStragegy.Set(objCacheData.Key, objCacheData as T); + + //var cacheKey = ContainerHelper.GetCacheKey(this.GetType()); + //containerBag.CacheTime = DateTime.Now;// Record cache time + //containerCacheStragegy.UpdateContainerBag(cacheKey, containerBag); + }); + } + + + /// + /// 设置Container属性 + /// + /// + /// + /// + /// + /// + protected bool SetContainerProperty(ref T storage, T value, [CallerMemberName] string propertyName = null) + { + var result = base.SetProperty(ref storage, value, propertyName); + return result; + } + + public BaseCacheBindable() + { + base.PropertyChanged += BaseCacheBindable_PropertyChanged; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/Extensions/ObjectCacheStrategyExtensions.cs b/src/Basic/Senparc.Ncf.Core/Cache/Extensions/ObjectCacheStrategyExtensions.cs index ac8f55a4a..f21f4e7c1 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/Extensions/ObjectCacheStrategyExtensions.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/Extensions/ObjectCacheStrategyExtensions.cs @@ -1,32 +1,32 @@ -using Senparc.CO2NET.Cache; -using Senparc.CO2NET.Cache.CsRedis; -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Core.Cache.Extensions -{ - public static class ObjectCacheStrategyExtensions - { - public static IList GetAllByPrefix(this IBaseObjectCacheStrategy obj, string key) - { - if (obj is RedisObjectCacheStrategy) - { - RedisObjectCacheStrategy _obj = obj as RedisObjectCacheStrategy; - return _obj.GetAllByPrefix(key); - } - throw new Exception("未实现或缓存不支持前缀方式获取缓存!"); - } - - public static async Task> GetAllByPrefixAsync(this IBaseObjectCacheStrategy obj, string key) - { - if (obj is RedisObjectCacheStrategy) - { - var _obj = obj as RedisObjectCacheStrategy; - return await _obj.GetAllByPrefixAsync(key); - } - throw new Exception("未实现或缓存不支持前缀方式获取缓存!"); - } - } -} +using Senparc.CO2NET.Cache; +using Senparc.CO2NET.Cache.CsRedis; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Core.Cache.Extensions +{ + public static class ObjectCacheStrategyExtensions + { + public static IList GetAllByPrefix(this IBaseObjectCacheStrategy obj, string key) + { + if (obj is RedisObjectCacheStrategy) + { + RedisObjectCacheStrategy _obj = obj as RedisObjectCacheStrategy; + return _obj.GetAllByPrefix(key); + } + throw new Exception("Not implemented or cache does not support prefix access!"); + } + + public static async Task> GetAllByPrefixAsync(this IBaseObjectCacheStrategy obj, string key) + { + if (obj is RedisObjectCacheStrategy) + { + var _obj = obj as RedisObjectCacheStrategy; + return await _obj.GetAllByPrefixAsync(key); + } + throw new Exception("Not implemented or cache does not support prefix access!"); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/Interface/IBaseCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/Interface/IBaseCache.cs index b53fa427a..bc40bbfed 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/Interface/IBaseCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/Interface/IBaseCache.cs @@ -1,43 +1,43 @@ -using System; -using System.Threading.Tasks; -using Senparc.CO2NET.Cache; -using Senparc.Ncf.Core.DI; - -namespace Senparc.Ncf.Core.Cache -{ - public interface IBaseCache : IAutoDI - where T : class, new() - { - IBaseObjectCacheStrategy Cache { get; set; } - /// - /// Data不能在Update()方法中调用,否则会引发循环调用。Update()方法中应该使用SetData()方法 - /// - DateTime CacheTime { get; set; } - DateTime CacheTimeOut { get; set; } - - #region 同步方法 - T Data { get; set; } - void RemoveCache(); - void SetData(T value, int timeOut, BaseCache.UpdateWithBataBase updateWithDatabases); - T Update(); - void UpdateToDatabase(T obj); - #endregion - - #region 异步方法 - Task GetDataAsync(); - Task RemoveCacheAsync(); - Task SetDataAsync(T value, int timeOut, BaseCache.UpdateWithBataBase updateWithDatabases); - Task UpdateAsync(); - Task UpdateToDatabaseAsync(T obj); - #endregion - - - ///// - ///// 更新到缓存 - ///// - ///// - ///// - ///// - //bool UpdateToCache(string key, T obj); - } -} +using System; +using System.Threading.Tasks; +using Senparc.CO2NET.Cache; +using Senparc.Ncf.Core.DI; + +namespace Senparc.Ncf.Core.Cache +{ + public interface IBaseCache : IAutoDI + where T : class, new() + { + IBaseObjectCacheStrategy Cache { get; set; } + /// + /// Data不能在Update()方法中调用,否则会引发循环调用。Update()方法中应该使用SetData()方法 + /// + DateTime CacheTime { get; set; } + DateTime CacheTimeOut { get; set; } + + #region Synchronous Methods + T Data { get; set; } + void RemoveCache(); + void SetData(T value, int timeOut, BaseCache.UpdateWithBataBase updateWithDatabases); + T Update(); + void UpdateToDatabase(T obj); + #endregion + + #region Asynchronous Methods + Task GetDataAsync(); + Task RemoveCacheAsync(); + Task SetDataAsync(T value, int timeOut, BaseCache.UpdateWithBataBase updateWithDatabases); + Task UpdateAsync(); + Task UpdateToDatabaseAsync(T obj); + #endregion + + + ///// + ///// 更新到缓存 + ///// + ///// + ///// + ///// + //bool UpdateToCache(string key, T obj); + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/MethodCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/MethodCache.cs index 33add8337..0bb60fb7c 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/MethodCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/MethodCache.cs @@ -1,124 +1,124 @@ -using System; -using System.Threading.Tasks; -using Senparc.CO2NET.Cache; -using Senparc.CO2NET.Extensions; - -namespace Senparc.Ncf.Core.Cache -{ - public static class MethodCache - { - #region 同步方法 - - /// - /// 获取缓存 - /// - /// - /// - /// 当未命中缓存时存入并返回的结果 - /// - /// - public static T GetMethodCache(string cacheKey, Func func, int timeoutSeconds) where T : class - { - - cacheKey = cacheKey.ToUpper(); - - var cache = CacheStrategyFactory.GetObjectCacheStrategyInstance(); - - T result; - - if (!cache.CheckExisted(cacheKey)) - { - cache.Set(cacheKey, func(), //每次储存的是重新执行过的最新的结果 - TimeSpan.FromSeconds(timeoutSeconds)); - } - - result = cache.Get(cacheKey);//输出结果 - - return result; - } - - /// - /// 获取缓存,默认缓存时间为 60 分钟 - /// - /// - /// - /// 当未命中缓存时存入并返回的结果 - /// - public static T GetMethodCache(string cacheKey, Func func) where T : class - { - return GetMethodCache(cacheKey, func, 60 * 60); - } - - /// - /// 清除缓存 - /// - /// - public static void ClearMethodCache(string cacheKey) where T : class //虽然这边的T不需要传入,不过为了拿到CacheStrategy仍然需要提供 - { - cacheKey = cacheKey.ToUpper(); - - var cache = CacheStrategyFactory.GetObjectCacheStrategyInstance(); - - cache.RemoveFromCache(cacheKey); - - Console.WriteLine("Cache:" + cache.Get(cacheKey)?.ToJson(true)); - } - #endregion - - - #region 异步方法 - - /// - /// 获取缓存 - /// - /// - /// - /// 当未命中缓存时存入并返回的结果 - /// - /// - public static async Task GetMethodCacheAsync(string cacheKey, Func> func, int timeoutSeconds) where T : class - { - cacheKey = cacheKey.ToUpper(); - var cache = CacheStrategyFactory.GetObjectCacheStrategyInstance(); - T result; - - if (!await cache.CheckExistedAsync(cacheKey)) - { - var value = await func(); - await cache.SetAsync(cacheKey, value, TimeSpan.FromSeconds(timeoutSeconds)); - } - - result = await cache.GetAsync(cacheKey); - return result; - } - - /// - /// 获取缓存,默认缓存时间为 60 分钟 - /// - /// - /// - /// 当未命中缓存时存入并返回的结果 - /// - public static Task GetMethodCacheAsync(string cacheKey, Func> func) where T : class - { - return GetMethodCacheAsync(cacheKey, func, 60 * 60); - } - - /// - /// 清除缓存 - /// - /// - public static async Task ClearMethodCacheAsync(string cacheKey) where T : class - { - cacheKey = cacheKey.ToUpper(); - var cache = CacheStrategyFactory.GetObjectCacheStrategyInstance(); - await cache.RemoveFromCacheAsync(cacheKey); - var cacheResult = await cache.GetAsync(cacheKey); - Console.WriteLine("Cache:" + (cacheResult != null ? cacheResult.ToJson(true) : "null")); - } - - #endregion - - - } -} +using System; +using System.Threading.Tasks; +using Senparc.CO2NET.Cache; +using Senparc.CO2NET.Extensions; + +namespace Senparc.Ncf.Core.Cache +{ + public static class MethodCache + { + #region Synchronous Methods + + /// + /// 获取缓存 + /// + /// + /// + /// 当未命中缓存时存入并返回的结果 + /// + /// + public static T GetMethodCache(string cacheKey, Func func, int timeoutSeconds) where T : class + { + + cacheKey = cacheKey.ToUpper(); + + var cache = CacheStrategyFactory.GetObjectCacheStrategyInstance(); + + T result; + + if (!cache.CheckExisted(cacheKey)) + { + cache.Set(cacheKey, func(), //每次储存的是重新执行过的最新的结果 + TimeSpan.FromSeconds(timeoutSeconds)); + } + + result = cache.Get(cacheKey);//输出结果 + + return result; + } + + /// + /// 获取缓存,默认缓存时间为 60 分钟 + /// + /// + /// + /// 当未命中缓存时存入并返回的结果 + /// + public static T GetMethodCache(string cacheKey, Func func) where T : class + { + return GetMethodCache(cacheKey, func, 60 * 60); + } + + /// + /// 清除缓存 + /// + /// + public static void ClearMethodCache(string cacheKey) where T : class //虽然这边的T不需要传入,不过为了拿到CacheStrategy仍然需要提供 + { + cacheKey = cacheKey.ToUpper(); + + var cache = CacheStrategyFactory.GetObjectCacheStrategyInstance(); + + cache.RemoveFromCache(cacheKey); + + Console.WriteLine("Cache:" + cache.Get(cacheKey)?.ToJson(true)); + } + #endregion + + + #region Asynchronous Methods + + /// + /// 获取缓存 + /// + /// + /// + /// 当未命中缓存时存入并返回的结果 + /// + /// + public static async Task GetMethodCacheAsync(string cacheKey, Func> func, int timeoutSeconds) where T : class + { + cacheKey = cacheKey.ToUpper(); + var cache = CacheStrategyFactory.GetObjectCacheStrategyInstance(); + T result; + + if (!await cache.CheckExistedAsync(cacheKey)) + { + var value = await func(); + await cache.SetAsync(cacheKey, value, TimeSpan.FromSeconds(timeoutSeconds)); + } + + result = await cache.GetAsync(cacheKey); + return result; + } + + /// + /// 获取缓存,默认缓存时间为 60 分钟 + /// + /// + /// + /// 当未命中缓存时存入并返回的结果 + /// + public static Task GetMethodCacheAsync(string cacheKey, Func> func) where T : class + { + return GetMethodCacheAsync(cacheKey, func, 60 * 60); + } + + /// + /// 清除缓存 + /// + /// + public static async Task ClearMethodCacheAsync(string cacheKey) where T : class + { + cacheKey = cacheKey.ToUpper(); + var cache = CacheStrategyFactory.GetObjectCacheStrategyInstance(); + await cache.RemoveFromCacheAsync(cacheKey); + var cacheResult = await cache.GetAsync(cacheKey); + Console.WriteLine("Cache:" + (cacheResult != null ? cacheResult.ToJson(true) : "null")); + } + + #endregion + + + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/DemoLoginKeyCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/DemoLoginKeyCache.cs index f191755ef..75bc3d26f 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/DemoLoginKeyCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/DemoLoginKeyCache.cs @@ -1,68 +1,68 @@ -using System; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Core.Cache -{ - [Serializable] - public class DemoLoginKeyCacheData - { - public string OpenId { get; set; } - public DateTime AddTime { get; set; } - public QrCodeLoginDataType QrCodeLoginDataType { get; set; } - public DemoLoginKeyCacheData(string openId, QrCodeLoginDataType qrCodeLoginDataType) - { - OpenId = openId; - QrCodeLoginDataType = qrCodeLoginDataType; - AddTime = DateTime.Now; - } - } - - /// - /// 登录许可缓存(缓存数据:UserId) - /// - public interface IDemoLoginKeyCache : IQueueCache - { - - } - - [Serializable] - public class DemoLoginKeyCache : QueueCache, IDemoLoginKeyCache - { - private const string cacheKey = "DemoLoginKeyCache"; - private const int timeoutSeconds = 5 * 60; - private DemoLoginKeyCache() - : base(cacheKey, timeoutSeconds) - { - } - - public static async Task CreateAsync() - { - var instance = new DemoLoginKeyCache(); - - // 初始化父类中的异步初始化操作 - await instance.InitializeAsync(); - - return instance; - } - - private async Task InitializeAsync() - { - // 调用父类的静态工厂方法进行异步初始化 - var queueCache = await QueueCache.CreateAsync(cacheKey, timeoutSeconds); - - // 将初始化的结果赋值给当前实例 - this.MessageQueue = queueCache.MessageQueue; - this.MessageCollection = queueCache.MessageCollection; - } - - public override QueueCacheData Get(string key, bool removeDataWhenExist = true) - { - var value = base.Get(key,removeDataWhenExist); - if (value != null) - { - base.Remove(key);//一次性有效 - } - return value; - } - } -} +using System; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Core.Cache +{ + [Serializable] + public class DemoLoginKeyCacheData + { + public string OpenId { get; set; } + public DateTime AddTime { get; set; } + public QrCodeLoginDataType QrCodeLoginDataType { get; set; } + public DemoLoginKeyCacheData(string openId, QrCodeLoginDataType qrCodeLoginDataType) + { + OpenId = openId; + QrCodeLoginDataType = qrCodeLoginDataType; + AddTime = DateTime.Now; + } + } + + /// + /// 登录许可缓存(缓存数据:UserId) + /// + public interface IDemoLoginKeyCache : IQueueCache + { + + } + + [Serializable] + public class DemoLoginKeyCache : QueueCache, IDemoLoginKeyCache + { + private const string cacheKey = "DemoLoginKeyCache"; + private const int timeoutSeconds = 5 * 60; + private DemoLoginKeyCache() + : base(cacheKey, timeoutSeconds) + { + } + + public static async Task CreateAsync() + { + var instance = new DemoLoginKeyCache(); + + // 初始化父类中的异步初始化操作 + await instance.InitializeAsync(); + + return instance; + } + + private async Task InitializeAsync() + { + // 调用父类的静态工厂方法进行异步初始化 + var queueCache = await QueueCache.CreateAsync(cacheKey, timeoutSeconds); + + // 将初始化的结果赋值给当前实例 + this.MessageQueue = queueCache.MessageQueue; + this.MessageCollection = queueCache.MessageCollection; + } + + public override QueueCacheData Get(string key, bool removeDataWhenExist = true) + { + var value = base.Get(key,removeDataWhenExist); + if (value != null) + { + base.Remove(key); // One-time use + } + return value; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/OAuthCodeCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/OAuthCodeCache.cs index 7509a808c..0f4dcc4b3 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/OAuthCodeCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/OAuthCodeCache.cs @@ -1,57 +1,57 @@ -using System; - -namespace Senparc.Ncf.Core.Cache -{ - [Serializable] - public class OAuthCodeData - { - - public int AccountId { get; set; } - public int AppId { get; set; } - public string AppKey { get; set; } - public string AppSecret { get; set; } - - public OAuthCodeData(int accountId, int appId, string appKey, string appSecret) - { - AccountId = accountId; - AppId = appId; - AppKey = appKey; - AppSecret = appSecret; - } - } - - /// - /// 登录许可缓存(缓存数据:UserId) - /// - public interface IOAuthCodeCache : IQueueCache - { - - } - - [Serializable] - public class OAuthCodeCache : QueueCache, IOAuthCodeCache - { - private const string cacheKey = "OAuthCodeCache"; - private const int timeoutSeconds = 5 * 60; - public OAuthCodeCache() - : base(cacheKey, timeoutSeconds) - { - - } - - public override string CreateKey() - { - return Guid.NewGuid().ToString("n"); - } - - public override QueueCacheData Get(string key, bool removeDataWhenExist = true) - { - var value = base.Get(key, removeDataWhenExist); - if (value != null) - { - base.Remove(key);//一次性有效 - } - return value; - } - } -} +using System; + +namespace Senparc.Ncf.Core.Cache +{ + [Serializable] + public class OAuthCodeData + { + + public int AccountId { get; set; } + public int AppId { get; set; } + public string AppKey { get; set; } + public string AppSecret { get; set; } + + public OAuthCodeData(int accountId, int appId, string appKey, string appSecret) + { + AccountId = accountId; + AppId = appId; + AppKey = appKey; + AppSecret = appSecret; + } + } + + /// + /// 登录许可缓存(缓存数据:UserId) + /// + public interface IOAuthCodeCache : IQueueCache + { + + } + + [Serializable] + public class OAuthCodeCache : QueueCache, IOAuthCodeCache + { + private const string cacheKey = "OAuthCodeCache"; + private const int timeoutSeconds = 5 * 60; + public OAuthCodeCache() + : base(cacheKey, timeoutSeconds) + { + + } + + public override string CreateKey() + { + return Guid.NewGuid().ToString("n"); + } + + public override QueueCacheData Get(string key, bool removeDataWhenExist = true) + { + var value = base.Get(key, removeDataWhenExist); + if (value != null) + { + base.Remove(key); // One-time use + } + return value; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/PhoneCheckCodeCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/PhoneCheckCodeCache.cs index e4da88bf9..6eb84b9cd 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/PhoneCheckCodeCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/PhoneCheckCodeCache.cs @@ -1,54 +1,54 @@ -using System; - -namespace Senparc.Ncf.Core.Cache -{ - [Serializable] - public class PhoneCheckCodeData - { - public string Phone { get; set; } - - public PhoneCheckCodeData(string phone) - { - Phone = phone; - } - } - - ///// - ///// 手机验证码 - ///// - //public interface IPhoneCheckCodeCache : IQueueCache - //{ - - //} - - /// - /// 手机验证码 - /// - [Serializable] - public class PhoneCheckCodeCache : QueueCache/*, IPhoneCheckCodeCache*/ - { - private const string cacheKey = "PhoneCheckCodeCache"; - private const int timeoutSeconds = 10 * 60; - public PhoneCheckCodeCache() - : base(cacheKey, timeoutSeconds) - { - - } - - public override string CreateKey() - { - string key = new Random().Next(100000,999999).ToString("######"); - return key; - } - - public override QueueCacheData Get(string key, bool removeDataWhenExist = true) - { - var value = base.Get(key, removeDataWhenExist); - if (value != null) - { - base.Remove(key);//一次性有效 - } - return value; - } - } -} +using System; + +namespace Senparc.Ncf.Core.Cache +{ + [Serializable] + public class PhoneCheckCodeData + { + public string Phone { get; set; } + + public PhoneCheckCodeData(string phone) + { + Phone = phone; + } + } + + ///// + ///// 手机验证码 + ///// + //public interface IPhoneCheckCodeCache : IQueueCache + //{ + + //} + + /// + /// 手机验证码 + /// + [Serializable] + public class PhoneCheckCodeCache : QueueCache/*, IPhoneCheckCodeCache*/ + { + private const string cacheKey = "PhoneCheckCodeCache"; + private const int timeoutSeconds = 10 * 60; + public PhoneCheckCodeCache() + : base(cacheKey, timeoutSeconds) + { + + } + + public override string CreateKey() + { + string key = new Random().Next(100000,999999).ToString("######"); + return key; + } + + public override QueueCacheData Get(string key, bool removeDataWhenExist = true) + { + var value = base.Get(key, removeDataWhenExist); + if (value != null) + { + base.Remove(key); // One-time use + } + return value; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeGroupCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeGroupCache.cs index ce943afba..db26d0eb5 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeGroupCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeGroupCache.cs @@ -1,72 +1,72 @@ -using System; - -namespace Senparc.Ncf.Core.Cache.QueueCache -{ - public enum QrCodeGroupDataType - { - /// - /// 门派 - /// - Group, - } - - [Serializable] - public class QrCodeGroupData - { - /// - /// 即SceneId的字符串 - /// - public string Key { get; set; } - /// - /// GroupId - /// - public int SceneId { get; set; } - public string Ticket { get; set; } - public DateTime ExpireTime { get; set; } - - - public QrCodeGroupDataType QrCodeGroupDataType { get; set; } - - public QrCodeGroupData(int sceneId, int expireSeconds, string ticket, - QrCodeGroupDataType qrCodeGroupDataType) - { - SceneId = sceneId; - Key = sceneId.ToString(); - Ticket = ticket; - ExpireTime = DateTime.Now.AddSeconds(expireSeconds - 5); - //UpDataTime = DateTime.Now; - //AddTime = DateTime.Now; - QrCodeGroupDataType = qrCodeGroupDataType; - } - } - - /// - /// 门派许可缓存(缓存数据:GroupId) - /// - public interface IQrCodeGroupCache : IQueueCache - { - } - - [Serializable] - public class QrCodeGroupCache : QueueCache, IQrCodeGroupCache - { - private const string cacheKey = "QrCodeGroupCache"; - private const int timeoutSeconds = 604800; - - public QrCodeGroupCache() - : base(cacheKey, timeoutSeconds) - { - } - - public override string CreateKey() - { - throw new Exception("请在外部生成Key"); - } - - public override QueueCacheData Get(string key, bool removeDataWhenExist = true) - { - var value = base.Get(key, removeDataWhenExist); - return value; - } - } +using System; + +namespace Senparc.Ncf.Core.Cache.QueueCache +{ + public enum QrCodeGroupDataType + { + /// + /// 门派 + /// + Group, + } + + [Serializable] + public class QrCodeGroupData + { + /// + /// 即SceneId的字符串 + /// + public string Key { get; set; } + /// + /// GroupId + /// + public int SceneId { get; set; } + public string Ticket { get; set; } + public DateTime ExpireTime { get; set; } + + + public QrCodeGroupDataType QrCodeGroupDataType { get; set; } + + public QrCodeGroupData(int sceneId, int expireSeconds, string ticket, + QrCodeGroupDataType qrCodeGroupDataType) + { + SceneId = sceneId; + Key = sceneId.ToString(); + Ticket = ticket; + ExpireTime = DateTime.Now.AddSeconds(expireSeconds - 5); + //UpDataTime = DateTime.Now; + //AddTime = DateTime.Now; + QrCodeGroupDataType = qrCodeGroupDataType; + } + } + + /// + /// 门派许可缓存(缓存数据:GroupId) + /// + public interface IQrCodeGroupCache : IQueueCache + { + } + + [Serializable] + public class QrCodeGroupCache : QueueCache, IQrCodeGroupCache + { + private const string cacheKey = "QrCodeGroupCache"; + private const int timeoutSeconds = 604800; + + public QrCodeGroupCache() + : base(cacheKey, timeoutSeconds) + { + } + + public override string CreateKey() + { + throw new Exception("Please generate Key externally"); + } + + public override QueueCacheData Get(string key, bool removeDataWhenExist = true) + { + var value = base.Get(key, removeDataWhenExist); + return value; + } + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeLoginCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeLoginCache.cs index a5795f9c9..fb7b0f5d5 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeLoginCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeLoginCache.cs @@ -1,84 +1,84 @@ -using System; - -namespace Senparc.Ncf.Core.Cache -{ - public enum QrCodeLoginDataType - { - /// - /// 登陆 - /// - Login, - /// - /// Demo_Hockey_Player1 - /// - Demo_Hockey_P1, - /// - /// Demo_Hockey_Player1 - /// - Demo_Hockey_P2 - } - - [Serializable] - public class QrCodeLoginData - { - /// - /// 即SceneId的字符串 - /// - public string Key { get; set; } - public int SceneId { get; set; } - public string Ticket { get; set; } - public DateTime ExpireTime { get; set; } - public Guid LoginGuid { get; set; } - /// - /// 验证通过 - /// - public bool CheckPassed { get; set; } - public string UserName { get; set; } - public QrCodeLoginDataType QrCodeLoginDataType { get; set; } - - public QrCodeLoginData(int sceneId, int expireSeconds, string ticket, Guid loginGuid, QrCodeLoginDataType qrCodeLoginDataType) - { - SceneId = sceneId; - Key = sceneId.ToString(); - Ticket = ticket; - ExpireTime = DateTime.Now.AddSeconds(expireSeconds - 5); - LoginGuid = loginGuid; - QrCodeLoginDataType = qrCodeLoginDataType; - } - } - - /// - /// 登录许可缓存(缓存数据:UserId) - /// - public interface IQrCodeLoginCache : IQueueCache - { - - } - - [Serializable] - public class QrCodeLoginCache : QueueCache, IQrCodeLoginCache - { - private const string cacheKey = "QrCodeLoginCache"; - private const int timeoutSeconds = -1; - public QrCodeLoginCache() - : base(cacheKey, timeoutSeconds) - { - - } - - public override string CreateKey() - { - throw new Exception("请在外部生成Key"); - } - - public override QueueCacheData Get(string key, bool removeDataWhenExist = true) - { - var value = base.Get(key, removeDataWhenExist); - //if (value != null) - //{ - // base.Remove(key);//一次性有效 - //} - return value; - } - } -} +using System; + +namespace Senparc.Ncf.Core.Cache +{ + public enum QrCodeLoginDataType + { + /// + /// 登陆 + /// + Login, + /// + /// Demo_Hockey_Player1 + /// + Demo_Hockey_P1, + /// + /// Demo_Hockey_Player1 + /// + Demo_Hockey_P2 + } + + [Serializable] + public class QrCodeLoginData + { + /// + /// 即SceneId的字符串 + /// + public string Key { get; set; } + public int SceneId { get; set; } + public string Ticket { get; set; } + public DateTime ExpireTime { get; set; } + public Guid LoginGuid { get; set; } + /// + /// 验证通过 + /// + public bool CheckPassed { get; set; } + public string UserName { get; set; } + public QrCodeLoginDataType QrCodeLoginDataType { get; set; } + + public QrCodeLoginData(int sceneId, int expireSeconds, string ticket, Guid loginGuid, QrCodeLoginDataType qrCodeLoginDataType) + { + SceneId = sceneId; + Key = sceneId.ToString(); + Ticket = ticket; + ExpireTime = DateTime.Now.AddSeconds(expireSeconds - 5); + LoginGuid = loginGuid; + QrCodeLoginDataType = qrCodeLoginDataType; + } + } + + /// + /// 登录许可缓存(缓存数据:UserId) + /// + public interface IQrCodeLoginCache : IQueueCache + { + + } + + [Serializable] + public class QrCodeLoginCache : QueueCache, IQrCodeLoginCache + { + private const string cacheKey = "QrCodeLoginCache"; + private const int timeoutSeconds = -1; + public QrCodeLoginCache() + : base(cacheKey, timeoutSeconds) + { + + } + + public override string CreateKey() + { + throw new Exception("Please generate Key externally"); + } + + public override QueueCacheData Get(string key, bool removeDataWhenExist = true) + { + var value = base.Get(key, removeDataWhenExist); + //if (value != null) + //{ + // base.Remove(key); // One-time use + //} + return value; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeRegCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeRegCache.cs index 00f918430..9ca3ff4a5 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeRegCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeRegCache.cs @@ -1,69 +1,69 @@ -using System; - -namespace Senparc.Ncf.Core.Cache -{ - public enum QrCodeRegDataType - { - /// - /// 注册 - /// - Reg, - } - - [Serializable] - public class QrCodeRegData - { - /// - /// 即SceneId的字符串 - /// - public string Key { get; set; } - public int SceneId { get; set; } - public string Ticket { get; set; } - public DateTime ExpireTime { get; set; } - public Guid RegGuid { get; set; } - public string Code { get; set; } - public string OpenId { get; set; } - public QrCodeRegDataType QrCodeRegDataType { get; set; } - - public QrCodeRegData(int sceneId, int expireSeconds, string ticket, Guid regGuid, QrCodeRegDataType qrCodeRegDataType) - { - SceneId = sceneId; - Key = sceneId.ToString(); - Ticket = ticket; - ExpireTime = DateTime.Now.AddSeconds(expireSeconds - 5); - RegGuid = regGuid; - QrCodeRegDataType = qrCodeRegDataType; - } - } - - /// - /// 登录许可缓存(缓存数据:UserId) - /// - public interface IQrCodeRegCache : IQueueCache - { - - } - - [Serializable] - public class QrCodeRegCache : QueueCache, IQrCodeRegCache - { - private const string cacheKey = "QrCodeRegCache"; - private const int timeoutSeconds = -1; - public QrCodeRegCache() - : base(cacheKey, timeoutSeconds) - { - - } - - public override string CreateKey() - { - throw new Exception("请在外部生成Key"); - } - - public override QueueCacheData Get(string key, bool removeDataWhenExist = true) - { - var value = base.Get(key, removeDataWhenExist); - return value; - } - } -} +using System; + +namespace Senparc.Ncf.Core.Cache +{ + public enum QrCodeRegDataType + { + /// + /// 注册 + /// + Reg, + } + + [Serializable] + public class QrCodeRegData + { + /// + /// 即SceneId的字符串 + /// + public string Key { get; set; } + public int SceneId { get; set; } + public string Ticket { get; set; } + public DateTime ExpireTime { get; set; } + public Guid RegGuid { get; set; } + public string Code { get; set; } + public string OpenId { get; set; } + public QrCodeRegDataType QrCodeRegDataType { get; set; } + + public QrCodeRegData(int sceneId, int expireSeconds, string ticket, Guid regGuid, QrCodeRegDataType qrCodeRegDataType) + { + SceneId = sceneId; + Key = sceneId.ToString(); + Ticket = ticket; + ExpireTime = DateTime.Now.AddSeconds(expireSeconds - 5); + RegGuid = regGuid; + QrCodeRegDataType = qrCodeRegDataType; + } + } + + /// + /// 登录许可缓存(缓存数据:UserId) + /// + public interface IQrCodeRegCache : IQueueCache + { + + } + + [Serializable] + public class QrCodeRegCache : QueueCache, IQrCodeRegCache + { + private const string cacheKey = "QrCodeRegCache"; + private const int timeoutSeconds = -1; + public QrCodeRegCache() + : base(cacheKey, timeoutSeconds) + { + + } + + public override string CreateKey() + { + throw new Exception("Please generate Key externally"); + } + + public override QueueCacheData Get(string key, bool removeDataWhenExist = true) + { + var value = base.Get(key, removeDataWhenExist); + return value; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QueueCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QueueCache.cs index c48d442d4..86ff66ce0 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QueueCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QueueCache.cs @@ -1,173 +1,173 @@ -using Senparc.CO2NET.Extensions; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Core.Cache -{ - public interface IQueueCache - { - List> MessageQueue { get; set; } - Dictionary> MessageCollection { get; set; } - string CreateKey(); - /// - /// 插入缓存 - /// - /// - /// 如果为null将自动生成16位Guid - /// - string Insert(T obj, string key); - QueueCacheData Get(string key, bool removeDataWhenExist = true); - void Remove(string key); - } - - [Serializable] - public class QueueCache : IQueueCache - { - private string _cacheKey; - private int _timeoutSeconds; - public List> MessageQueue { get; set; } - public Dictionary> MessageCollection { get; set; } - - /// - /// - /// - /// - /// 小于等于0则没有过期时间 - protected QueueCache(string cacheKey, int timeoutSeconds) - { - _cacheKey = cacheKey; - _timeoutSeconds = timeoutSeconds; - } - - public static async Task> CreateAsync(string cacheKey, int timeoutSeconds) - { - var instance = new QueueCache(cacheKey, timeoutSeconds); - instance.MessageQueue = await MethodCache.GetMethodCacheAsync( - cacheKey + "Queue", - () => Task.FromResult(new List>()), - timeoutSeconds); - - instance.MessageCollection = await MethodCache.GetMethodCacheAsync( - cacheKey + "Collection", - () => Task.FromResult(new Dictionary>(StringComparer.OrdinalIgnoreCase)), - timeoutSeconds); - - return instance; - } - - /// - /// 获取MessageContext,如果不存在,返回null - /// 这个方法的更重要意义在于操作TM队列,及时移除过期信息,并将最新活动的对象移到尾部 - /// - /// 用户名(OpenId) - /// 是否清除key - /// - private QueueCacheData GetMessageContext(string key, bool removeDataWhenExist = true) - { - //检查并移除过期记录,为了尽量节约资源,这里暂不使用独立线程轮询 - while (MessageQueue.Count > 0) - { - var firstMessageContext = MessageQueue[0]; - var timeSpan = DateTime.Now - firstMessageContext.LastActiveTime; - if (removeDataWhenExist && _timeoutSeconds >= 0 && timeSpan.TotalSeconds >= _timeoutSeconds) - { - MessageQueue.RemoveAt(0);//从队列中移除过期对象 - MessageCollection.Remove(firstMessageContext.Key);//从集合中删除过期对象 - } - else - { - break; - } - } - - /* - * 全局只有在这里用到MessageCollection.ContainsKey - * 充分分离MessageCollection内部操作, - * 为以后变化或扩展MessageCollection留余地 - */ - if (!MessageCollection.ContainsKey(key)) - { - return null; - } - - return MessageCollection[key]; - } - - private QueueCacheData GetMessageContext_CreateIfNotExists(string key) - { - var messageContext = GetMessageContext(key); - - if (messageContext == null) - { - //全局只在这一个地方使用MessageCollection[Key]写入 - MessageCollection[key] = new QueueCacheData(key); - messageContext = GetMessageContext(key); - //插入列队 - MessageQueue.Add(messageContext); //最新的排到末尾 - } - return messageContext; - } - - public virtual string CreateKey() - { - return Guid.NewGuid().ToString("n").Substring(0, 16); - } - - public virtual string Insert(T obj, string key) - { - //检查并移除过期记录,为了尽量节约资源,这里暂不使用独立线程轮询 - //while (MessageQueue.Count > 0) - //{ - // var firstMessageContext = MessageQueue[0]; - // var timeSpan = DateTime.Now - firstMessageContext.LastActiveTime; - // if (timeSpan.TotalSeconds >= _timeoutSeconds) - // { - // MessageQueue.RemoveAt(0);//从队列中移除过期对象 - // MessageCollection.Remove(firstMessageContext.Key);//从集合中删除过期对象 - // } - // else - // { - // break; - // } - //} - - while (key.IsNullOrEmpty() || MessageCollection.ContainsKey(key)) - { - key = CreateKey(); - } - - var messageContext = GetMessageContext_CreateIfNotExists(key); - messageContext.Data = obj; - - var messageContextInQueue = MessageQueue.IndexOf(messageContext); - if (MessageQueue.Count > 1 && messageContextInQueue != MessageQueue.Count - 1) - { - //如果不是新建的对象,把当前对象移到队列尾部(新对象已经在底部) - MessageQueue.RemoveAt(messageContextInQueue); //移除当前对象 - MessageQueue.Add(messageContext); //插入到末尾 - } - - messageContext.LastActiveTime = DateTime.Now;//记录请求时间 - return key; - } - - public virtual QueueCacheData Get(string key, bool removeDataWhenExist = true) - { - return GetMessageContext(key, removeDataWhenExist); - } - - public virtual void Remove(string key) - { - try - { - var messageContext = GetMessageContext_CreateIfNotExists(key); - MessageQueue.Remove(messageContext); - MessageCollection.Remove(key); - } - catch (Exception) - { - } - } - } -} +using Senparc.CO2NET.Extensions; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Core.Cache +{ + public interface IQueueCache + { + List> MessageQueue { get; set; } + Dictionary> MessageCollection { get; set; } + string CreateKey(); + /// + /// 插入缓存 + /// + /// + /// 如果为null将自动生成16位Guid + /// + string Insert(T obj, string key); + QueueCacheData Get(string key, bool removeDataWhenExist = true); + void Remove(string key); + } + + [Serializable] + public class QueueCache : IQueueCache + { + private string _cacheKey; + private int _timeoutSeconds; + public List> MessageQueue { get; set; } + public Dictionary> MessageCollection { get; set; } + + /// + /// + /// + /// + /// 小于等于0则没有过期时间 + protected QueueCache(string cacheKey, int timeoutSeconds) + { + _cacheKey = cacheKey; + _timeoutSeconds = timeoutSeconds; + } + + public static async Task> CreateAsync(string cacheKey, int timeoutSeconds) + { + var instance = new QueueCache(cacheKey, timeoutSeconds); + instance.MessageQueue = await MethodCache.GetMethodCacheAsync( + cacheKey + "Queue", + () => Task.FromResult(new List>()), + timeoutSeconds); + + instance.MessageCollection = await MethodCache.GetMethodCacheAsync( + cacheKey + "Collection", + () => Task.FromResult(new Dictionary>(StringComparer.OrdinalIgnoreCase)), + timeoutSeconds); + + return instance; + } + + /// + /// 获取MessageContext,如果不存在,返回null + /// 这个方法的更重要意义在于操作TM队列,及时移除过期信息,并将最新活动的对象移到尾部 + /// + /// 用户名(OpenId) + /// 是否清除key + /// + private QueueCacheData GetMessageContext(string key, bool removeDataWhenExist = true) + { + // Check and remove expired records. Temporary no standalone thread polling to save resources + while (MessageQueue.Count > 0) + { + var firstMessageContext = MessageQueue[0]; + var timeSpan = DateTime.Now - firstMessageContext.LastActiveTime; + if (removeDataWhenExist && _timeoutSeconds >= 0 && timeSpan.TotalSeconds >= _timeoutSeconds) + { + MessageQueue.RemoveAt(0);//从队列中移除过期对象 + MessageCollection.Remove(firstMessageContext.Key);//从集合中删除过期对象 + } + else + { + break; + } + } + + /* + * 全局只有在这里用到MessageCollection.ContainsKey + * 充分分离MessageCollection内部操作, + * 为以后变化或扩展MessageCollection留余地 + */ + if (!MessageCollection.ContainsKey(key)) + { + return null; + } + + return MessageCollection[key]; + } + + private QueueCacheData GetMessageContext_CreateIfNotExists(string key) + { + var messageContext = GetMessageContext(key); + + if (messageContext == null) + { + //全局只在这一个地方使用MessageCollection[Key]写入 + MessageCollection[key] = new QueueCacheData(key); + messageContext = GetMessageContext(key); + //插入列队 + MessageQueue.Add(messageContext); //最新的排到末尾 + } + return messageContext; + } + + public virtual string CreateKey() + { + return Guid.NewGuid().ToString("n").Substring(0, 16); + } + + public virtual string Insert(T obj, string key) + { + // Check and remove expired records. Temporary no standalone thread polling to save resources + //while (MessageQueue.Count > 0) + //{ + // var firstMessageContext = MessageQueue[0]; + // var timeSpan = DateTime.Now - firstMessageContext.LastActiveTime; + // if (timeSpan.TotalSeconds >= _timeoutSeconds) + // { + // MessageQueue.RemoveAt(0);//从队列中移除过期对象 + // MessageCollection.Remove(firstMessageContext.Key);//从集合中删除过期对象 + // } + // else + // { + // break; + // } + //} + + while (key.IsNullOrEmpty() || MessageCollection.ContainsKey(key)) + { + key = CreateKey(); + } + + var messageContext = GetMessageContext_CreateIfNotExists(key); + messageContext.Data = obj; + + var messageContextInQueue = MessageQueue.IndexOf(messageContext); + if (MessageQueue.Count > 1 && messageContextInQueue != MessageQueue.Count - 1) + { + //如果不是新建的对象,把当前对象移到队列尾部(新对象已经在底部) + MessageQueue.RemoveAt(messageContextInQueue); //移除当前对象 + MessageQueue.Add(messageContext); //插入到末尾 + } + + messageContext.LastActiveTime = DateTime.Now;//记录请求时间 + return key; + } + + public virtual QueueCacheData Get(string key, bool removeDataWhenExist = true) + { + return GetMessageContext(key, removeDataWhenExist); + } + + public virtual void Remove(string key) + { + try + { + var messageContext = GetMessageContext_CreateIfNotExists(key); + MessageQueue.Remove(messageContext); + MessageCollection.Remove(key); + } + catch (Exception) + { + } + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/WeixinCheckCodeCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/WeixinCheckCodeCache.cs index 6f75fa9b0..b266d7bd3 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/WeixinCheckCodeCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/WeixinCheckCodeCache.cs @@ -1,30 +1,30 @@ -using System; - -namespace Senparc.Ncf.Core.Cache.QueueCache -{ - //public interface IWeixinCheckCodeCache : IQueueCache> - //{ - //} - - [Serializable] - public class WeixinCheckCodeCache : QueueCache>/*, IWeixinCheckCodeCache*/ - { - private const string cacheKey = "WeixinCheckCodeCache"; - private const int timeoutSeconds = 3 * 60; - public WeixinCheckCodeCache() - : base(cacheKey, timeoutSeconds) - { - - } - - public override QueueCacheData> Get(string key, bool removeDataWhenExist = true) - { - var value = base.Get(key, removeDataWhenExist); - if (value != null) - { - base.Remove(key);//一次性有效 - } - return value; - } - } -} +using System; + +namespace Senparc.Ncf.Core.Cache.QueueCache +{ + //public interface IWeixinCheckCodeCache : IQueueCache> + //{ + //} + + [Serializable] + public class WeixinCheckCodeCache : QueueCache>/*, IWeixinCheckCodeCache*/ + { + private const string cacheKey = "WeixinCheckCodeCache"; + private const int timeoutSeconds = 3 * 60; + public WeixinCheckCodeCache() + : base(cacheKey, timeoutSeconds) + { + + } + + public override QueueCacheData> Get(string key, bool removeDataWhenExist = true) + { + var value = base.Get(key, removeDataWhenExist); + if (value != null) + { + base.Remove(key); // One-time use + } + return value; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Config/AIPluginHub.cs b/src/Basic/Senparc.Ncf.Core/Config/AIPluginHub.cs index d9b057144..077da37ea 100644 --- a/src/Basic/Senparc.Ncf.Core/Config/AIPluginHub.cs +++ b/src/Basic/Senparc.Ncf.Core/Config/AIPluginHub.cs @@ -1,102 +1,102 @@ -using Swashbuckle.AspNetCore.SwaggerGen; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -#nullable enable -namespace Senparc.Ncf.Core -{ - /// - /// 注册 AI 插件 - /// - public class AIPluginHub - { - #region 单例 - - AIPluginHub() { } - - /// - /// AIPluginHub 的全局单例 - /// - public static AIPluginHub Instance - { - get - { - return Nested.instance; - } - } - - class Nested - { - static Nested() { } - - internal static readonly AIPluginHub instance = new AIPluginHub(); - } - - #endregion - - - private readonly Dictionary _types = new Dictionary(); - - - /// - /// 添加 Plugin 类型 - /// - /// - - public void Add(Type pluginType) - { - var key = pluginType.FullName; - if (_types.ContainsKey(key)) - { - return; - } - _types[key] = pluginType; - } - - /// - /// 获取 Plugin 类型 - /// - /// - /// - /// - public Type? GetPluginType(string fullName, bool patternSearch = false) - { - if (!patternSearch) - { - if (_types.ContainsKey(fullName)) - { - return _types[fullName]; - } - else - { - return null; - } - } - else - { - var regex = new System.Text.RegularExpressions.Regex(fullName); - foreach (var key in _types.Keys) - { - if (regex.Match(key).Success) - { - return _types[key]; - } - } - return null; - } - } - - /// - /// 获取所有 Plugin 类型 - /// - /// - public List GetAllPluginNames() - { - return _types.Values.Select(z=>z.FullName).OrderBy(z => z).ToList(); - } - } -} +using Swashbuckle.AspNetCore.SwaggerGen; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +#nullable enable +namespace Senparc.Ncf.Core +{ + /// + /// 注册 AI 插件 + /// + public class AIPluginHub + { + #region Singleton + + AIPluginHub() { } + + /// + /// AIPluginHub 的全局单例 + /// + public static AIPluginHub Instance + { + get + { + return Nested.instance; + } + } + + class Nested + { + static Nested() { } + + internal static readonly AIPluginHub instance = new AIPluginHub(); + } + + #endregion + + + private readonly Dictionary _types = new Dictionary(); + + + /// + /// 添加 Plugin 类型 + /// + /// + + public void Add(Type pluginType) + { + var key = pluginType.FullName; + if (_types.ContainsKey(key)) + { + return; + } + _types[key] = pluginType; + } + + /// + /// 获取 Plugin 类型 + /// + /// + /// + /// + public Type? GetPluginType(string fullName, bool patternSearch = false) + { + if (!patternSearch) + { + if (_types.ContainsKey(fullName)) + { + return _types[fullName]; + } + else + { + return null; + } + } + else + { + var regex = new System.Text.RegularExpressions.Regex(fullName); + foreach (var key in _types.Keys) + { + if (regex.Match(key).Success) + { + return _types[key]; + } + } + return null; + } + } + + /// + /// 获取所有 Plugin 类型 + /// + /// + public List GetAllPluginNames() + { + return _types.Values.Select(z=>z.FullName).OrderBy(z => z).ToList(); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Config/NcfCoreState.cs b/src/Basic/Senparc.Ncf.Core/Config/NcfCoreState.cs index 01cc02a3d..d9032b27e 100644 --- a/src/Basic/Senparc.Ncf.Core/Config/NcfCoreState.cs +++ b/src/Basic/Senparc.Ncf.Core/Config/NcfCoreState.cs @@ -1,80 +1,80 @@ -using Senparc.Ncf.Utility.Helpers; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Config -{ - /// - /// NCF 系统状态 - /// - public record class NcfCoreState - { - #region 单例 - - NcfCoreState() { } - - /// - /// DatabaseConfigurationFactory 的全局单例 - /// - public static NcfCoreState Instance - { - get - { - return Nested.instance; - } - } - - class Nested - { - static Nested() { } - - internal static readonly NcfCoreState instance = new NcfCoreState(); - } - - #endregion - - /// - /// 已经完成所有 AddXncfModule 的应用 - /// - public bool AllAddXncfModuleApplied { get; set; } - /// - /// 已经完成所有 UseXncfModuleApplied 的应用 - /// - public bool AllUseXncfModuleApplied { get; set; } - /// - /// 已经完成所有 AuthorizeConfig 的应用,即所有 Area 的权限配置已经完成,以及所有 IAreaRegister 接口已经执行完成 - /// - public bool AllAuthorizeConfigApplied { get; set; } - - /// - /// 任意一个 XNCF 模块的数据库注册已经载入 - /// - public bool AynDatabaseXncfLoaded { get; set; } - /// - /// 所有 XNCF 模块的数据库注册已经载入 - /// - public bool AllDatabaseXncfLoaded { get; set; } - - /// - /// 任意一个 XNCF 模块的线程注册已经载入 - /// - public bool AnyThreadXncfLoaded { get; set; } - /// - /// 所有 XNCF 模块的线程注册已经载入 - /// - public bool AllThreadXncfLoaded { get; set; } - - /// - /// XNCF 模块的 AddXncfDatabaseModule 方法已经载入(任意一个) - /// - public bool AnyAddXncfDatabaseModuleApplied { get; set; } - - /// - /// 被包含的 dll 的文件名,“.Xncf.”会被必定包含在里面 - /// - public List DllFilePatterns { get; set; } - - public SystemLanguage SystemLanguage => GlobalCulture.CurrentLanguage; - } -} +using Senparc.Ncf.Utility.Helpers; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Config +{ + /// + /// NCF system state + /// + public record class NcfCoreState + { + #region Singleton + + NcfCoreState() { } + + /// + /// Global singleton for DatabaseConfigurationFactory + /// + public static NcfCoreState Instance + { + get + { + return Nested.instance; + } + } + + class Nested + { + static Nested() { } + + internal static readonly NcfCoreState instance = new NcfCoreState(); + } + + #endregion + + /// + /// Indicates all AddXncfModule operations have completed + /// + public bool AllAddXncfModuleApplied { get; set; } + /// + /// Indicates all UseXncfModuleApplied operations have completed + /// + public bool AllUseXncfModuleApplied { get; set; } + /// + /// Indicates all AuthorizeConfig operations are complete, including Area permission setup and IAreaRegister execution + /// + public bool AllAuthorizeConfigApplied { get; set; } + + /// + /// Indicates database registration for any XNCF module has been loaded + /// + public bool AynDatabaseXncfLoaded { get; set; } + /// + /// Indicates database registrations for all XNCF modules have been loaded + /// + public bool AllDatabaseXncfLoaded { get; set; } + + /// + /// Indicates thread registration for any XNCF module has been loaded + /// + public bool AnyThreadXncfLoaded { get; set; } + /// + /// Indicates thread registrations for all XNCF modules have been loaded + /// + public bool AllThreadXncfLoaded { get; set; } + + /// + /// Indicates AddXncfDatabaseModule has been loaded for at least one XNCF module + /// + public bool AnyAddXncfDatabaseModuleApplied { get; set; } + + /// + /// Included DLL file-name patterns. ".Xncf." is always included. + /// + public List DllFilePatterns { get; set; } + + public SystemLanguage SystemLanguage => GlobalCulture.CurrentLanguage; + } +} diff --git a/src/Basic/Senparc.Ncf.Core/DI/AutoDITypeAttribute.cs b/src/Basic/Senparc.Ncf.Core/DI/AutoDITypeAttribute.cs index f8cf9fe2e..74fd8ee60 100644 --- a/src/Basic/Senparc.Ncf.Core/DI/AutoDITypeAttribute.cs +++ b/src/Basic/Senparc.Ncf.Core/DI/AutoDITypeAttribute.cs @@ -7,7 +7,7 @@ namespace Senparc.Ncf.Core.DI { /// - /// 自动依赖注入特性标签 + /// Attribute marker for automatic dependency injection /// public class AutoDITypeAttribute : Attribute { diff --git a/src/Basic/Senparc.Ncf.Core/DI/IAutoDI.cs b/src/Basic/Senparc.Ncf.Core/DI/IAutoDI.cs index e5d91cd98..8ccd134c6 100644 --- a/src/Basic/Senparc.Ncf.Core/DI/IAutoDI.cs +++ b/src/Basic/Senparc.Ncf.Core/DI/IAutoDI.cs @@ -6,8 +6,8 @@ namespace Senparc.Ncf.Core.DI { /// - /// 所有需要自动扫描进行依赖注入的基础接口 - /// 默认 DI 使用 AddScoped 方法,如果需要强制使用其他方法,请在子类上使用 [AutoDIType(typeName)] 特性标签 + /// Base interface for all types that require auto-scanned dependency injection + /// By default, DI uses AddScoped. To force a different lifecycle, add the [AutoDIType(typeName)] attribute on the implementation class. /// public interface IAutoDI { diff --git a/src/Basic/Senparc.Ncf.Core/Enums.Core.cs b/src/Basic/Senparc.Ncf.Core/Enums.Core.cs index 02cfc38c7..acd10b876 100644 --- a/src/Basic/Senparc.Ncf.Core/Enums.Core.cs +++ b/src/Basic/Senparc.Ncf.Core/Enums.Core.cs @@ -1,7 +1,7 @@ namespace Senparc.Ncf.Core.Enums { /// - /// 消息类型(级别) + /// Message type (level) /// public enum MessageType { @@ -13,7 +13,7 @@ public enum MessageType /// - /// Email账户 + /// Email account /// public enum EmailAccountType { @@ -23,7 +23,7 @@ public enum EmailAccountType } /// - /// 排序类型 + /// Ordering type /// public enum OrderingType { @@ -32,7 +32,7 @@ public enum OrderingType } /// - /// 安装或更新 + /// Install or update /// public enum InstallOrUpdate { @@ -41,7 +41,7 @@ public enum InstallOrUpdate } /// - /// Email设置类型 + /// Email setting type /// public enum SendEmailType { @@ -54,13 +54,13 @@ public enum SendEmailType OrderCreate, OrderPaySuccess, OrderCancelled, - WeixinStat, //微信统计 - AppStatusChanged, //应用状态改变 + WeixinStat, //WeChat statistics + AppStatusChanged, //Application status changed } /// - /// Meta类型 + /// Meta type /// public enum MetaType { @@ -68,7 +68,7 @@ public enum MetaType description } - #region 实体属性 + #region Entity properties public enum Account_RegisterWay @@ -107,14 +107,14 @@ public enum XncfModules_State #endregion } -//#region 弥补 MySQL 库暂时的 bug +//#region Temporary workaround for MySQL library bug //#if RELEASE //namespace Microsoft.EntityFrameworkCore.Metadata //{ // /// -// /// 说明:因为 Pomelo.EntityFrameworkCore.MySql 一个未充分解耦的问题,这里暂时先引用,待其升级后会取消,和具体数据库充分解耦 -// /// 官方反馈:https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues/1205 +// /// Note: due to an insufficient decoupling issue in Pomelo.EntityFrameworkCore.MySql, this temporary reference is used and will be removed after upstream upgrade. +// /// Official feedback: https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues/1205 // /// // public enum MySqlValueGenerationStrategy // { diff --git a/src/Basic/Senparc.Ncf.Core/EventBus/EventBusExtensions.cs b/src/Basic/Senparc.Ncf.Core/EventBus/EventBusExtensions.cs index 24f45b944..6dc1d961e 100644 --- a/src/Basic/Senparc.Ncf.Core/EventBus/EventBusExtensions.cs +++ b/src/Basic/Senparc.Ncf.Core/EventBus/EventBusExtensions.cs @@ -10,22 +10,22 @@ namespace Senparc.Ncf.Core.EventBus public static class EventBusExtensions { /// - /// 注册 Senparc EventBus 及所有事件处理器 + /// Register Senparc EventBus and all event handlers /// - /// 服务集合 - /// 配置 EventBus 选项的委托(可选) - /// 需要扫描事件处理器的程序集 + /// Service collection + /// Delegate used to configure EventBus options (optional) + /// Assemblies to scan for event handlers public static IServiceCollection AddSenparcEventBus( this IServiceCollection services, Action configureOptions = null, params Assembly[] assembliesToScan) { - // 1. 注册 EventBus 配置选项 + // 1. Register EventBus options var options = new EventBusOptions(); configureOptions?.Invoke(options); services.AddSingleton(options); - // 2. 注册单例 EventBus (发布者用) - 使用工厂方法来支持 ILogger 注入 + // 2. Register singleton EventBus (for publishers) using factory method to support ILogger injection services.AddSingleton(sp => { var logger = sp.GetService>(); @@ -33,15 +33,15 @@ public static IServiceCollection AddSenparcEventBus( }); services.AddSingleton(sp => sp.GetRequiredService()); - // 3. 注册后台托管服务 (消费者用) + // 3. Register background hosted service (for consumers) services.AddHostedService(); - // 4. 扫描并注册 Handler + // 4. Scan and register handlers if (assembliesToScan != null && assembliesToScan.Length > 0) { foreach (var assembly in assembliesToScan) { - // 查找实现了 IIntegrationEventHandler 的具体类 + // Find concrete classes implementing IIntegrationEventHandler var handlerTypes = assembly.GetTypes() .Where(t => t.IsClass && !t.IsAbstract && t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IIntegrationEventHandler<>))); @@ -53,7 +53,7 @@ public static IServiceCollection AddSenparcEventBus( foreach (var i in interfaces) { - // 注册为 Scoped + // Register as Scoped services.AddScoped(i, type); } } diff --git a/src/Basic/Senparc.Ncf.Core/EventBus/EventBusHostedService.cs b/src/Basic/Senparc.Ncf.Core/EventBus/EventBusHostedService.cs index 73fc04819..08b51628f 100644 --- a/src/Basic/Senparc.Ncf.Core/EventBus/EventBusHostedService.cs +++ b/src/Basic/Senparc.Ncf.Core/EventBus/EventBusHostedService.cs @@ -12,8 +12,8 @@ namespace Senparc.Ncf.Core.EventBus { /// - /// 后台消息泵:负责从 Channel 读取事件并分发给 Handler - /// 支持高并发场景,可配置并发度 + /// Background message pump: reads events from Channel and dispatches to handlers + /// Supports high-concurrency scenarios with configurable parallelism /// public class EventBusHostedService : BackgroundService { @@ -43,7 +43,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) _options.MaxEventChainDepth, _options.EnableCircularReferenceDetection); - // 使用信号量控制并发度,防止过多任务同时执行导致资源耗尽 + // Control concurrency with semaphore to avoid resource exhaustion using var semaphore = new SemaphoreSlim(_options.MaxConcurrency); var activeTasks = new List(); @@ -51,7 +51,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) { await foreach (var @event in _eventBus.Reader.ReadAllAsync(stoppingToken)) { - // 1. 防止重复处理检测 + // 1. Duplicate processing detection if (_options.EnableDuplicateDetection) { if (!_eventBus.TryMarkEventAsProcessed(@event.Id)) @@ -64,7 +64,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } } - // 2. 检查事件链深度(防止无限递归) + // 2. Check event chain depth (prevent infinite recursion) if (@event.Depth >= _options.MaxEventChainDepth) { _logger.LogError( @@ -76,7 +76,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) continue; } - // 3. 检查循环引用(防止 A→B→A 循环模式) + // 3. Check circular references (prevent A→B→A cycle) if (_options.EnableCircularReferenceDetection) { var currentEventType = @event.GetType().Name; @@ -93,10 +93,10 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } } - // 等待获取信号量(如果当前并发数已达上限) + // Wait for semaphore if max concurrency reached await semaphore.WaitAsync(stoppingToken); - // 启动异步任务处理事件 + // Start async task to process event var task = Task.Run(async () => { try @@ -112,7 +112,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) @event.Id, @event.Depth); - // 根据配置决定是否重试 + // Retry based on configuration if (_options.RetryOnFailure && _options.MaxRetryAttempts > 0) { await RetryEventProcessingAsync(@event, stoppingToken); @@ -120,21 +120,21 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } finally { - // 释放信号量,允许下一个事件处理 + // Release semaphore for next event semaphore.Release(); } }, stoppingToken); activeTasks.Add(task); - // 定期清理已完成的任务,防止内存泄漏 + // Periodically clean completed tasks to prevent memory growth if (activeTasks.Count >= _options.MaxConcurrency * 2) { activeTasks.RemoveAll(t => t.IsCompleted); } } - // 等待所有正在处理的任务完成 + // Wait for all active tasks to complete await Task.WhenAll(activeTasks); } catch (OperationCanceledException) @@ -152,10 +152,10 @@ private async Task ProcessEventAsync(IIntegrationEvent @event, CancellationToken { var startTime = DateTime.UtcNow; - // 创建 Scope 以支持 Scoped 服务注入(如 DbContext, Repository) + // Create scope to support scoped service injection (e.g., DbContext, Repository) using var scope = _serviceProvider.CreateScope(); - // 动态查找:IIntegrationEventHandler + // Dynamic lookup: IIntegrationEventHandler var handlerType = typeof(IIntegrationEventHandler<>).MakeGenericType(@event.GetType()); var handlers = scope.ServiceProvider.GetServices(handlerType).ToList(); @@ -205,7 +205,7 @@ private async Task ProcessEventAsync(IIntegrationEvent @event, CancellationToken @event.Id, @event.Depth, @event.EventChain); - throw; // 重新抛出异常,触发重试机制 + throw; // Re-throw to trigger retry mechanism } } @@ -231,7 +231,7 @@ private async Task RetryEventProcessingAsync(IIntegrationEvent @event, Cancellat attempt, _options.MaxRetryAttempts); - // 指数退避策略 + // Exponential backoff strategy var delay = TimeSpan.FromSeconds(Math.Pow(2, attempt - 1)); await Task.Delay(delay, ct); @@ -242,7 +242,7 @@ private async Task RetryEventProcessingAsync(IIntegrationEvent @event, Cancellat @event.GetType().Name, @event.Id, attempt); - return; // 成功处理,退出重试循环 + return; // Success, exit retry loop } catch (Exception ex) { @@ -267,38 +267,38 @@ private async Task RetryEventProcessingAsync(IIntegrationEvent @event, Cancellat } /// - /// EventBus 配置选项 + /// EventBus configuration options /// public class EventBusOptions { /// - /// 最大并发处理数(默认值根据 CPU 核心数计算) - /// 建议:数据库连接池大小 / 2,或者 Environment.ProcessorCount * 2 + /// Maximum concurrent processing count (default is based on CPU core count) + /// Recommended: database connection pool size / 2, or Environment.ProcessorCount * 2 /// public int MaxConcurrency { get; set; } = Math.Max(4, Environment.ProcessorCount * 2); /// - /// 是否启用重复事件检测(防止同一事件被处理多次) + /// Whether to enable duplicate event detection (prevents processing the same event multiple times) /// public bool EnableDuplicateDetection { get; set; } = true; /// - /// 处理失败时是否重试 + /// Whether to retry on processing failure /// public bool RetryOnFailure { get; set; } = true; /// - /// 最大重试次数 + /// Maximum retry attempts /// public int MaxRetryAttempts { get; set; } = 3; /// - /// 最大事件链深度(防止无限循环) + /// Maximum event chain depth (prevents infinite loops) /// public int MaxEventChainDepth { get; set; } = 10; /// - /// 是否启用循环引用检测(检测事件类型链中的循环模式) + /// Whether to enable circular reference detection (detects loop patterns in event type chain) /// public bool EnableCircularReferenceDetection { get; set; } = true; } diff --git a/src/Basic/Senparc.Ncf.Core/EventBus/InMemoryEventBus.cs b/src/Basic/Senparc.Ncf.Core/EventBus/InMemoryEventBus.cs index 4f873de10..72ebb6673 100644 --- a/src/Basic/Senparc.Ncf.Core/EventBus/InMemoryEventBus.cs +++ b/src/Basic/Senparc.Ncf.Core/EventBus/InMemoryEventBus.cs @@ -10,14 +10,14 @@ namespace Senparc.Ncf.Core.EventBus { /// - /// 基于内存 Channel 的事件总线实现 + /// In-memory Channel-based event bus implementation /// public class InMemoryEventBus : IEventBus { private readonly Channel _channel; private readonly ILogger _logger; - // 用于防止重复处理的事件 ID 追踪(使用滑动窗口,保留最近 10 分钟的事件 ID) + // Tracks processed event IDs to prevent duplicate processing (sliding window of last 10 minutes) private readonly ConcurrentDictionary _processedEventIds = new(); private readonly TimeSpan _eventIdRetentionPeriod = TimeSpan.FromMinutes(10); @@ -25,12 +25,12 @@ public InMemoryEventBus(ILogger logger = null) { _logger = logger; - // 配置无界通道(生产速度 > 消费速度时,内存会增加,但不会阻塞生产者) - // 如果需要背压控制,可以使用 Channel.CreateBounded + // Configure unbounded channel (memory can grow when producer speed > consumer speed, but producers won't block) + // Use Channel.CreateBounded if backpressure control is required var options = new UnboundedChannelOptions { - SingleReader = false, // 支持多个消费者并发读取 - SingleWriter = false // 多个业务模块在并发写 + SingleReader = false, // Support concurrent reads from multiple consumers + SingleWriter = false // Allow concurrent writes from multiple business modules }; _channel = Channel.CreateUnbounded(options); } @@ -42,13 +42,13 @@ public ValueTask PublishAsync(TEvent @event, CancellationToken cancellat } /// - /// 发布派生事件(自动继承父事件的链信息并检测循环) - /// 注意:此方法要求事件类型继承自 IntegrationEvent 基类 + /// Publish a derived event (automatically inherit parent chain metadata and detect cycles) + /// Note: this method requires event types to inherit from the IntegrationEvent base class /// public ValueTask PublishDerivedAsync(TEvent @event, IIntegrationEvent parentEvent, CancellationToken cancellationToken = default) where TEvent : IIntegrationEvent { - // 仅支持 IntegrationEvent 基类(因为需要访问 DeriveMetadata 等方法) + // Only IntegrationEvent base class is supported (DeriveMetadata and related methods are required) if (parentEvent is not IntegrationEvent typedParent) { throw new ArgumentException("Parent event must inherit from IntegrationEvent base class", nameof(parentEvent)); @@ -59,10 +59,10 @@ public ValueTask PublishDerivedAsync(TEvent @event, IIntegrationEvent pa throw new ArgumentException("Event must inherit from IntegrationEvent base class", nameof(@event)); } - // 派生事件的元数据 + // Metadata for the derived event var metadata = typedParent.DeriveMetadata(); - // 检查是否会产生循环引用(在发布前预检) + // Pre-check for circular references before publishing var newEventType = typedEvent.GetType().Name; if (typedParent.HasCircularReference(newEventType)) { @@ -77,7 +77,7 @@ public ValueTask PublishDerivedAsync(TEvent @event, IIntegrationEvent pa $"This would cause an infinite event loop."); } - // 创建一个带有链信息的新事件实例 + // Create a new event instance with propagated chain metadata var derivedEvent = typedEvent with { ParentEventId = metadata.ParentEventId, @@ -96,11 +96,11 @@ public ValueTask PublishDerivedAsync(TEvent @event, IIntegrationEvent pa } /// - /// 检查事件是否已经被处理过(用于防止重复处理) + /// Check whether the event has already been processed (to prevent duplicate handling) /// public bool TryMarkEventAsProcessed(Guid eventId) { - // 清理过期的事件 ID(每100次调用清理一次) + // Clean up expired event IDs (once per 100 calls) if (_processedEventIds.Count > 0 && _processedEventIds.Count % 100 == 0) { CleanupExpiredEventIds(); @@ -123,7 +123,7 @@ private void CleanupExpiredEventIds() } } - // 供同一个程序集内的 HostedService 读取 + // Exposed for HostedService reads within the same assembly internal ChannelReader Reader => _channel.Reader; } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Core/Extensions/ObjectQueryExtensions.cs b/src/Basic/Senparc.Ncf.Core/Extensions/ObjectQueryExtensions.cs index 913174f91..82bea1436 100644 --- a/src/Basic/Senparc.Ncf.Core/Extensions/ObjectQueryExtensions.cs +++ b/src/Basic/Senparc.Ncf.Core/Extensions/ObjectQueryExtensions.cs @@ -10,7 +10,7 @@ namespace Senparc.Ncf.Core.Extensions public static class ObjectQueryExtensions { /// - /// 添加多个Include()对象 + /// Add multiple Include() members /// /// /// @@ -18,7 +18,7 @@ public static class ObjectQueryExtensions /// public static IQueryable Includes(this IQueryable obj, string[] includes) where T : class { - //** 用法:entities.Users.Includes(new string[]{ "Roles","Products" }).ToList() + //** Usage: entities.Users.Includes(new string[]{ "Roles","Products" }).ToList() if (includes == null) { return obj; @@ -32,7 +32,7 @@ public static IQueryable Includes(this IQueryable obj, string[] include } /// - /// 添加多个可在编译时检测的Include类型 + /// Add multiple Include types that can be checked at compile time /// /// /// @@ -40,7 +40,7 @@ public static IQueryable Includes(this IQueryable obj, string[] include /// public static IQueryable Includes(this IQueryable obj, Type[] includeTyles) where T : class { - //** 用法:entities.Users.Includes(new Type[]{ typeof(Roles),typeof(Products) }).ToList() + //** Usage: entities.Users.Includes(new Type[]{ typeof(Roles),typeof(Products) }).ToList() if (includeTyles == null) return obj; @@ -52,7 +52,7 @@ public static IQueryable Includes(this IQueryable obj, Type[] includeTy } /// - /// 添加可在编译时检测的Include类型 + /// Add an Include type that can be checked at compile time /// /// /// @@ -60,17 +60,17 @@ public static IQueryable Includes(this IQueryable obj, Type[] includeTy /// public static IQueryable Include(this IQueryable obj, Type includeType) where T : class { - //** 用法:entities.Users.Include(typeof(Roles)).ToList() + //** Usage: entities.Users.Include(typeof(Roles)).ToList() return obj.Include(includeType.Name); } /// - /// 委托排序 + /// Delegate-based sorting /// /// /// /// - /// 是否升序排列 + /// Whether to sort in ascending order /// public static IOrderedQueryable OrderBy(this IQueryable obj, Expression> orderBy, OrderingType orderingType) where T : class { @@ -81,12 +81,12 @@ public static IOrderedQueryable OrderBy(this IQueryable obj, Expres } /// - /// 委托排序 + /// Delegate-based sorting /// /// /// /// - /// 是否升序排列 + /// Whether to sort in ascending order /// public static IOrderedEnumerable OrderByIEnumerable(this IEnumerable obj, Func orderBy, OrderingType orderingType) where T : class { diff --git a/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/DatabaseConfigurationFactory.cs b/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/DatabaseConfigurationFactory.cs index 6df00b169..180a97260 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/DatabaseConfigurationFactory.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/DatabaseConfigurationFactory.cs @@ -1,60 +1,60 @@ -using Senparc.Ncf.Core.Exceptions; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// 多数据库配置工厂 - /// - public class DatabaseConfigurationFactory - { - #region 单例 - - DatabaseConfigurationFactory() { } - - /// - /// DatabaseConfigurationFactory 的全局单例 - /// - public static DatabaseConfigurationFactory Instance - { - get - { - return Nested.instance; - } - } - - class Nested - { - static Nested() { } - - internal static readonly DatabaseConfigurationFactory instance = new DatabaseConfigurationFactory(); - } - - #endregion - - //TODO:如果是分布式,需要存储到缓存中 - - private IDatabaseConfiguration _currentDatabaseConfiguration; - - public IDatabaseConfiguration Current - { - get - { - if (_currentDatabaseConfiguration == null) - { - throw new NcfDatabaseException("未指定 DatabaseConfiguration!", null); - } - return _currentDatabaseConfiguration; - } - set - { - _currentDatabaseConfiguration = value; - } - } - - - ///// - ///// 给 design time(设计时)操作数据库(如migration)使用。指定当前正在操作的 XNCF 数据库信息(如果是直接继承自 DbContext 的类,需要模拟此参数) - ///// - //public XncfDatabaseData CurrentXncfDatabaseData { get; set; } - } -} +using Senparc.Ncf.Core.Exceptions; + +namespace Senparc.Ncf.Core.Models +{ + /// + /// 多数据库配置工厂 + /// + public class DatabaseConfigurationFactory + { + #region Singleton + + DatabaseConfigurationFactory() { } + + /// + /// DatabaseConfigurationFactory 的全局单例 + /// + public static DatabaseConfigurationFactory Instance + { + get + { + return Nested.instance; + } + } + + class Nested + { + static Nested() { } + + internal static readonly DatabaseConfigurationFactory instance = new DatabaseConfigurationFactory(); + } + + #endregion + + //TODO:如果是分布式,需要存储到缓存中 + + private IDatabaseConfiguration _currentDatabaseConfiguration; + + public IDatabaseConfiguration Current + { + get + { + if (_currentDatabaseConfiguration == null) + { + throw new NcfDatabaseException("未指定 DatabaseConfiguration!", null); + } + return _currentDatabaseConfiguration; + } + set + { + _currentDatabaseConfiguration = value; + } + } + + + ///// + ///// 给 design time(设计时)操作数据库(如migration)使用。指定当前正在操作的 XNCF 数据库信息(如果是直接继承自 DbContext 的类,需要模拟此参数) + ///// + //public XncfDatabaseData CurrentXncfDatabaseData { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/SenparcEntities/SenparcEntitiesDbContextBase.cs b/src/Basic/Senparc.Ncf.Core/Models/SenparcEntities/SenparcEntitiesDbContextBase.cs index 88785791b..84437c4af 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/SenparcEntities/SenparcEntitiesDbContextBase.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/SenparcEntities/SenparcEntitiesDbContextBase.cs @@ -1,266 +1,266 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.Extensions.DependencyInjection; -using Senparc.CO2NET; -using Senparc.CO2NET.Extensions; -using Senparc.Ncf.Core.Config; -using Senparc.Ncf.Core.Exceptions; -using Senparc.Ncf.Core.Models.DataBaseModel; -using Senparc.Ncf.Core.MultiTenant; -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// SenparcEntities 的基类 - /// - public abstract class SenparcEntitiesDbContextBase : DbContext, ISenparcEntitiesDbContext - { - private static readonly bool[] _migrated = { true }; - private IServiceProvider _serviceProvider; - //private IServiceScope _serviceScope; - private RequestTenantInfo _tenantInfo; - - /// - /// SenparcDI 储存的 GlobalServiceCollection 生成的 ServiceProvider - /// - protected virtual IServiceProvider ServiceProvider - { - get - { - if (_serviceProvider == null) - { - throw new Senparc.Ncf.Core.Exceptions.NcfDatabaseException("_serviceProvider 不可以为 null", null); - - //base.Database.GetDbConnection().Site.GetRequiredService<> - - //_serviceScope = SenparcDI.GlobalServiceCollection.BuildServiceProvider().CreateScope(); - //_serviceProvider = _serviceScope.ServiceProvider;// ((IInfrastructure)this).Instance; - } - return _serviceProvider; - } - } - - /// - /// 当前上下文中租户信息 - /// - public RequestTenantInfo TenantInfo - { - get - { - if (this.EnableMultiTenant && _tenantInfo == null) - { - _tenantInfo = MultiTenantHelper.TryGetAndCheckRequestTenantInfo(ServiceProvider, $"SenparcEntitiesDbContextBase.TenantInfo", this); - } - return _tenantInfo; - } - set - { - _tenantInfo = value; - } - } - - /// - /// 自动添加多租户Id - /// - private void AddTenandId() - { - if (this.EnableMultiTenant) - { - ChangeTracker.DetectChanges(); // - var addedEntities = this.ChangeTracker - .Entries() - .Where(z => z.State == EntityState.Added) - .Select(z => z.Entity) - .ToList(); - - RequestTenantInfo requestTenantInfo = TenantInfo; - foreach (var entity in addedEntities) - { - if (!(entity is IIgnoreMulitTenant) && (entity is IMultiTenancy multiTenantEntity)) - { - ////如果未设置,则进行设定 - //requestTenantInfo = requestTenantInfo ?? MultiTenantHelper.TryGetAndCheckRequestTenantInfo(ServiceProvider, "SenparcEntitiesDbContextBase.AddTenandId()", this); - multiTenantEntity.TenantId = requestTenantInfo.Id; - } - } - } - } - - public bool? _enableMultiTenant; - - /// - /// 是否启用多租户,默认读取 SiteConfig.SenparcCoreSetting.EnableMultiTenant - /// - public bool EnableMultiTenant - { - get - { - if (!_enableMultiTenant.HasValue) - { - _enableMultiTenant = SiteConfig.SenparcCoreSetting.EnableMultiTenant; - } - return _enableMultiTenant.Value; - } - private set - { - _enableMultiTenant = value; - } - } - - - - - public SenparcEntitiesDbContextBase(DbContextOptions options, IServiceProvider serviceProvider) : base(options) - { - _serviceProvider = serviceProvider; - } - - //~SenparcEntitiesBase() - //{ - // _serviceScope?.Dispose(); - //} - - #region Migration 迁移相关方法 - - /// - /// 执行 EF Core 的合并操作(等价于 update-database) - /// 出于安全考虑,每次执行 Migrate() 方法之前,必须先执行 ResetMigrate() 开启允许 Migrate 执行的状态。 - /// - public virtual void Migrate() - { - if (!_migrated[0]) - { - lock (_migrated) - { - if (!_migrated[0]) - { - Database.Migrate(); // apply all migrations - _migrated[0] = true; - } - } - } - } - - /// - /// 重置合并状态 - /// - public virtual void ResetMigrate() - { - _migrated[0] = false; - } - - #endregion - - /// - /// 【核心】 将当前动态模块的 DbContext 对象注入到 DbContext - /// - /// - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - var dt1 = SystemTime.Now; - var types = modelBuilder.Model.GetEntityTypes().Where(e => typeof(EntityBase).IsAssignableFrom(e.ClrType)).ToList(); - - Console.WriteLine($"============ SenparcEntitiesDbContextBase OnModelCreating:{this.GetType().Name} ({types.Count()} types) ============"); - - //Console.WriteLine("\t\t types:" + types.Select(z => z.Name).ToJson()); - - var maxLength = types.Max(z => z.Name.Length); - Console.WriteLine($" No. | Entity Type"); - Console.WriteLine($"------|{new string('-', maxLength)}"); - for (int i = 0; i < types.Count; i++) - { - //设置需要添加的全局查询 - - var entityType = types[i]; - Console.WriteLine($" {(i + 1).ToString().PadRight(4)}| {entityType.Name}"); - SetGlobalQueryMethodInfo - .MakeGenericMethod(entityType.ClrType) - .Invoke(this, new object[] { modelBuilder }); - } - - - base.OnModelCreating(modelBuilder); - - Console.WriteLine($"============ SenparcEntitiesDbContextBase OnModelCreating End({SystemTime.DiffTotalMS(dt1)}ms) ============"); - Console.WriteLine(); - } - - /// - /// 设置全局查询 - /// - private static readonly MethodInfo SetGlobalQueryMethodInfo = typeof(SenparcEntitiesBase) - .GetMethods(BindingFlags.Public | BindingFlags.Instance) - .Single(t => t.IsGenericMethod && t.Name == nameof(SetGlobalQuery) /*"SetGlobalQuery"*/); - - /// - /// 全局查询 - /// - /// - /// - public virtual void SetGlobalQuery(ModelBuilder builder) where T : EntityBase - { - var entityBuilder = builder.Entity();//.HasQueryFilter(z => !z.Flag); - - //多租户 - //Console.WriteLine($"\t DbContext:{this.GetHashCode()} \tSetGlobalQuery<{typeof(T).Name}> this.EnableMultiTenant:" + this.EnableMultiTenant + $" / SiteConfig.SenparcCoreSetting.EnableMultiTenant:{SiteConfig.SenparcCoreSetting.EnableMultiTenant}"); - if (this.EnableMultiTenant && typeof(IMultiTenancy).IsAssignableFrom(typeof(T)) && !(typeof(IIgnoreMulitTenant).IsAssignableFrom(typeof(T)))) - { - //多租户 + 软删除 - entityBuilder.HasQueryFilter(z => z.TenantId == TenantInfo.Id && !z.Flag); - } - else - { - //仅软删除 - entityBuilder.HasQueryFilter(z => !z.Flag); - } - } - - /// - /// 设置当前 DbContext 是否启用上下文 - /// - /// - public void SetMultiTenantEnable(bool enable) - { - //Console.WriteLine($"\t {this.GetHashCode()}\tset EnableMultiTenant to:" + enable); - EnableMultiTenant = enable; - } - - /// - /// 多租户状态重置为 SiteConfig.SenparcCoreSetting.EnableMultiTenant - /// - public void ResetMultiTenantEnable() - { - //Console.WriteLine($"\t {this.GetHashCode()}\tResetMultiTenantEnable()"); - SetMultiTenantEnable(SiteConfig.SenparcCoreSetting.EnableMultiTenant); - } - - - public override int SaveChanges() - { - //处理多租户 - AddTenandId(); - return base.SaveChanges(); - } - - public override Task SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) - { - //处理多租户 - AddTenandId(); - return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); - } - - - //public override Task SaveChangesAsync(CancellationToken cancellationToken = default) - //{ - // return base.SaveChangesAsync(cancellationToken);//底层引用的就是 SaveChangesAsync,所以不用处理 - //} - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; +using Senparc.CO2NET; +using Senparc.CO2NET.Extensions; +using Senparc.Ncf.Core.Config; +using Senparc.Ncf.Core.Exceptions; +using Senparc.Ncf.Core.Models.DataBaseModel; +using Senparc.Ncf.Core.MultiTenant; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Core.Models +{ + /// + /// SenparcEntities 的基类 + /// + public abstract class SenparcEntitiesDbContextBase : DbContext, ISenparcEntitiesDbContext + { + private static readonly bool[] _migrated = { true }; + private IServiceProvider _serviceProvider; + //private IServiceScope _serviceScope; + private RequestTenantInfo _tenantInfo; + + /// + /// SenparcDI 储存的 GlobalServiceCollection 生成的 ServiceProvider + /// + protected virtual IServiceProvider ServiceProvider + { + get + { + if (_serviceProvider == null) + { + throw new Senparc.Ncf.Core.Exceptions.NcfDatabaseException("_serviceProvider 不可以为 null", null); + + //base.Database.GetDbConnection().Site.GetRequiredService<> + + //_serviceScope = SenparcDI.GlobalServiceCollection.BuildServiceProvider().CreateScope(); + //_serviceProvider = _serviceScope.ServiceProvider;// ((IInfrastructure)this).Instance; + } + return _serviceProvider; + } + } + + /// + /// 当前上下文中租户信息 + /// + public RequestTenantInfo TenantInfo + { + get + { + if (this.EnableMultiTenant && _tenantInfo == null) + { + _tenantInfo = MultiTenantHelper.TryGetAndCheckRequestTenantInfo(ServiceProvider, $"SenparcEntitiesDbContextBase.TenantInfo", this); + } + return _tenantInfo; + } + set + { + _tenantInfo = value; + } + } + + /// + /// 自动添加多租户Id + /// + private void AddTenandId() + { + if (this.EnableMultiTenant) + { + ChangeTracker.DetectChanges(); // + var addedEntities = this.ChangeTracker + .Entries() + .Where(z => z.State == EntityState.Added) + .Select(z => z.Entity) + .ToList(); + + RequestTenantInfo requestTenantInfo = TenantInfo; + foreach (var entity in addedEntities) + { + if (!(entity is IIgnoreMulitTenant) && (entity is IMultiTenancy multiTenantEntity)) + { + ////如果未设置,则进行设定 + //requestTenantInfo = requestTenantInfo ?? MultiTenantHelper.TryGetAndCheckRequestTenantInfo(ServiceProvider, "SenparcEntitiesDbContextBase.AddTenandId()", this); + multiTenantEntity.TenantId = requestTenantInfo.Id; + } + } + } + } + + public bool? _enableMultiTenant; + + /// + /// 是否启用多租户,默认读取 SiteConfig.SenparcCoreSetting.EnableMultiTenant + /// + public bool EnableMultiTenant + { + get + { + if (!_enableMultiTenant.HasValue) + { + _enableMultiTenant = SiteConfig.SenparcCoreSetting.EnableMultiTenant; + } + return _enableMultiTenant.Value; + } + private set + { + _enableMultiTenant = value; + } + } + + + + + public SenparcEntitiesDbContextBase(DbContextOptions options, IServiceProvider serviceProvider) : base(options) + { + _serviceProvider = serviceProvider; + } + + //~SenparcEntitiesBase() + //{ + // _serviceScope?.Dispose(); + //} + + #region Migration 迁移相关方法 + + /// + /// 执行 EF Core 的合并操作(等价于 update-database) + /// 出于安全考虑,每次执行 Migrate() 方法之前,必须先执行 ResetMigrate() 开启允许 Migrate 执行的状态。 + /// + public virtual void Migrate() + { + if (!_migrated[0]) + { + lock (_migrated) + { + if (!_migrated[0]) + { + Database.Migrate(); // apply all migrations + _migrated[0] = true; + } + } + } + } + + /// + /// 重置合并状态 + /// + public virtual void ResetMigrate() + { + _migrated[0] = false; + } + + #endregion + + /// + /// 【核心】 将当前动态模块的 DbContext 对象注入到 DbContext + /// + /// + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + var dt1 = SystemTime.Now; + var types = modelBuilder.Model.GetEntityTypes().Where(e => typeof(EntityBase).IsAssignableFrom(e.ClrType)).ToList(); + + Console.WriteLine($"============ SenparcEntitiesDbContextBase OnModelCreating:{this.GetType().Name} ({types.Count()} types) ============"); + + //Console.WriteLine("\t\t types:" + types.Select(z => z.Name).ToJson()); + + var maxLength = types.Max(z => z.Name.Length); + Console.WriteLine($" No. | Entity Type"); + Console.WriteLine($"------|{new string('-', maxLength)}"); + for (int i = 0; i < types.Count; i++) + { + //设置需要添加的全局查询 + + var entityType = types[i]; + Console.WriteLine($" {(i + 1).ToString().PadRight(4)}| {entityType.Name}"); + SetGlobalQueryMethodInfo + .MakeGenericMethod(entityType.ClrType) + .Invoke(this, new object[] { modelBuilder }); + } + + + base.OnModelCreating(modelBuilder); + + Console.WriteLine($"============ SenparcEntitiesDbContextBase OnModelCreating End({SystemTime.DiffTotalMS(dt1)}ms) ============"); + Console.WriteLine(); + } + + /// + /// 设置全局查询 + /// + private static readonly MethodInfo SetGlobalQueryMethodInfo = typeof(SenparcEntitiesBase) + .GetMethods(BindingFlags.Public | BindingFlags.Instance) + .Single(t => t.IsGenericMethod && t.Name == nameof(SetGlobalQuery) /*"SetGlobalQuery"*/); + + /// + /// 全局查询 + /// + /// + /// + public virtual void SetGlobalQuery(ModelBuilder builder) where T : EntityBase + { + var entityBuilder = builder.Entity();//.HasQueryFilter(z => !z.Flag); + + //多租户 + //Console.WriteLine($"\t DbContext:{this.GetHashCode()} \tSetGlobalQuery<{typeof(T).Name}> this.EnableMultiTenant:" + this.EnableMultiTenant + $" / SiteConfig.SenparcCoreSetting.EnableMultiTenant:{SiteConfig.SenparcCoreSetting.EnableMultiTenant}"); + if (this.EnableMultiTenant && typeof(IMultiTenancy).IsAssignableFrom(typeof(T)) && !(typeof(IIgnoreMulitTenant).IsAssignableFrom(typeof(T)))) + { + //多租户 + 软删除 + entityBuilder.HasQueryFilter(z => z.TenantId == TenantInfo.Id && !z.Flag); + } + else + { + //仅软删除 + entityBuilder.HasQueryFilter(z => !z.Flag); + } + } + + /// + /// 设置当前 DbContext 是否启用上下文 + /// + /// + public void SetMultiTenantEnable(bool enable) + { + //Console.WriteLine($"\t {this.GetHashCode()}\tset EnableMultiTenant to:" + enable); + EnableMultiTenant = enable; + } + + /// + /// 多租户状态重置为 SiteConfig.SenparcCoreSetting.EnableMultiTenant + /// + public void ResetMultiTenantEnable() + { + //Console.WriteLine($"\t {this.GetHashCode()}\tResetMultiTenantEnable()"); + SetMultiTenantEnable(SiteConfig.SenparcCoreSetting.EnableMultiTenant); + } + + + public override int SaveChanges() + { + // Handle multi-tenant + AddTenandId(); + return base.SaveChanges(); + } + + public override Task SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) + { + // Handle multi-tenant + AddTenandId(); + return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); + } + + + //public override Task SaveChangesAsync(CancellationToken cancellationToken = default) + //{ + // return base.SaveChangesAsync(cancellationToken);//底层引用的就是 SaveChangesAsync,所以不用处理 + //} + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Register.cs b/src/Basic/Senparc.Ncf.Core/Register.cs index a390c9e11..f79507d22 100644 --- a/src/Basic/Senparc.Ncf.Core/Register.cs +++ b/src/Basic/Senparc.Ncf.Core/Register.cs @@ -1,149 +1,149 @@ -using log4net; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Senparc.CO2NET; -using Senparc.CO2NET.RegisterServices; -using Senparc.CO2NET.Trace; -using Senparc.Ncf.Core.AssembleScan; -using Senparc.Ncf.Core.DI; -using System; -using System.Linq; -using System.Reflection; - -namespace Senparc.Ncf.Core -{ - /// - /// NCF 的注册程序 - /// - public static class Register - { - /// - /// 扫描自动依赖注入的接口 - /// - public static IServiceCollection ScanAssamblesForAutoDI(this IServiceCollection services) - { - //遍历所有程序集进行注册 - Action action = assembly => - { - var areaRegisterTypes = assembly.GetTypes() //.GetExportedTypes() - .Where(z => !z.IsAbstract && !z.IsInterface && z.GetInterface(nameof(IAutoDI)) != null) - .ToArray(); - - DILifecycleType dILifecycleType = DILifecycleType.Scoped; - - foreach (var registerType in areaRegisterTypes) - { - try - { - //判断特性标签 - var attrs = System.Attribute.GetCustomAttributes(registerType, false).Where(z => z is AutoDITypeAttribute); - if (attrs.Count() > 0) - { - var attr = attrs.First() as AutoDITypeAttribute; - dILifecycleType = attr.DILifecycleType;//使用指定的方式 - } - - //针对不同的类型进行不同生命周期的 DI 设置 - switch (dILifecycleType) - { - case DILifecycleType.Scoped: - services.AddScoped(registerType); - break; - case DILifecycleType.Singleton: - services.AddSingleton(registerType); - break; - case DILifecycleType.Transient: - services.AddTransient(registerType); - break; - default: - throw new NotImplementedException($"未处理此 DILifecycleType 类型:{dILifecycleType.ToString()}"); - } - } - catch (Exception ex) - { - SenparcTrace.BaseExceptionLog(ex); - } - } - }; - - AssembleScanHelper.AddAssembleScanItem(action, true); - - return services; - } - - - #region TryRegisterMiniCore - /// - /// 以最小化的过程进行自动注册,适用于缺少环境的单元测试、Code First 命令等。请勿在生产环境中使用此命令! - /// 如果已经注册过,则返回 null - /// - public static IRegisterService TryRegisterMiniCore(Action servicesAction = null, Action appAction = null) - { - //初始化项目 - if (!Senparc.CO2NET.RegisterServices.RegisterServiceExtension.SenparcGlobalServicesRegistered) - { - try - { - var repository = LogManager.CreateRepository("NETCoreRepository");//读取Log配置文件 - Console.WriteLine("[TryRegisterMiniCore] NETCoreRepository 注册完成"); - } - catch - { - Console.WriteLine("NETCoreRepository 已进行过配置,无需重复配置"); - } - //允许从外部添加对 services 的注册操作 - var services = RegisterServiceCollection(); - - Console.WriteLine("[TryRegisterMiniCore] RegisterServiceCollection() 完成"); - - servicesAction?.Invoke(services); - Console.WriteLine("[TryRegisterMiniCore] servicesAction?.Invoke(services) 完成"); - - //允许从外部添加对 app 的注册操作 - var serviceProvider = services.BuildServiceProvider(); - Console.WriteLine("[TryRegisterMiniCore] services.BuildServiceProvider() 完成"); - - IApplicationBuilder app = new ApplicationBuilder(serviceProvider); - appAction?.Invoke(app); - Console.WriteLine("[TryRegisterMiniCore] appAction?.Invoke(app) 完成"); - - - return RegisterServiceStart(); - } - return null; - } - - private static SenparcSetting CreateSenparcSetting() - { - return new SenparcSetting() { IsDebug = true }; - } - - /// - /// 注册 IServiceCollection 和 MemoryCache - /// - private static IServiceCollection RegisterServiceCollection() - { - var serviceCollection = new ServiceCollection(); - var configBuilder = new ConfigurationBuilder(); - var config = configBuilder.Build(); - serviceCollection.AddSenparcGlobalServices(config); - serviceCollection.AddMemoryCache();//使用内存缓存 - return serviceCollection; - } - - - /// - /// 注册 RegisterService.Start() - /// - private static IRegisterService RegisterServiceStart(bool autoScanExtensionCacheStrategies = false) - { - Console.WriteLine("[TryRegisterMiniCore] RegisterServiceStart() 开始"); - - //注册 - var senparcSetting = CreateSenparcSetting(); - return Senparc.CO2NET.Register.UseSenparcGlobal(senparcSetting, reg => { }, autoScanExtensionCacheStrategies); - } - #endregion - } -} +using log4net; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Senparc.CO2NET; +using Senparc.CO2NET.RegisterServices; +using Senparc.CO2NET.Trace; +using Senparc.Ncf.Core.AssembleScan; +using Senparc.Ncf.Core.DI; +using System; +using System.Linq; +using System.Reflection; + +namespace Senparc.Ncf.Core +{ + /// + /// NCF registration program + /// + public static class Register + { + /// + /// Scan interfaces for auto dependency injection + /// + public static IServiceCollection ScanAssamblesForAutoDI(this IServiceCollection services) + { + // Traverse all assemblies for registration + Action action = assembly => + { + var areaRegisterTypes = assembly.GetTypes() //.GetExportedTypes() + .Where(z => !z.IsAbstract && !z.IsInterface && z.GetInterface(nameof(IAutoDI)) != null) + .ToArray(); + + DILifecycleType dILifecycleType = DILifecycleType.Scoped; + + foreach (var registerType in areaRegisterTypes) + { + try + { + // Check attribute tags + var attrs = System.Attribute.GetCustomAttributes(registerType, false).Where(z => z is AutoDITypeAttribute); + if (attrs.Count() > 0) + { + var attr = attrs.First() as AutoDITypeAttribute; + dILifecycleType = attr.DILifecycleType; // Use specified way + } + + // Set different DI lifecycles for different types + switch (dILifecycleType) + { + case DILifecycleType.Scoped: + services.AddScoped(registerType); + break; + case DILifecycleType.Singleton: + services.AddSingleton(registerType); + break; + case DILifecycleType.Transient: + services.AddTransient(registerType); + break; + default: + throw new NotImplementedException($"Unhandled DILifecycleType: {dILifecycleType.ToString()}"); + } + } + catch (Exception ex) + { + SenparcTrace.BaseExceptionLog(ex); + } + } + }; + + AssembleScanHelper.AddAssembleScanItem(action, true); + + return services; + } + + + #region TryRegisterMiniCore + /// + /// Perform auto-registration with minimal process, suitable for unit tests or Code First commands in limited environments. Do not use in production! + /// If already registered, returns null + /// + public static IRegisterService TryRegisterMiniCore(Action servicesAction = null, Action appAction = null) + { + //Initialize project + if (!Senparc.CO2NET.RegisterServices.RegisterServiceExtension.SenparcGlobalServicesRegistered) + { + try + { + var repository = LogManager.CreateRepository("NETCoreRepository");//Load Log config file + Console.WriteLine("[TryRegisterMiniCore] NETCoreRepository registration completed"); + } + catch + { + Console.WriteLine("NETCoreRepository already configured, skip duplicate configuration"); + } + //Allow additional external service registrations + var services = RegisterServiceCollection(); + + Console.WriteLine("[TryRegisterMiniCore] RegisterServiceCollection() 完成"); + + servicesAction?.Invoke(services); + Console.WriteLine("[TryRegisterMiniCore] servicesAction?.Invoke(services) 完成"); + + //Allow additional external app configuration + var serviceProvider = services.BuildServiceProvider(); + Console.WriteLine("[TryRegisterMiniCore] services.BuildServiceProvider() 完成"); + + IApplicationBuilder app = new ApplicationBuilder(serviceProvider); + appAction?.Invoke(app); + Console.WriteLine("[TryRegisterMiniCore] appAction?.Invoke(app) 完成"); + + + return RegisterServiceStart(); + } + return null; + } + + private static SenparcSetting CreateSenparcSetting() + { + return new SenparcSetting() { IsDebug = true }; + } + + /// + /// Register IServiceCollection and MemoryCache + /// + private static IServiceCollection RegisterServiceCollection() + { + var serviceCollection = new ServiceCollection(); + var configBuilder = new ConfigurationBuilder(); + var config = configBuilder.Build(); + serviceCollection.AddSenparcGlobalServices(config); + serviceCollection.AddMemoryCache();//Use in-memory cache + return serviceCollection; + } + + + /// + /// Register RegisterService.Start() + /// + private static IRegisterService RegisterServiceStart(bool autoScanExtensionCacheStrategies = false) + { + Console.WriteLine("[TryRegisterMiniCore] RegisterServiceStart() 开始"); + + //Register + var senparcSetting = CreateSenparcSetting(); + return Senparc.CO2NET.Register.UseSenparcGlobal(senparcSetting, reg => { }, autoScanExtensionCacheStrategies); + } + #endregion + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Utility/XmlDataContext.cs b/src/Basic/Senparc.Ncf.Core/Utility/XmlDataContext.cs index bf35ac633..5b7e627fb 100644 --- a/src/Basic/Senparc.Ncf.Core/Utility/XmlDataContext.cs +++ b/src/Basic/Senparc.Ncf.Core/Utility/XmlDataContext.cs @@ -330,7 +330,7 @@ public bool Insert(TEntity entity) where TEntity : class { value = maxId; maxIdAttr.Value = maxId.ToString(); - prop.SetValue(entity, maxId, null); //把最新的Id设置到实体 + prop.SetValue(entity, maxId, null); //Set the latest Id to the entity } switch (prop.PropertyType.Name) @@ -352,7 +352,7 @@ public bool Insert(TEntity entity) where TEntity : class } /// - /// 使用此方法须确定数据库中没有重复项(或有主键) + /// Use this method only when the database has no duplicates (or has a primary key) /// /// /// @@ -362,7 +362,7 @@ public bool Delete(TEntity entity) where TEntity : class try { string entityName = typeof(TEntity).Name; - XElement xml = this.GetXElement(GetXmlFullApplicationPath(entityName));//载入文档 + XElement xml = this.GetXElement(GetXmlFullApplicationPath(entityName));//Load document var idProp = typeof(TEntity).GetProperty("Id"); if (idProp == null) @@ -375,9 +375,9 @@ public bool Delete(TEntity entity) where TEntity : class XElement delElement = xml.Elements(entityName).FirstOrDefault(z => z.Element("Id").Value == id.ToString()); if (delElement == null) { - return true;//throw new Exception("待更新数据不存在!"); + return true;//throw new Exception("The data to update does not exist!"); } - delElement.Remove();//删除节点 + delElement.Remove();//Remove node xml.Save(this.GetMapPath(this.GetXmlFullApplicationPath(entityName))); return true; @@ -401,7 +401,7 @@ public bool Delete(TEntity entity) where TEntity : class } /// - /// 使用此方法须确定数据库中没有重复项(或有主键) + /// Use this method only when the database has no duplicates (or has a primary key) /// /// /// @@ -411,7 +411,7 @@ public bool Delete(TEntity entity) where TEntity : class try { string entityName = typeof(TEntity).Name; - XElement xml = this.GetXElement(GetXmlFullApplicationPath(entityName));//载入文档 + XElement xml = this.GetXElement(GetXmlFullApplicationPath(entityName));//Load document var idProp = typeof(TEntity).GetProperty("Id"); if (idProp == null) @@ -423,7 +423,7 @@ public bool Delete(TEntity entity) where TEntity : class XElement item = xml.Elements(entityName).FirstOrDefault(z => z.Element("Id").Value == id.ToString()); if (item == null) { - return null;//throw new Exception("待更新数据不存在!"); + return null;//throw new Exception("The data to update does not exist!"); } TEntity result = ConvertXmlToEntity(item); diff --git a/src/Basic/Senparc.Ncf.Core/VersionManager.cs b/src/Basic/Senparc.Ncf.Core/VersionManager.cs index 9ade7be6b..b5fa65dd8 100644 --- a/src/Basic/Senparc.Ncf.Core/VersionManager.cs +++ b/src/Basic/Senparc.Ncf.Core/VersionManager.cs @@ -1,82 +1,82 @@ -using Senparc.CO2NET.Extensions; -using Senparc.Ncf.Utility.Helpers; -using System; -using System.ComponentModel.Design; -using System.Globalization; -using System.Reflection; -using System.Text; - -namespace Senparc.Ncf.Core -{ - public class VersionManager - { - /// - /// 返回版本信息 - /// - /// - public static string GetVersionNote(string ncfVersion = null, string note = null, bool showOpenSourceInfo = true) - { - ncfVersion ??= Assembly.GetExecutingAssembly().GetName().Version.ToString(); - - var sb = new StringBuilder(); - sb.AppendLine(""); - sb.AppendLine(" _______ ______ _______"); - sb.AppendLine(" | | || || ___|"); - sb.AppendLine(" | || ---|| ___|"); - sb.AppendLine(" |__|____||______||___|"); - sb.AppendLine(""); - sb.AppendLine($" NeuCharFramework {ncfVersion}"); - sb.AppendLine($" Apache License 2.0 "); - sb.AppendLine(""); - - GlobalCulture.Create() - .SetEnglish(() => - { - sb.AppendLine(" AI Native / Domain-Driven Design System"); - sb.AppendLine(""); - if (showOpenSourceInfo) - { - - sb.AppendLine(" OpenSource Template:https://github.com/NeuCharFramework/NCF"); - //sb.AppendLine(" OpenSource Template:https://gitee.com/NeuCharFramework/NCF"); - //sb.AppendLine(" Base Module Source Code:https://github.com/NeuCharFramework/NcfPackageSources"); - sb.AppendLine(" Document:https://doc.ncf.pub/"); - } - }) - .SetChinese(() => - { - sb.AppendLine(" AI 原生 / DDD(Domain-Driven Design)系统"); - sb.AppendLine(""); - if (showOpenSourceInfo) - { - sb.AppendLine(" 开源模板:https://github.com/NeuCharFramework/NCF"); - sb.AppendLine(" 开源模板:https://gitee.com/NeuCharFramework/NCF"); - //sb.AppendLine(" 基础模块源码:https://github.com/NeuCharFramework/NcfPackageSources"); - sb.AppendLine(" 文档:https://doc.ncf.pub/"); - } - }) - .InvokeDefault(); - - sb.AppendLine(""); - if (!note.IsNullOrEmpty()) - { - sb.AppendLine(note); - sb.AppendLine(""); - } - return sb.ToString(); - } - - public static void ShowSuccessTip(string note, string systemVersion = null, bool showOpenSourceInfo = true) - { - //输出启动成功标志 - systemVersion ??= Assembly.GetExecutingAssembly().GetName().Version.ToString(); - var startupNote = Senparc.Ncf.Core.VersionManager.GetVersionNote(systemVersion, note, showOpenSourceInfo); - Console.WriteLine("----------------------------------------------------------"); - Console.ForegroundColor = ConsoleColor.DarkMagenta; - Console.WriteLine(startupNote); - Console.ResetColor(); - Console.WriteLine("----------------------------------------------------------"); - - } - } -} +using Senparc.CO2NET.Extensions; +using Senparc.Ncf.Utility.Helpers; +using System; +using System.ComponentModel.Design; +using System.Globalization; +using System.Reflection; +using System.Text; + +namespace Senparc.Ncf.Core +{ + public class VersionManager + { + /// + /// 返回版本信息 + /// + /// + public static string GetVersionNote(string ncfVersion = null, string note = null, bool showOpenSourceInfo = true) + { + ncfVersion ??= Assembly.GetExecutingAssembly().GetName().Version.ToString(); + + var sb = new StringBuilder(); + sb.AppendLine(""); + sb.AppendLine(" _______ ______ _______"); + sb.AppendLine(" | | || || ___|"); + sb.AppendLine(" | || ---|| ___|"); + sb.AppendLine(" |__|____||______||___|"); + sb.AppendLine(""); + sb.AppendLine($" NeuCharFramework {ncfVersion}"); + sb.AppendLine($" Apache License 2.0 "); + sb.AppendLine(""); + + GlobalCulture.Create() + .SetEnglish(() => + { + sb.AppendLine(" AI Native / Domain-Driven Design System"); + sb.AppendLine(""); + if (showOpenSourceInfo) + { + + sb.AppendLine(" OpenSource Template:https://github.com/NeuCharFramework/NCF"); + //sb.AppendLine(" OpenSource Template:https://gitee.com/NeuCharFramework/NCF"); + //sb.AppendLine(" Base Module Source Code:https://github.com/NeuCharFramework/NcfPackageSources"); + sb.AppendLine(" Document:https://doc.ncf.pub/"); + } + }) + .SetChinese(() => + { + sb.AppendLine(" AI Native / DDD (Domain-Driven Design) System"); + sb.AppendLine(""); + if (showOpenSourceInfo) + { + sb.AppendLine(" Open source template: https://github.com/NeuCharFramework/NCF"); + sb.AppendLine(" Open source template: https://gitee.com/NeuCharFramework/NCF"); + //sb.AppendLine(" 基础模块源码:https://github.com/NeuCharFramework/NcfPackageSources"); + sb.AppendLine(" Documentation: https://doc.ncf.pub/"); + } + }) + .InvokeDefault(); + + sb.AppendLine(""); + if (!note.IsNullOrEmpty()) + { + sb.AppendLine(note); + sb.AppendLine(""); + } + return sb.ToString(); + } + + public static void ShowSuccessTip(string note, string systemVersion = null, bool showOpenSourceInfo = true) + { + // Output startup success flag + systemVersion ??= Assembly.GetExecutingAssembly().GetName().Version.ToString(); + var startupNote = Senparc.Ncf.Core.VersionManager.GetVersionNote(systemVersion, note, showOpenSourceInfo); + Console.WriteLine("----------------------------------------------------------"); + Console.ForegroundColor = ConsoleColor.DarkMagenta; + Console.WriteLine(startupNote); + Console.ResetColor(); + Console.WriteLine("----------------------------------------------------------"); + + } + } +} From 6fcd7b7f12ca23b47a0143f71872ed84c7f110e6 Mon Sep 17 00:00:00 2001 From: JeffreySu Date: Fri, 3 Apr 2026 12:39:30 -0700 Subject: [PATCH 19/21] Add translation state file with initial index and total count --- .gitignore | 1307 + .translation_cache.json | 1 + .translation_state.json | 2194 + .translation_state_v2.json | 4 + EVENTBUS_QUICK_REFERENCE.md | 1 + .../Admin/AdminPageModelBase.cs | 114 +- .../Admin/AdminXscfModulePageModelBase.cs | 132 +- .../Admin/Filters/AdminAuthorizeAttribute.cs | 52 +- .../AuthenticationAsyncPageFilterAttribute.cs | 160 +- .../AuthenticationResultFilterAttribute.cs | 234 +- .../Filters/CustomerResourceAttribute.cs | 50 +- src/Basic/Senparc.Ncf.Core/AjaxReturnModel.cs | 72 +- .../AppServices/AppServiceLogger.cs | 96 +- .../Exceptions/BaseAppServiceException.cs | 38 +- .../AppServices/FunctionRenderAttribute.cs | 62 +- .../AppServices/FunctionRenderSymbolHelper.cs | 8 +- .../AppServices/Helpers/AppServiceHelper.cs | 346 +- .../AppServices/Models/AppResponseBase.cs | 8 +- .../AppServices/Models/StringAppResponse.cs | 26 +- src/Basic/Senparc.Ncf.Core/Area/AreaData.cs | 10 +- .../Areas/AreaPageMenuItem.cs | 74 +- .../Senparc.Ncf.Core/Areas/IAreaRegister.cs | 66 +- .../AssembleScan/AssembleScanHelper.cs | 2 +- .../AssembleScan/AssembleScanItem.cs | 116 +- .../Authorization/ICheckPermission.cs | 42 +- .../Authorization/PermissionAttribute.cs | 50 +- .../PermissionFilterAttribute.cs | 74 +- .../Authorization/PermissionHandler.cs | 106 +- .../Authorization/PermissionRequirement.cs | 70 +- .../AutoMapper/SystemProfile.cs | 72 +- .../Senparc.Ncf.Core/Cache/AreaDataCache.cs | 136 +- .../Cache/BaseCache/BaseCache.cs | 22 +- .../BaseDictionary/BaseDictionaryCache.cs | 26 +- .../BaseStringDictionaryCache.cs | 92 +- .../Cache/BaseCache/ICacheData.cs | 28 +- .../BaseCacheBindable.cs | 10 +- .../CacheDataMessageQueue.cs | 286 +- .../CacheDataMessageQueueItem.cs | 82 +- .../Senparc.Ncf.Core/Cache/CommonDataCache.cs | 82 +- .../EntityCache/FullSystemConfigCache.cs | 182 +- .../Cache/Extensions/BaseCacheExtensions.cs | 8 +- .../Cache/Interface/IBaseCache.cs | 4 +- .../Cache/Interface/IBaseCacheStrategy.cs | 66 +- .../Senparc.Ncf.Core/Cache/MethodCache.cs | 26 +- .../Cache/MobileLoginCodeCache.cs | 180 +- .../Cache/QueueCache/DemoLoginKeyCache.cs | 8 +- .../Cache/QueueCache/OAuthCodeCache.cs | 2 +- .../Cache/QueueCache/PhoneCheckCodeCache.cs | 4 +- .../Cache/QueueCache/QrCodeBaseData.cs | 68 +- .../Cache/QueueCache/QrCodeGroupCache.cs | 6 +- .../Cache/QueueCache/QrCodeLoginCache.cs | 8 +- .../Cache/QueueCache/QrCodeRegCache.cs | 6 +- .../Cache/QueueCache/QueueCache.cs | 42 +- .../Senparc.Ncf.Core/Config/AIPluginHub.cs | 10 +- .../Config/SiteConfig.Core.cs | 397 +- .../Exceptions/NcfDatabaseException.cs | 64 +- .../Exceptions/NcfUninstallException.cs | 42 +- .../AuthorizeAttributeExtensions.cs | 40 +- .../Extensions/ObjectExtensions.cs | 28 +- .../DataBaseModel/Dto/AdminUserInfoDto.cs | 64 +- .../Models/DataBaseModel/Dto/Base/DtoBase.cs | 126 +- .../Models/DataBaseModel/Dto/Base/IDtoBase.cs | 110 +- .../Models/DataBaseModel/Dto/SysButtonDto.cs | 80 +- .../Models/DataBaseModel/Dto/SysMenuDto.cs | 180 +- .../DataBaseModel/Dto/SysPermissionDto.cs | 50 +- .../Dto/SysRoleAdminUserInfoDto.cs | 58 +- .../Models/DataBaseModel/Dto/SysRoleDto.cs | 60 +- .../Models/DataBaseModel/Dto/XscfModuleDto.cs | 258 +- .../Base/BlankEntityTypeConfiguration.cs | 50 +- .../Mapping/Base/ConfigurationMappingBase.cs | 88 +- .../Models/DataBaseModel/SysButton.cs | 128 +- .../Models/DataBaseModel/SysMenu.cs | 212 +- .../Models/DataBaseModel/SysRole.cs | 126 +- .../DataBaseModel/SysRoleAdminUserInfo.cs | 86 +- .../Models/DataBaseModel/SysRolePermission.cs | 128 +- .../Models/DataBaseModel/SystemConfig.cs | 154 +- .../Models/DataBaseModel/XncfModule.cs | 172 +- .../Models/EntityBase/EntityBase.cs | 178 +- .../Models/EntityBase/IAggregateRoot.cs | 26 +- .../Models/EntityBase/IEntityBase.cs | 68 +- .../Models/EntityBase/IMultiTenancy.cs | 34 +- .../Models/EntityBase/ISoftDelete.cs | 34 +- .../Senparc.Ncf.Core/Models/EntitySetKeys.cs | 342 +- .../Models/ExtensionEntity.Core.cs | 756 +- .../Models/IValidatorEnvironment.cs | 48 +- .../DatabaseConfigurationFactory.cs | 8 +- .../Helpers/NcfDatabaseMigrationHelper.cs | 96 +- .../IDatabaseConfiguration.cs | 144 +- .../MultipleDatabaseType.cs | 48 +- .../XncfModules/IXncfDatabase.cs | 104 +- .../XncfModules/XncfDatabaseData.cs | 66 +- .../Models/NcfDbData/NcfDbData.cs | 62 +- .../Models/SenparcCoreSetting.Core.cs | 138 +- .../ISenparcEntitiesDbContext.cs | 66 +- .../SenparcEntitiesDbContextBase.cs | 38 +- .../Senparc.Ncf.Core/Models/VD/CommonVD.cs | 186 +- .../Models/VD/Razor/PageModelBase.cs | 302 +- .../MultiTenant/IIgnoreMulitTenant.cs | 26 +- .../MultiTenant/MultiTenantHelper.cs | 94 +- .../MultiTenant/RequestTenantInfo.cs | 100 +- .../MultiTenant/TenantRule.cs | 58 +- .../Properties/BindableBase.cs | 96 +- .../Utility/CacheEntity/FullEntityCache.cs | 86 +- .../Utility/CheckCodeHandle.cs | 290 +- .../Utility/CommonWebParts.cs | 424 +- .../Utility/XmlDataContext.cs | 16 +- .../Senparc.Ncf.Core/Validator/Validator.cs | 6 +- src/Basic/Senparc.Ncf.Core/VersionManager.cs | 4 +- .../WebApi/NcfWebApiHelper.cs | 150 +- .../WorkContext/AdminWorkContext.cs | 54 +- .../InMemoryDatabaseConfiguration.cs | 120 +- .../InMemoryDbContextOptionsBuilderForNcf.cs | 204 +- .../InMemoryOptionsExtensionForNcf.cs | 484 +- .../MySqlWithBackupDatabaseConfiguration.cs | 94 +- .../MySqlDatabaseConfiguration.cs | 104 +- .../OracleDatabaseConfiguration.cs | 176 +- .../OracleDatabaseConfigurationForV11.cs | 60 +- .../PostgreSQLDatabaseConfiguration.cs | 238 +- .../SqlServerDatabaseConfiguration.cs | 110 +- .../SqliteDatabaseConfiguration.cs | 156 +- .../SqliteMemoryDatabaseConfiguration.cs | 128 +- .../BySettingDatabaseConfiguration.cs | 78 +- .../DatabaseConfigurationBase.cs | 200 +- .../UnitTestDatabaseConfiguration.cs | 88 +- .../DbContextPools/MultipleDatabasePool.cs | 472 +- .../XncfDatabaseDbContextPool.cs | 118 +- .../Helpers/NcfDatabaseHelper.cs | 126 +- .../IMultipleMigrationDbContext.cs | 34 +- .../MultipleMigrationDbContextAttribute.cs | 106 +- .../Workaround.cs | 68 +- src/Basic/Senparc.Ncf.Database/Register.cs | 400 +- .../SenparcDatabaseConnectionConfigs.cs | 176 +- src/Basic/Senparc.Ncf.Log/LogUtility.cs | 90 +- .../Senparc.Ncf.Log/NLogExtension.Core.cs | 44 +- .../CustomBindingHelperExtentions.cs | 166 +- .../Senparc.Ncf.Mvc.UI/HtmlExtension.Core.cs | 256 +- .../UIHelpers/CurrentBsMenuExtensions.cs | 88 +- .../UIHelpers/GridViewActionExtension.cs | 132 +- .../UIHelpers/GridViewExtension.cs | 506 +- .../UIHelpers/OnClickSpanExtension.cs | 114 +- .../UIHelpers/PagerBarExtension.cs | 680 +- .../UIHelpers/RepeaterExtension.cs | 444 +- .../BaseRepoisitory/IRepositoryBase.cs | 358 +- .../BaseRepoisitory/RepositoryBase.cs | 1526 +- .../System/SysButtonRespository.cs | 94 +- .../System/SysRolePermissionRepository.cs | 88 +- src/Basic/Senparc.Ncf.SMS/Enums.cs | 86 +- .../SmsPlatform/SmsPlatform.cs | 114 +- .../SmsPlatform/SmsPlatform_Fissoft.cs | 268 +- .../SmsPlatform/SmsPlatform_JunMei.cs | 238 +- .../Common/EncryptionService.cs | 180 +- .../AutoDetectChangeContext.cs | 92 +- .../AutoDetectChangeContextExtension.cs | 74 +- .../ServiceBase/ClientServiceBase.cs | 74 +- .../ServiceBase/DtoServiceBase.cs | 262 +- .../ServiceBase/IServiceBase.cs | 174 +- .../ServiceBase/ResilientTransaction.cs | 70 +- .../ServiceBase/ServiceBase.cs | 1226 +- .../ServiceBase/ServiceDataBase.cs | 128 +- .../SignalrHubs/SignalrTicker.cs | 168 +- .../System/SysMenuService.cs | 754 +- .../System/SysRoleAdminUserInfoService.cs | 106 +- .../System/SysRolePermissionService.cs | 660 +- .../System/XncfModuleService.cs | 262 +- .../ServiceBase/ServiceDataBaseTests.cs | 252 +- .../Events/IEventBus.cs | 6 +- .../Events/IIntegrationEvent.cs | 26 +- .../Events/IIntegrationEventHandler.cs | 4 +- .../BaseNcfUnitTest.MockRepository.cs | 560 +- .../BaseNcfUnitTest.cs | 530 +- .../Database/NcfUnitTestDataDb.cs | 134 +- .../Database/NcfUnitTestEntities.cs | 102 +- .../Entities/DataList.cs | 196 +- .../UnitTestHelper.cs | 152 +- .../UnitTestSeedDataBuilder.cs | 66 +- .../DIExtension/Extensions.cs | 50 +- .../SenparcExpressionHelper.cs | 334 +- .../SenparcExpressionModifier.cs | 114 +- .../SenparcIQueryableExtension.cs | 174 +- .../Extensions/DateTimeExtensions.cs | 62 +- .../Extensions/StringExtensions.cs | 598 +- src/Basic/Senparc.Ncf.Utility/FileUtility.cs | 30 +- .../Helpers/GlobalCulture.cs | 272 +- .../Helpers/ReflectionHelper.cs | 150 +- src/Basic/Senparc.Ncf.Utility/IDCardValid.cs | 216 +- src/Basic/Senparc.Ncf.Utility/IPData.cs | 1116 +- .../ModelStateDictionaryExtension.cs | 44 +- .../Senparc.Ncf.Utility/RequestExtension.cs | 310 +- .../Senparc.Core.Utility/DateTimeUtility.cs | 180 +- .../Senparc.Core.Utility/DesUtility.cs | 128 +- .../Senparc.Core.Utility/Extensions.cs | 188 +- .../Senparc.Core.Utility/IoUtility.cs | 96 +- .../Senparc.Core.Utility/MD5.cs | 48 +- .../Senparc.Core.Utility/W3wp.cs | 112 +- .../Senparc.Ncf.Utility/SenparcHttpContext.cs | 160 +- .../XncfAutoConfigurationMappingAttribute.cs | 32 +- .../Attributes/XncfMethodAttribute.cs | 34 +- .../Attributes/XncfOrderAttribute.cs | 52 +- .../Attributes/XncfRegisterAttribute.cs | 24 +- .../AutoMapper/XncfModuleProfile.cs | 64 +- .../SenparcDesignTimeDbContextFactoryBase.cs | 508 +- .../Database/XncfDatabaseDbContext.cs | 442 +- src/Basic/Senparc.Ncf.XncfBase/Enums.cs | 34 +- .../Exceptions/XncfFunctionException.cs | 42 +- .../FunctionRenders/FunctionAppRequestBase.cs | 82 +- .../FunctionRenderCollection.cs | 250 +- .../Functions/EnumType.cs | 44 +- .../Functions/FunctionHelper.cs | 488 +- .../Parameters/FunctionParameterInfo.cs | 164 +- .../Functions/Parameters/SelectionList.cs | 252 +- .../Helpers/StartupHelper.cs | 66 +- .../Interfaces/IXncfMiddleware.cs | 40 +- .../IXncfRazorRuntimeCompilation.cs | 34 +- .../Interfaces/IXncfRegister.cs | 288 +- .../Interfaces/IXncfThread.cs | 38 +- .../Threads/ThreadInfo.cs | 140 +- .../Threads/XncfThreadBuilder.cs | 170 +- .../VersionManager/VersionHelper.cs | 352 +- .../VersionManager/VersionInfo.cs | 150 +- .../XncfRegisterManager.cs | 190 +- .../FileExtension.cs | 128 +- .../Pages/AIAgentsHub/DatabaseSample.cshtml | 200 +- .../AIAgentsHubSenparcEntities.cs | 48 +- .../Domain/Models/DatabaseModel/Color.cs | 162 +- .../Models/DatabaseModel/Dto/ColorDto.cs | 44 +- .../AIAgentsHubSenparcEntities_Dm.cs | 82 +- .../AIAgentsHubSenparcEntities_MySql.cs | 80 +- .../AIAgentsHubSenparcEntities_Oracle.cs | 82 +- .../AIAgentsHubSenparcEntities_PostgreSQL.cs | 82 +- .../AIAgentsHubSenparcEntities_SQLite.cs | 76 +- .../AIAgentsHubSenparcEntities_SqlServer.cs | 82 +- .../SenparcDbContextFactoryConfig.cs | 82 +- .../Domain/Services/ColorService.cs | 136 +- .../OHS/Local/AppService/ApiAppService.cs | 125 +- .../OHS/Local/AppService/ColorAppService.cs | 86 +- .../Local/AppService/MyFuctionAppService.cs | 168 +- .../OHS/Local/PL/ColorResponse.cs | 72 +- .../Senparc.Xncf.AIAgentsHub/Register.Area.cs | 88 +- .../Register.Database.cs | 82 +- .../Senparc.Xncf.AIAgentsHub/Register.cs | 178 +- .../Areas/Admin/Pages/AIKernel/Index.cshtml | 588 +- .../Areas/Admin/Pages/AIVector/Index.cshtml | 6 +- .../Areas/Admin/Pages/Dashboard/Index.cshtml | 38 +- .../DatabaseModel/AIKernelSenparcEntities.cs | 50 +- .../Domain/Models/DatabaseModel/AIModel.cs | 312 +- .../Domain/Models/DatabaseModel/AIVector.cs | 20 +- .../Models/DatabaseModel/Dto/AIModelDto.cs | 210 +- .../Models/DatabaseModel/Dto/AIVectorDto.cs | 18 +- .../Domain/Models/Enums.cs | 46 +- .../Domain/Models/Extensions/NeuCharModel.cs | 104 +- .../AIKernelSenparcEntities_Dm.cs | 80 +- .../AIKernelSenparcEntities_MySql.cs | 78 +- .../AIKernelSenparcEntities_Oracle.cs | 80 +- .../AIKernelSenparcEntities_PostgreSQL.cs | 80 +- .../AIKernelSenparcEntities_SQLite.cs | 80 +- .../AIKernelSenparcEntities_SqlServer.cs | 80 +- .../SenparcDbContextFactoryConfig.cs | 82 +- .../Domain/Services/AIModelService.cs | 584 +- .../Domain/Services/AIVectorService.cs | 16 +- .../Domain/Services/ColorService.cs | 136 +- .../OHS/Local/AppService/AIModelAppService.cs | 512 +- .../AppService/AIModelStudioAppService.cs | 122 +- .../Local/AppService/AIVectorAppService.cs | 16 +- .../OHS/Local/AppService/ColorAppService.cs | 86 +- .../OHS/Local/PL/AIModelStudioRequest.cs | 186 +- .../OHS/Local/PL/AIModel_Request.cs | 370 +- .../OHS/Local/PL/AIVector_Request.cs | 34 +- .../OHS/Local/PL/ColorResponse.cs | 72 +- .../OHS/Local/PL/MyFunctionRequest.cs | 76 +- .../Senparc.Xncf.AIKernel/Register.Area.cs | 102 +- .../Register.Database.cs | 84 +- .../Senparc.Xncf.AIKernel/Register.cs | 240 +- .../wwwroot/js/Admin/AIKernel/index.js | 646 +- .../wwwroot/js/Admin/AIVector/index.js | 6 +- .../wwwroot/js/Admin/Dashboard/Index.js | 66 +- .../AgentTemplatePrintMessageMiddleware.cs | 120 +- .../AppService/AgentTemplateAppService.cs | 52 +- .../AppService/ChatGroupAppService.cs | 64 +- .../AppService/ChatTaskAppService.cs | 2 +- .../PromptOptimizationAppService.cs | 22 +- .../Application/DTOs/AgentTemplateRequest.cs | 4 +- .../Application/DTOs/ChatGroupRequest.cs | 26 +- .../PromptOptimizationChatTaskHandler.cs | 4 +- .../PromptOptimizationHandler.cs | 6 +- .../Admin/Pages/AgentsManager/Index.cshtml | 3586 +- .../Models/DatabaseModel/AgentTemplate.cs | 438 +- .../AgentsManagerSenparcEntities.cs | 72 +- .../Domain/Models/DatabaseModel/ChatGroup.cs | 236 +- .../Models/DatabaseModel/ChatGroupHistory.cs | 230 +- .../Models/DatabaseModel/ChatGroupMember.cs | 164 +- .../Domain/Models/DatabaseModel/ChatTask.cs | 236 +- .../DatabaseModel/Dto/AgentTemplateDto.cs | 198 +- .../Models/DatabaseModel/Dto/ChatGroupDto.cs | 158 +- .../DatabaseModel/Dto/ChatGroupHistoryDto.cs | 156 +- .../DatabaseModel/Dto/ChatGroupMemberDto.cs | 116 +- .../Models/DatabaseModel/Dto/ChatTaskDto.cs | 192 +- .../Mapping/ChatGroupConfigurationMapping.cs | 60 +- .../ChatGroupMemberConfigurationMapping.cs | 52 +- .../AgentsManagerSenparcEntities_Dm.cs | 84 +- .../AgentsManagerSenparcEntities_MySql.cs | 80 +- .../AgentsManagerSenparcEntities_Oracle.cs | 82 +- ...AgentsManagerSenparcEntities_PostgreSQL.cs | 82 +- .../AgentsManagerSenparcEntities_SQLite.cs | 82 +- .../AgentsManagerSenparcEntities_SqlServer.cs | 84 +- .../SenparcDbContextFactoryConfig.cs | 82 +- .../Domain/Services/AIPlugins/CrawlPlugin.cs | 524 +- .../Services/AIPlugins/FormatorPlugin.cs | 154 +- .../AIPlugins/PromptCatalyzerPlugin.cs | 6 +- .../AIPlugins/PromptOptimizationPlugin.cs | 30 +- .../Domain/Services/AgentsTemplateService.cs | 114 +- .../Services/ChatGroupHistoryService.cs | 108 +- .../Domain/Services/ChatGroupService.cs | 88 +- .../Domain/Services/ChatTaskService.cs | 134 +- .../Services/PromptOptimizationAgentBridge.cs | 18 +- ...PromptOptimizationKernelFallbackService.cs | 4 +- .../Services/PromptOptimizationService.cs | 38 +- .../Local/AppService/ApiAuthorizeAttribute.cs | 14 +- .../PromptCatalyzerInitController.cs | 28 +- .../PromptOptimizationController.cs | 12 +- .../Register.Area.cs | 88 +- .../Register.Database.cs | 90 +- .../Register.Thread.cs | 128 +- .../Senparc.Xncf.AgentsManager/Register.cs | 308 +- .../wwwroot/css/AgentsManager/index.css | 3652 +- .../wwwroot/js/AgentsManager/axios.js | 172 +- .../wwwroot/js/AgentsManager/index.js | 6738 +- .../AgentsManagerTestBase.cs | 490 +- .../Domain/Services/ChatGroupServiceTests.cs | 130 +- .../Functions/LaunchApp.cs | 261 +- .../Senparc.Xncf.Application/Register.cs | 92 +- .../OHS/Local/NameSpaceAppService.cs | 428 +- .../OHS/PL/NameSpaceRequest.cs | 116 +- .../Senparc.Xncf.ChangeNamespace/Register.cs | 94 +- .../Senparc.Xncf.Dapr/Client/DaprClient.cs | 770 +- .../Client/DaprClientOptions.cs | 158 +- .../DaprClientServiceCollectionExtensions.cs | 72 +- .../Utils/Serializer/ISerializer.cs | 66 +- .../Utils/Serializer/Serializer.cs | 196 +- .../Domain/Models/DataBaseModel/DbConfig.cs | 134 +- .../DatabaseToolkitSenparcEntities_Dm.cs | 96 +- .../DatabaseToolkitSenparcEntities_MySql.cs | 92 +- .../DatabaseToolkitSenparcEntities_Oracle.cs | 96 +- ...tabaseToolkitSenparcEntities_PostgreSQL.cs | 96 +- ...atabaseToolkitSenparcEntities_SqlServer.cs | 96 +- .../DatabaseToolkitSenparcEntities_Sqlite.cs | 92 +- .../AIAgentIntegrationAppService.cs | 12 +- .../AppService/DatabaseBackupAppService.cs | 276 +- .../AppService/DatabaseOperationAppService.cs | 28 +- .../DatabaseSchemaQueryAppService.cs | 20 +- .../AppService/DatabaseUpdateAppService.cs | 144 +- .../Local/Models/DatabaseSchemaMetadata.cs | 82 +- .../OHS/Local/Services/DatabaseExecutor.cs | 10 +- .../DatabaseSchemaMetadataProvider.cs | 94 +- .../Register.Database.cs | 88 +- .../Register.Thread.cs | 278 +- .../Senparc.Xncf.DatabaseToolkit/Register.cs | 116 +- .../Pages/DynamicData/DataSheetSet.cshtml | 382 +- .../Pages/DynamicData/DatabaseSample.cshtml | 200 +- .../Admin/Pages/DynamicData/Index.cshtml | 86 +- .../Admin/Pages/DynamicData/LayoutSet.cshtml | 2933 +- .../Pages/DynamicData/renderLayoutPage.cshtml | 263 +- .../Domain/Models/DatabaseModel/Color.cs | 162 +- .../Models/DatabaseModel/ColumnMetadata.cs | 140 +- .../Models/DatabaseModel/Dto/ColorDto.cs | 44 +- .../DatabaseModel/Dto/ColumnMetadataDto.cs | 116 +- .../Models/DatabaseModel/Dto/TableDataDto.cs | 82 +- .../DatabaseModel/Dto/TableMetadataDto.cs | 76 +- .../DynamicDataSenparcEntities.cs | 58 +- .../Mapping/DynamicData_Mapping.cs | 118 +- .../Domain/Models/DatabaseModel/TableData.cs | 110 +- .../Models/DatabaseModel/TableMetadata.cs | 98 +- .../Domain/Models/Extensions/DataTemplate.cs | 52 +- .../DynamicDataSenparcEntities_Dm.cs | 82 +- .../DynamicDataSenparcEntities_MySql.cs | 80 +- .../DynamicDataSenparcEntities_Oracle.cs | 82 +- .../DynamicDataSenparcEntities_PostgreSQL.cs | 82 +- .../DynamicDataSenparcEntities_SQLite.cs | 82 +- .../DynamicDataSenparcEntities_SqlServer.cs | 82 +- .../SenparcDbContextFactoryConfig.cs | 82 +- .../Domain/Services/ColorService.cs | 136 +- .../Domain/Services/ColumnMetadataService.cs | 164 +- .../Domain/Services/TableDataService.cs | 242 +- .../OHS/Local/AppService/ApiAppService.cs | 130 +- .../OHS/Local/AppService/ColorAppService.cs | 86 +- .../Local/AppService/MyFuctionAppService.cs | 174 +- .../OHS/Local/PL/ColorResponse.cs | 72 +- .../OHS/Local/PL/MyFunctionRequest.cs | 76 +- .../Senparc.Xncf.DynamicData/Register.Area.cs | 102 +- .../Register.Database.cs | 92 +- .../Senparc.Xncf.DynamicData/Register.cs | 226 +- .../wwwroot/css/DynamicData/dataSheetSet.css | 504 +- .../wwwroot/css/DynamicData/interfaceSet.css | 328 +- .../wwwroot/css/DynamicData/layoutSet.css | 1160 +- .../css/DynamicData/renderLayoutPage.css | 214 +- .../wwwroot/js/DynamicData/axios.js | 206 +- .../wwwroot/js/DynamicData/dataSheetSet.js | 678 +- .../wwwroot/js/DynamicData/interfaceSet.js | 250 +- .../wwwroot/js/DynamicData/layoutSet.js | 2372 +- .../js/DynamicData/renderLayoutPage.js | 252 +- .../BaseDynamicDataTest.cs | 231 +- .../Services/ColumnMetadataServiceTests.cs | 94 +- .../Domain/Services/TableDataTests.cs | 134 +- .../Pages/FileManager/DatabaseSample.cshtml | 200 +- .../Domain/Models/DatabaseModel/Color.cs | 162 +- .../Models/DatabaseModel/Dto/ColorDto.cs | 44 +- .../Models/DatabaseModel/Dto/NcfFileDto.cs | 52 +- .../FileManagerSenparcEntities.cs | 60 +- .../Domain/Models/DatabaseModel/NcfFile.cs | 94 +- .../Domain/Models/DatabaseModel/NcfFolder.cs | 6 +- .../FileManagerSenparcEntities_Dm.cs | 82 +- .../FileManagerSenparcEntities_MySql.cs | 80 +- .../FileManagerSenparcEntities_Oracle.cs | 82 +- .../FileManagerSenparcEntities_PostgreSQL.cs | 82 +- .../FileManagerSenparcEntities_SQLite.cs | 82 +- .../FileManagerSenparcEntities_SqlServer.cs | 82 +- .../SenparcDbContextFactoryConfig.cs | 82 +- .../Domain/Services/ColorService.cs | 136 +- .../Domain/Services/NcfFileService.cs | 284 +- .../OHS/Local/AppService/ApiAppService.cs | 128 +- .../OHS/Local/AppService/ColorAppService.cs | 86 +- .../AppService/FileTemplateAppService.cs | 16 +- .../Local/AppService/MyFuctionAppService.cs | 174 +- .../OHS/Local/PL/ColorResponse.cs | 72 +- .../OHS/Local/PL/MyFunctionRequest.cs | 76 +- .../Senparc.Xncf.FileManager/Register.Area.cs | 90 +- .../Register.Database.cs | 84 +- .../Senparc.Xncf.FileManager/Register.cs | 220 +- .../wwwroot/js/Admin/FileManager/index.js | 220 +- .../Admin/Pages/KnowledgeBase/Index.cshtml | 612 +- .../Pages/KnowledgeBase/RecallTest.cshtml | 4 +- .../Domain/Models/DatabaseModel/Color.cs | 162 +- .../Config/AllowFileExtension.cs | 8 +- .../Config/StaticResourceSetting.cs | 10 +- .../Models/DatabaseModel/Dto/ColorDto.cs | 44 +- .../DatabaseModel/Dto/KnowledgeBaseDto.cs | 10 +- .../DatabaseModel/Dto/KnowledgeBaseItemDto.cs | 10 +- .../Models/DatabaseModel/KnowledgeBase.cs | 14 +- .../Models/DatabaseModel/KnowledgeBaseItem.cs | 20 +- .../KnowledgeBaseSenparcEntities.cs | 54 +- .../Request/KnowledgeBaseItemRequest.cs | 6 +- .../Request/KnowledgeBaseRequest.cs | 8 +- .../KnowledgeBaseSenparcEntities_Dm.cs | 82 +- .../KnowledgeBaseSenparcEntities_MySql.cs | 80 +- .../KnowledgeBaseSenparcEntities_Oracle.cs | 82 +- ...KnowledgeBaseSenparcEntities_PostgreSQL.cs | 82 +- .../KnowledgeBaseSenparcEntities_SQLite.cs | 82 +- .../KnowledgeBaseSenparcEntities_SqlServer.cs | 82 +- .../SenparcDbContextFactoryConfig.cs | 82 +- .../Domain/Services/ColorService.cs | 136 +- .../Domain/Services/KnowledgeBaseService.cs | 96 +- .../Services/KnowledgeBasesItemService.cs | 4 +- .../OHS/Local/AppService/ApiAppService.cs | 128 +- .../OHS/Local/AppService/ColorAppService.cs | 86 +- .../OHS/Local/AppService/CommonService.cs | 2 +- .../AppService/KnowledgeBaseAppService.cs | 16 +- .../AppService/KnowledgeBaseItemAppService.cs | 6 +- .../Local/AppService/MyFuctionAppService.cs | 174 +- .../Local/AppService/RecallTestAppService.cs | 2 +- .../OHS/Local/PL/ColorResponse.cs | 72 +- .../OHS/Local/PL/MyFunctionRequest.cs | 76 +- .../PL/Request/KnowledgeBasesDetailRequest.cs | 6 +- .../OHS/Local/PL/Request/RecallTestRequest.cs | 4 +- .../Register.Area.cs | 184 +- .../Register.Database.cs | 84 +- .../Senparc.Xncf.KnowledgeBase/Register.cs | 202 +- .../KnowledgeBase/KnowledgeBase.css | 14 +- .../KnowledgeBasesDetail.css | 2 +- .../KnowledgeBase/RecallTest/RecallTest.css | 4 +- .../Pages/KnowledgeBase/knowledgeBase.js | 166 +- .../knowledgeBasesDetail.js | 42 +- .../Pages/RecallTest/recallTest.js | 6 +- .../Events/MCPEndpointEvents.cs | 26 +- .../Admin/Pages/MCP/DatabaseSample.cshtml | 200 +- .../Areas/Admin/Pages/MCP/Index.cshtml | 86 +- .../Domain/Models/DatabaseModel/Color.cs | 162 +- .../Models/DatabaseModel/Dto/ColorDto.cs | 44 +- .../Models/DatabaseModel/MCPEndpoint.cs | 38 +- .../DatabaseModel/MCPSenparcEntities.cs | 54 +- .../MultipleDatabase/MCPSenparcEntities_Dm.cs | 82 +- .../MCPSenparcEntities_MySql.cs | 80 +- .../MCPSenparcEntities_Oracle.cs | 82 +- .../MCPSenparcEntities_PostgreSQL.cs | 82 +- .../MCPSenparcEntities_SQLite.cs | 82 +- .../MCPSenparcEntities_SqlServer.cs | 82 +- .../SenparcDbContextFactoryConfig.cs | 82 +- .../Domain/Services/ColorService.cs | 136 +- .../Domain/Services/MCPEndpointService.cs | 14 +- .../Domain/Services/McpServerService.cs | 388 +- .../OHS/Local/AppService/ApiAppService.cs | 128 +- .../OHS/Local/AppService/ColorAppService.cs | 86 +- .../Local/AppService/MCPEndpointAppService.cs | 30 +- .../Local/AppService/MyFuctionAppService.cs | 850 +- .../OHS/Local/PL/ColorResponse.cs | 72 +- .../OHS/Local/PL/MyFunctionRequest.cs | 154 +- .../Senparc.Xncf.MCP/Register.Area.cs | 88 +- .../Senparc.Xncf.MCP/Register.Database.cs | 84 +- src/Extensions/Senparc.Xncf.MCP/Register.cs | 574 +- .../Events/PromptInitEvents.cs | 6 +- .../Events/PromptOptimizationEvents.cs | 12 +- .../Events/PromptTestFinishedEvent.cs | 2 +- .../Application/AppServices/ApiAppService.cs | 12 +- .../AppServices/LlmModelAppService.cs | 12 +- .../AppServices/PromptItemAppService.cs | 108 +- .../AppServices/PromptRangeAppService.cs | 16 +- .../AppServices/PromptResultAppService.cs | 80 +- .../AppServices/StatisticAppService.cs | 4 +- .../DTOs/Extensions/PromptRangeItemHelper.cs | 2 +- .../DTOs/Request/LlModel_AddRequest.cs | 8 +- .../DTOs/Request/PromptItem_AddRequest.cs | 20 +- .../DTOs/Request/PromptRange_AddRequest.cs | 6 +- .../PromptResult_ContinueChatRequest.cs | 6 +- .../PromptResult_UpdateChatFeedbackRequest.cs | 6 +- .../DTOs/Response/LlModel_GetPageResponse.cs | 4 +- .../DTOs/Response/PromptItem_AddResponse.cs | 20 +- .../PromptItem_GetIdAndNameResponse.cs | 14 +- .../DTOs/Response/PromptItem_GetResponse.cs | 26 +- ...mptResult_ChatHistoryWithPromptResponse.cs | 6 +- .../EventHandlers/PromptInitRequestHandler.cs | 24 +- .../Admin/Pages/PromptRange/Index.cshtml | 184 +- .../Admin/Pages/PromptRange/Model.cshtml | 178 +- .../Admin/Pages/PromptRange/Prompt.cshtml | 156 +- .../Models/DatabaseModel/Dto/Constants.cs | 68 +- .../Models/DatabaseModel/Dto/LlModelDto.cs | 168 +- .../Models/DatabaseModel/Dto/PromptItemDto.cs | 282 +- .../DatabaseModel/Dto/PromptRangeDto.cs | 50 +- .../DatabaseModel/Dto/PromptResultChatDto.cs | 20 +- .../DatabaseModel/Dto/PromptResultDto.cs | 38 +- .../Domain/Models/DatabaseModel/LlModel.cs | 372 +- .../PromptResultChatConfigurationMapping.cs | 14 +- .../Domain/Models/DatabaseModel/PromptItem.cs | 1156 +- .../Models/DatabaseModel/PromptRange.cs | 188 +- .../PromptRangeSenparcEntities.cs | 12 +- .../Models/DatabaseModel/PromptResult.cs | 48 +- .../Models/DatabaseModel/PromptResultChat.cs | 42 +- .../PromptRangeSenparcEntities_Dm.cs | 78 +- .../PromptRangeSenparcEntities_MySql.cs | 76 +- .../PromptRangeSenparcEntities_Oracle.cs | 78 +- .../PromptRangeSenparcEntities_PostgreSQL.cs | 80 +- .../PromptRangeSenparcEntities_SQLite.cs | 138 +- .../PromptRangeSenparcEntities_SqlServer.cs | 78 +- .../SenparcDbContextFactoryConfig.cs | 82 +- .../Domain/Services/LlModelService.cs | 206 +- .../Services/PromptItemService.Plugins.cs | 1088 +- .../Domain/Services/PromptItemService.cs | 1456 +- .../Domain/Services/PromptRangeService.cs | 218 +- .../Services/PromptResultChatService.cs | 62 +- .../Domain/Services/PromptResultService.cs | 138 +- .../Domain/Services/PromptService.cs | 610 +- .../Local/AppService/ApiAuthorizeAttribute.cs | 14 +- .../Senparc.Xncf.PromptRange/Register.Area.cs | 92 +- .../Register.Database.cs | 84 +- .../Senparc.Xncf.PromptRange/Register.cs | 16 +- .../wwwroot/css/PromptRange/prompt.css | 362 +- .../wwwroot/js/PromptRange/axios.js | 206 +- .../js/PromptRange/echarts/echarts-gl.min.js | 2 +- .../wwwroot/js/PromptRange/index.js | 502 +- .../js/PromptRange/lib/OrbitControls.js | 76 +- .../wwwroot/js/PromptRange/model.js | 404 +- .../wwwroot/js/PromptRange/prompt.js | 2846 +- .../js/PromptRange/utils/copyHelper.js | 88 +- .../js/PromptRange/utils/dateHelper.js | 98 +- .../js/PromptRange/utils/htmlHelper.js | 86 +- .../js/PromptRange/utils/nameHelper.js | 98 +- .../js/PromptRange/utils/storageHelper.js | 84 +- .../js/PromptRange/utils/test-utils.html | 72 +- .../Pages/SenMapic/DatabaseSample.cshtml | 200 +- .../Admin/Pages/SenMapic/Task/Index.cshtml | 154 +- .../Domain/Models/DatabaseModel/Color.cs | 162 +- .../Models/DatabaseModel/Dto/ColorDto.cs | 44 +- .../Models/DatabaseModel/SenMapicTask.cs | 260 +- .../FileManagerSenparcEntities_Dm.cs | 82 +- .../FileManagerSenparcEntities_MySql.cs | 80 +- .../FileManagerSenparcEntities_Oracle.cs | 82 +- .../FileManagerSenparcEntities_PostgreSQL.cs | 82 +- .../FileManagerSenparcEntities_SQLite.cs | 82 +- .../FileManagerSenparcEntities_SqlServer.cs | 82 +- .../SenparcDbContextFactoryConfig.cs | 82 +- .../Domain/SenMapic/SenMapicClasses.cs | 920 +- .../Domain/SenMapic/SenMapicEngine.cs | 944 +- .../SenMapic/SenMapicEngineExtension.cs | 1616 +- .../SenMapic/SenMapicEngineExtensionForWeb.cs | 92 +- .../Domain/SenMapic/SenMapicSynData.cs | 72 +- .../SiteMap/AutoAlertSitemapUtility.cs | 1546 +- .../SiteMap/BuildGoogleSitemapWithReport.cs | 600 +- .../Domain/SenMapic/SiteMap/SiteMapHandler.cs | 56 +- .../Domain/Services/ColorService.cs | 136 +- .../Domain/Services/SenMapicTaskService.cs | 14 +- .../OHS/Local/AppService/ApiAppService.cs | 128 +- .../OHS/Local/AppService/ColorAppService.cs | 86 +- .../Local/AppService/MyFuctionAppService.cs | 266 +- .../OHS/Local/PL/ColorResponse.cs | 72 +- .../OHS/Local/PL/MyFunctionRequest.cs | 106 +- .../Senparc.Xncf.SenMapic/Register.Area.cs | 90 +- .../Register.Database.cs | 84 +- .../Senparc.Xncf.SenMapic/Register.cs | 228 +- .../wwwroot/js/Admin/FileManager/index.js | 220 +- .../Services/SenMapicTaskServicesTests.cs | 52 +- ...ustomSwaggerServiceCollectionExtensions.cs | 426 +- .../Builder/SwaggerBuilderExtensions.cs | 374 +- .../ApiExplorerGroupPerVersionConvention.cs | 50 +- .../Filters/SwaggerDefaultValueFilter.cs | 130 +- .../Filters/SwaggerFileUploadFilter.cs | 160 +- .../Models/CustsomSwaggerOptions.cs | 144 +- .../Models/DataBaseModel/Config.cs | 64 +- .../Senparc.Xncf.Swagger/Register.Area.cs | 116 +- .../Senparc.Xncf.Swagger/Register.cs | 444 +- .../Utils/ConfigurationHelpler.cs | 182 +- .../Utils/VersionHelper.cs | 126 +- .../wwwroot/js/swagger.js | 240 +- .../Local/AppService/TerminalAppService.cs | 438 +- .../Senparc.Xncf.Terminal/Register.cs | 178 +- .../Admin/Pages/WeixinManager/Index.cshtml | 494 +- .../WeixinManager/MpAccount/Index.cshtml | 684 +- .../WeixinManager/WeixinUser/Index.cshtml | 495 +- .../WeixinManager/WeixinUser/Index.cshtml.cs | 50 +- .../Domain/Cache/WeixinFaceCache.cs | 100 +- .../Models/DatabaseModel/Dto/WeixinUserDto.cs | 360 +- .../Domain/Models/DatabaseModel/MpAccount.cs | 114 +- .../Domain/Models/DatabaseModel/UserTag.cs | 120 +- .../DatabaseModel/UserTag_WeixinUser.cs | 96 +- .../Domain/Models/DatabaseModel/WeixinUser.cs | 254 +- .../WeixinSenparcEntities_Dm.cs | 92 +- .../WeixinSenparcEntities_MySql.cs | 92 +- .../WeixinSenparcEntities_Oracle.cs | 92 +- .../WeixinSenparcEntities_PostgreSQL.cs | 92 +- .../WeixinSenparcEntities_SqlServer.cs | 92 +- .../WeixinSenparcEntities_Sqlite.cs | 92 +- .../Domain/Models/WeixinFace.cs | 438 +- .../Domain/Services/MpAccountService.cs | 120 +- .../Domain/Services/WeixinService.cs | 376 +- .../WeixinTemplate_AuthNotice.cs | 98 +- .../MessageHandler/AiMessageContext.cs | 232 +- .../MpMessageHandlerAttribute.cs | 40 +- .../MessageHandler/XncfMpMessageHandler.cs | 222 +- .../Register.Areas.cs | 116 +- .../Register.Database.cs | 104 +- .../Register.Middleware.cs | 540 +- .../Register.RazorRuntime.cs | 30 +- .../Senparc.Xncf.WeixinManager/Register.cs | 632 +- .../wwwroot/js/swagger.js | 240 +- .../MultiFileCodeGenerator.cs | 912 +- .../Senparc.Xncf.XncfBuilder/Domain/Enums.cs | 94 +- .../Domain/Models/DatabaseModel/Config.cs | 152 +- .../Domain/Models/DatabaseModel/ConfigDto.cs | 98 +- .../XncfBuilderSenparcEntities.cs | 48 +- .../XncfBuilderSenparcEntities_Dm.cs | 96 +- .../XncfBuilderSenparcEntities_MySql.cs | 92 +- .../XncfBuilderSenparcEntities_Oracle.cs | 96 +- .../XncfBuilderSenparcEntities_PostgreSQL.cs | 96 +- .../XncfBuilderSenparcEntities_SqlServer.cs | 96 +- .../XncfBuilderSenparcEntities_Sqlite.cs | 92 +- .../Services/Plugins/FileGenerateResult.cs | 42 +- .../Domain/Services/Plugins/FilePlugin.cs | 254 +- .../Domain/Services/PromptBuilderService.cs | 46 +- .../BuildXncfAppService.Generated.cs | 2728 +- .../OHS/Local/BuildXncfAppService.AI.MCP.cs | 382 +- .../OHS/Local/BuildXncfAppService.AI.cs | 526 +- .../OHS/Local/BuildXncfAppService.cs | 810 +- .../OHS/Local/DatabaseMigrationsAppService.cs | 470 +- .../OHS/Local/GenerateAppServiceInterface.cs | 62 +- .../OHS/PL/BuildXncfRequest.AI.cs | 264 +- .../OHS/PL/BuildXncfRequest.cs | 24 +- .../OHS/PL/DatabaseMigrationRequest.cs | 262 +- .../Register.Database.cs | 90 +- .../Senparc.Xncf.XncfBuilder/Register.cs | 196 +- .../Senparc.Xncf.XncfBuilder/Request.cs | 40 +- .../Senparc.Xncf.XncfBuilder/ResponseClass.cs | 16 +- .../Senparc.Xncf.AreasBase/AreaRegister.cs | 320 +- .../System/Senparc.Xncf.AreasBase/Register.cs | 244 +- .../DatabaseModel/MenuSenparcEntities.cs | 68 +- .../MenuSenparcEntities_Dm.cs | 80 +- .../MenuSenparcEntities_MySql.cs | 78 +- .../MenuSenparcEntities_Oracle.cs | 80 +- .../MenuSenparcEntities_PostgreSQL.cs | 80 +- .../MenuSenparcEntities_SQLite.cs | 80 +- .../MenuSenparcEntities_SqlServer.cs | 80 +- .../SenparcDbContextFactoryConfig.cs | 76 +- .../System/Senparc.Xncf.Menu/Register.cs | 100 +- .../BasePoolEntities/BasePoolEntities.cs | 46 +- .../MultipleDatabase/BasePoolEntities_Dm.cs | 94 +- .../BasePoolEntities_MySql.cs | 102 +- .../BasePoolEntities_Oracle.cs | 94 +- .../BasePoolEntities_PostgreSQL.cs | 94 +- .../BasePoolEntities_SqlServer.cs | 94 +- .../BasePoolEntities_Sqlite.cs | 94 +- .../BasePoolEntities_UnitTest.cs | 94 +- .../Database/NcfDbData/NcfClientDbData.cs | 126 +- .../Domain/Database/SenparcEntities.cs | 150 +- .../Register.Database.cs | 330 +- .../Senparc.Xncf.SystemCore/Register.cs | 368 +- .../Models/DatabaseModel/ExtensionEntity.cs | 190 +- .../Domain/Models/DatabaseModel/FeedBack.cs | 46 +- .../SenparcDbContextFactoryConfig.cs | 76 +- .../SystemManagerSenparcEntities_Dm.cs | 80 +- .../SystemManagerSenparcEntities_MySql.cs | 78 +- .../SystemManagerSenparcEntities_Oracle.cs | 80 +- ...SystemManagerSenparcEntities_PostgreSQL.cs | 80 +- .../SystemManagerSenparcEntities_SQLite.cs | 80 +- .../SystemManagerSenparcEntities_SqlServer.cs | 80 +- .../SystemManagerSenparcEntities.cs | 66 +- .../Domain/Services/SystemConfigService.cs | 248 +- .../Services/SystemConfigServiceBase.cs | 118 +- .../Register.Database.cs | 58 +- .../Senparc.Xncf.SystemManager/Register.cs | 136 +- .../SystemPermissionSenparcEntities.cs | 78 +- .../SenparcDbContextFactoryConfig.cs | 76 +- .../SystemPermissionSenparcEntities_Dm.cs | 80 +- .../SystemPermissionSenparcEntities_MySql.cs | 78 +- .../SystemPermissionSenparcEntities_Oracle.cs | 80 +- ...temPermissionSenparcEntities_PostgreSQL.cs | 80 +- .../SystemPermissionSenparcEntities_SQLite.cs | 80 +- ...stemPermissionSenparcEntities_SqlServer.cs | 80 +- .../Register.Database.cs | 58 +- .../Senparc.Xncf.SystemPermission/Register.cs | 102 +- .../Domain/DatabaseModel/Dto/TenantInfoDto.cs | 88 +- .../ACL/Repository/TenantInfoRepository.cs | 32 +- .../Domain/Cache/FullTenantInfoCache.cs | 220 +- .../SenparcDbContextFactoryConfig.cs | 76 +- .../TenantSenparcEntities_Dm.cs | 82 +- .../TenantSenparcEntities_MySql.cs | 80 +- .../TenantSenparcEntities_Oracle.cs | 82 +- .../TenantSenparcEntities_PostgreSQL.cs | 82 +- .../TenantSenparcEntities_SQLite.cs | 82 +- .../TenantSenparcEntities_SqlServer.cs | 82 +- .../SenparcEntitiesMultiTenant.cs | 82 +- .../Domain/DatabaseModel/TenantInfo.cs | 172 +- .../Domain/DatabaseModel/TenantInfoDbData.cs | 118 +- .../DatabaseModel/TenantSenparcEntities.cs | 48 +- .../Domain/Services/TenantInfoService.cs | 482 +- .../OHS/Remote/TenantMiddleware.cs | 172 +- .../Senparc.Xncf.Tenant/Register.Database.cs | 72 +- .../System/Senparc.Xncf.Tenant/Register.cs | 378 +- .../Register.Database.cs | 60 +- .../ACL/Repository/SysMenuRepository.cs | 216 +- .../Repository/SysRolePermissionRepository.cs | 88 +- .../AdminOrJwtAuthorizeAttribute.cs | 18 +- .../Areas/Admin/Pages/AdminChat/Chat.cshtml | 4 +- .../Admin/Pages/AdminChat/Chat.cshtml.cs | 12 +- .../Admin/Pages/AdminUserInfo/Index.cshtml | 232 +- .../Areas/Admin/Pages/Index.cshtml | 50 +- .../Areas/Admin/Pages/Index.cshtml.cs | 64 +- .../Areas/Admin/Pages/Login.cshtml | 128 +- .../Areas/Admin/Pages/Login.cshtml.cs | 426 +- .../Areas/Admin/Pages/Menu/Index.cshtml | 244 +- .../Areas/Admin/Pages/Role/Index.cshtml | 220 +- .../Areas/Admin/Pages/Role/Permission.cshtml | 86 +- .../Shared/_ChatModuleSelectorDialog.cshtml | 6 +- .../Admin/Pages/Shared/_Layout_Vue.cshtml | 212 +- .../Admin/Pages/Shared/_MenuPartial.cshtml | 378 +- .../Pages/Shared/_XncfModuleLayout.cshtml | 156 +- .../Shared/_XncfModuleLayout_Menu.cshtml | 54 +- .../Admin/Pages/SystemConfig/Index.cshtml | 142 +- .../Areas/Admin/Pages/TenantInfo/Index.cshtml | 360 +- .../Admin/Pages/TenantInfo/Index.cshtml.cs | 352 +- .../Areas/Admin/Pages/XncfModule/Index.cshtml | 406 +- .../Admin/Pages/XncfModule/Index.cshtml.cs | 496 +- .../Areas/Admin/Pages/XncfModule/Start.cshtml | 560 +- .../Admin/Pages/XncfModule/Start.cshtml.cs | 922 +- .../BackendJwtAuthorizeAttribute.cs | 46 +- .../Senparc.Areas.Admin/BaseAdminPageModel.cs | 138 +- .../Domain/Dto/SysRoleDto.cs | 232 +- .../Models/DatabaseModel/AdminChatMessage.cs | 50 +- .../Models/DatabaseModel/AdminChatSession.cs | 38 +- .../DatabaseModel/AdminChatSessionModule.cs | 26 +- .../DatabaseModel/AdminSenparcEntities.cs | 12 +- .../Models/DatabaseModel/AdminUserInfo.cs | 374 +- .../Models/DatabaseModel/Dto/AccountDto.cs | 114 +- .../DatabaseModel/Dto/AdminChatMessageDto.cs | 26 +- .../DatabaseModel/Dto/AdminChatSessionDto.cs | 26 +- .../Dto/AdminChatSessionModuleDto.cs | 24 +- .../DatabaseModel/Dto/XncfModuleDisplayDto.cs | 122 +- .../AdminSenparcEntities_Dm.cs | 94 +- .../AdminSenparcEntities_MySql.cs | 92 +- .../AdminSenparcEntities_Oracle.cs | 94 +- .../AdminSenparcEntities_PostgreSQL.cs | 94 +- .../AdminSenparcEntities_SqlServer.cs | 110 +- .../AdminSenparcEntities_Sqlite.cs | 92 +- .../SenparcDbContextFactoryConfig.cs | 82 +- .../Domain/Models/VD/BasePageModel.cs | 36 +- .../Domain/Models/VD/ErrorVD.Core.cs | 90 +- .../SeparcTraceManager/SenparcTraceHelper.cs | 370 +- .../SeparcTraceManager/SenparcTraceItem.cs | 128 +- .../SeparcTraceManager/SenparcTraceType.cs | 40 +- .../AIPlugins/ModuleAssistantPlugin.cs | 22 +- .../Domain/Services/AdminChatAiService.cs | 28 +- .../Services/AdminChatMessageService.cs | 34 +- .../Services/AdminChatSessionModuleService.cs | 30 +- .../Services/AdminChatSessionService.cs | 28 +- .../Domain/Services/AdminUserInfoService.cs | 780 +- .../Domain/Services/InstallerService.cs | 420 +- .../Domain/Services/SysMenuService.cs | 154 +- .../Services/SysRolePermissionService.cs | 120 +- .../Senparc.Areas.Admin/JwtSettings.cs | 72 +- .../Senparc.Areas.Admin/MyMessageHandler.cs | 208 +- .../Local/AppService/AdminChatAppService.cs | 40 +- .../AppService/AdminUserInfoAppService.cs | 410 +- .../Local/AppService/LocalAppServiceBase.cs | 84 +- .../OHS/Local/AppService/ModuleAppService.cs | 998 +- .../OHS/Local/AppService/StatAppService.cs | 144 +- .../OHS/Local/AppService/SysMenuAppService.cs | 306 +- .../OHS/Local/AppService/SysRoleAppService.cs | 356 +- .../Local/AppService/SystemInfoAppService.cs | 402 +- .../OHS/Local/PL/AdminUserInfo_Request.cs | 214 +- .../OHS/Local/PL/AdminUserInfo_Response.cs | 66 +- .../OHS/Local/PL/Module_Response.cs | 308 +- .../OHS/Local/PL/SysMenu_Request.cs | 116 +- .../OHS/Local/PL/SysMenu_Response.cs | 98 +- .../OHS/Local/PL/SysRole_Request.cs | 122 +- .../OHS/Local/PL/SysRole_Response.cs | 102 +- .../Senparc.Areas.Admin/Register.cs | 90 +- .../wwwroot/css/Admin/Menu/Menu.css | 154 +- .../css/Admin/Pages/AdminChat/Chat.css | 16 +- .../wwwroot/css/Admin/Shared/layout.css | 336 +- .../wwwroot/css/Admin/login/index.css | 422 +- .../wwwroot/js/Admin/Pages/AdminChat/Chat.js | 8 +- .../js/Admin/Pages/AdminUserInfo/Index.js | 508 +- .../js/Admin/Pages/Components/components.js | 268 +- .../wwwroot/js/Admin/Pages/Index/Index.js | 58 +- .../wwwroot/js/Admin/Pages/LogIn/Index.js | 198 +- .../wwwroot/js/Admin/Pages/Menu/Index.js | 1814 +- .../wwwroot/js/Admin/Pages/Role/Index.js | 440 +- .../js/Admin/Pages/SenparcTrace/detail.js | 384 +- .../js/Admin/Pages/SystemConfig/Index.js | 216 +- .../js/Admin/Pages/TenantInfo/Index.js | 462 +- .../js/Admin/Pages/XncfModule/index.js | 146 +- .../js/Admin/Pages/XncfModule/start.js | 560 +- .../js/Admin/Pages/XncfModule/startOld.js | 514 +- .../wwwroot/js/Admin/axios.js | 164 +- .../wwwroot/js/Admin/global.js | 124 +- .../wwwroot/js/Admin/navMenu.js | 102 +- .../wwwroot/js/Admin/store.js | 66 +- .../wwwroot/js/Admin/vuePermission.js | 40 +- .../AdminUserInfoModuleTest.cs | 210 +- .../Senparc.Web/JeffreyMpMessageHandler.cs | 108 +- .../Senparc.Web/Models/BasePageModel.cs | 36 +- .../Senparc.Web/Pages/Index.cshtml | 452 +- .../Senparc.Web/Pages/Index.cshtml.cs | 108 +- tools/NcfSimulatedSite/Senparc.Web/Program.cs | 238 +- .../NcfSimulatedSite/Senparc.Web/Register.cs | 432 +- .../Senparc.Web/wwwroot/css/Index/index.css | 518 +- .../Senparc.Web/wwwroot/css/WX/base/base.css | 1078 +- .../lib/echarts/dist/echarts.common.js | 74458 +++++----- .../wwwroot/lib/echarts/dist/echarts.js | 112562 +++++++-------- .../lib/echarts/dist/echarts.simple.js | 53558 +++---- .../lib/echarts/dist/extension/bmap.js | 726 +- .../echarts/extension/bmap/BMapCoordSys.js | 268 +- .../lib/echarts/src/chart/bar/BarSeries.js | 150 +- .../lib/echarts/src/chart/bar/BarView.js | 428 +- .../src/chart/boxplot/BoxplotSeries.js | 142 +- .../chart/candlestick/CandlestickSeries.js | 182 +- .../effectScatter/EffectScatterSeries.js | 126 +- .../echarts/src/chart/funnel/FunnelSeries.js | 200 +- .../echarts/src/chart/gauge/GaugeSeries.js | 242 +- .../chart/helper/createGraphFromNodeMatrix.js | 182 +- .../src/chart/helper/createListFromArray.js | 456 +- .../src/chart/helper/whiskerBoxCommon.js | 278 +- .../lib/echarts/src/chart/line/LineSeries.js | 146 +- .../echarts/src/chart/lines/LinesSeries.js | 232 +- .../lib/echarts/src/chart/map/MapSeries.js | 378 +- .../echarts/src/chart/map/mapDataStatistic.js | 158 +- .../src/chart/parallel/ParallelSeries.js | 236 +- .../src/chart/parallel/ParallelView.js | 398 +- .../lib/echarts/src/chart/pie/PieSeries.js | 290 +- .../lib/echarts/src/chart/pie/labelLayout.js | 452 +- .../lib/echarts/src/chart/pie/pieLayout.js | 244 +- .../src/chart/scatter/ScatterSeries.js | 122 +- .../src/chart/treemap/TreemapSeries.js | 696 +- .../echarts/src/chart/treemap/TreemapView.js | 1742 +- .../src/chart/treemap/treemapLayout.js | 1098 +- .../src/component/dataZoom/DataZoomModel.js | 864 +- .../src/component/dataZoom/InsideZoomView.js | 386 +- .../src/component/dataZoom/SliderZoomView.js | 1398 +- .../src/component/legend/LegendModel.js | 358 +- .../src/component/marker/MarkPointView.js | 398 +- .../component/timeline/SliderTimelineModel.js | 220 +- .../component/timeline/SliderTimelineView.js | 1374 +- .../src/component/timeline/TimelineModel.js | 392 +- .../lib/echarts/src/component/title.js | 374 +- .../src/component/tooltip/TooltipModel.js | 206 +- .../src/component/visualMap/PiecewiseModel.js | 646 +- .../src/component/visualMap/VisualMapModel.js | 1076 +- .../lib/echarts/src/coord/axisDefault.js | 258 +- .../lib/echarts/src/coord/axisHelper.js | 442 +- .../lib/echarts/src/coord/cartesian/Grid.js | 836 +- .../echarts/src/coord/cartesian/GridModel.js | 78 +- .../lib/echarts/src/coord/geo/GeoModel.js | 294 +- .../lib/echarts/src/coord/geo/fix/nanhai.js | 78 +- .../echarts/src/coord/geo/fix/textCoord.js | 48 +- .../echarts/src/coord/parallel/Parallel.js | 598 +- .../src/coord/parallel/ParallelModel.js | 198 +- .../echarts/src/coord/polar/polarCreator.js | 260 +- .../wwwroot/lib/echarts/src/data/Graph.js | 1028 +- .../wwwroot/lib/echarts/src/layout/barGrid.js | 432 +- .../wwwroot/lib/echarts/src/model/Global.js | 1502 +- .../wwwroot/lib/echarts/src/model/Model.js | 312 +- .../lib/echarts/src/model/OptionManager.js | 868 +- .../lib/echarts/src/model/globalDefault.js | 86 +- .../wwwroot/lib/echarts/src/scale/Time.js | 320 +- .../wwwroot/lib/echarts/src/util/format.js | 322 +- .../wwwroot/lib/echarts/src/util/number.js | 462 +- .../wwwroot/lib/echarts/src/util/symbol.js | 706 +- .../wwwroot/lib/echarts/src/util/throttle.js | 388 +- .../wwwroot/lib/element-ui_2.13.2/element.js | 2 +- .../Areas/Admin/Pages/Account/Index.cshtml | 86 +- .../Domain/Cache/FullAccountCache.cs | 334 +- .../Domain/Models/DatabaseModel/Account.cs | 134 +- .../DatabaseModel/AccountSenparcEntities.cs | 100 +- .../Models/DatabaseModel/Dto/AccountDto.cs | 88 +- .../Models/DatabaseModel/ExtensionEntity.cs | 364 +- .../AccountSenparcEntities_Dm.cs | 76 +- .../AccountSenparcEntities_MySql.cs | 80 +- .../AccountSenparcEntities_Oracle.cs | 84 +- .../AccountSenparcEntities_PostgreSQL.cs | 76 +- .../AccountSenparcEntities_SQLite.cs | 82 +- .../AccountSenparcEntities_SqlServer.cs | 76 +- .../SenparcDbContextFactoryConfig.cs | 82 +- .../Domain/OperationQueue/OperationQueue.cs | 346 +- .../OperationQueue/OperationQueueItem.cs | 102 +- .../OperationQueue/OperationQueueService.cs | 150 +- .../Domain/Services/AccountPayLogService.cs | 216 +- .../Domain/Services/AccountService.cs | 1004 +- .../Domain/Services/PointsLogService.cs | 180 +- .../OHS/Local/PL/MyFunctionRequest.cs | 76 +- .../Senparc.Xncf.Accounts/Program.cs | 232 +- .../Senparc.Xncf.Accounts/Register.Area.cs | 88 +- .../Register.Database.cs | 86 +- .../Senparc.Xncf.Accounts/Register.cs | 180 +- .../GetDefaultInstallOptionsResponseDto.cs | 20 +- .../Domain/Dto/InstallRequestDto.cs | 20 +- .../Domain/Dto/XncfRegisterDto.cs | 28 +- .../Areas/Install/Pages/Index.cshtml | 240 +- .../Areas/Install/Pages/Index.cshtml.cs | 388 +- .../DatabaseModel/InstallerSenparcEntities.cs | 48 +- .../InstallerSenparcEntities_Dm.cs | 82 +- .../InstallerSenparcEntities_MySql.cs | 80 +- .../InstallerSenparcEntities_PostgreSQL.cs | 84 +- .../InstallerSenparcEntities_SQLite.cs | 80 +- .../InstallerSenparcEntities_SqlServer.cs | 82 +- .../SenparcDbContextFactoryConfig.cs | 82 +- .../Domain/Services/InstallOptionsService.cs | 284 +- .../Domain/Services/InstallerService.cs | 780 +- .../OHS/Local/AppService/InstallAppService.cs | 124 +- .../Senparc.Xncf.Installer/Program.cs | 228 +- .../Senparc.Xncf.Installer/Register.Area.cs | 86 +- .../Senparc.Xncf.Installer/Register.cs | 166 +- .../wwwroot/js/Installer/Pages/index.js | 116 +- .../Template_XncfName/DatabaseSample.cshtml | 200 +- .../DatabaseSampleIndex.cshtml | 350 +- .../DatabaseSampleIndex.cshtml.cs | 488 +- .../Pages/Template_XncfName/Index.cshtml | 86 +- .../Domain/Models/DatabaseModel/Color.cs | 190 +- .../Models/DatabaseModel/Dto/ColorDto.cs | 54 +- .../DatabaseModel/Dto/ColorRequestDto.cs | 128 +- .../Template_XncfNameSenparcEntities.cs | 48 +- .../SenparcDbContextFactoryConfig.cs | 82 +- .../Template_XncfNameSenparcEntities_Dm.cs | 80 +- .../Template_XncfNameSenparcEntities_MySql.cs | 78 +- ...Template_XncfNameSenparcEntities_Oracle.cs | 80 +- ...late_XncfNameSenparcEntities_PostgreSQL.cs | 80 +- ...Template_XncfNameSenparcEntities_SQLite.cs | 80 +- ...plate_XncfNameSenparcEntities_SqlServer.cs | 80 +- .../Domain/Services/ColorService.cs | 134 +- .../OHS/Local/AppService/ApiAppService.cs | 128 +- .../OHS/Local/AppService/ColorAppService.cs | 86 +- .../Local/AppService/MyFuctionAppService.cs | 174 +- .../OHS/Local/PL/ColorResponse.cs | 72 +- .../OHS/Local/PL/MyFunctionRequest.cs | 76 +- .../Register.Area.cs | 94 +- .../Register.Database.cs | 84 +- .../Register.cs | 210 +- .../Template_XncfName/databaseSampleIndex.css | 436 +- .../Template_XncfName/databaseSampleIndex.js | 854 +- .../Domain/Models/AdminUserInfoTests.cs | 84 +- 973 files changed, 222900 insertions(+), 219416 deletions(-) create mode 100644 .translation_cache.json create mode 100644 .translation_state.json create mode 100644 .translation_state_v2.json diff --git a/.gitignore b/.gitignore index e27f3fa57..3fabc972f 100644 --- a/.gitignore +++ b/.gitignore @@ -109,3 +109,1310 @@ tools/NcfDesktopApp.GUI/publish-self-contained/* /.github/appmod/custom-tasks/ *.tmp .vscode/settings.json +.venv_translate/lib64 +.venv_translate/pyvenv.cfg +.venv_translate/lib/python3.12/site-packages/typing_extensions.py +.venv_translate/lib/python3.12/site-packages/__pycache__/typing_extensions.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/INSTALLER +.venv_translate/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/METADATA +.venv_translate/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/RECORD +.venv_translate/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/WHEEL +.venv_translate/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS +.venv_translate/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/LICENSE +.venv_translate/lib/python3.12/site-packages/bs4/__init__.py +.venv_translate/lib/python3.12/site-packages/bs4/_deprecation.py +.venv_translate/lib/python3.12/site-packages/bs4/_typing.py +.venv_translate/lib/python3.12/site-packages/bs4/_warnings.py +.venv_translate/lib/python3.12/site-packages/bs4/css.py +.venv_translate/lib/python3.12/site-packages/bs4/dammit.py +.venv_translate/lib/python3.12/site-packages/bs4/diagnose.py +.venv_translate/lib/python3.12/site-packages/bs4/element.py +.venv_translate/lib/python3.12/site-packages/bs4/exceptions.py +.venv_translate/lib/python3.12/site-packages/bs4/filter.py +.venv_translate/lib/python3.12/site-packages/bs4/formatter.py +.venv_translate/lib/python3.12/site-packages/bs4/py.typed +.venv_translate/lib/python3.12/site-packages/bs4/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/bs4/__pycache__/_deprecation.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/bs4/__pycache__/_typing.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/bs4/__pycache__/_warnings.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/bs4/__pycache__/css.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/bs4/__pycache__/dammit.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/bs4/__pycache__/diagnose.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/bs4/__pycache__/element.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/bs4/__pycache__/exceptions.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/bs4/__pycache__/filter.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/bs4/__pycache__/formatter.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/bs4/builder/__init__.py +.venv_translate/lib/python3.12/site-packages/bs4/builder/_html5lib.py +.venv_translate/lib/python3.12/site-packages/bs4/builder/_htmlparser.py +.venv_translate/lib/python3.12/site-packages/bs4/builder/_lxml.py +.venv_translate/lib/python3.12/site-packages/bs4/builder/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/bs4/builder/__pycache__/_html5lib.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/bs4/builder/__pycache__/_htmlparser.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/bs4/builder/__pycache__/_lxml.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/certifi/__init__.py +.venv_translate/lib/python3.12/site-packages/certifi/__main__.py +.venv_translate/lib/python3.12/site-packages/certifi/cacert.pem +.venv_translate/lib/python3.12/site-packages/certifi/core.py +.venv_translate/lib/python3.12/site-packages/certifi/py.typed +.venv_translate/lib/python3.12/site-packages/certifi/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/certifi/__pycache__/__main__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/certifi/__pycache__/core.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/certifi-2026.2.25.dist-info/INSTALLER +.venv_translate/lib/python3.12/site-packages/certifi-2026.2.25.dist-info/METADATA +.venv_translate/lib/python3.12/site-packages/certifi-2026.2.25.dist-info/RECORD +.venv_translate/lib/python3.12/site-packages/certifi-2026.2.25.dist-info/top_level.txt +.venv_translate/lib/python3.12/site-packages/certifi-2026.2.25.dist-info/WHEEL +.venv_translate/lib/python3.12/site-packages/certifi-2026.2.25.dist-info/licenses/LICENSE +.venv_translate/lib/python3.12/site-packages/charset_normalizer/__init__.py +.venv_translate/lib/python3.12/site-packages/charset_normalizer/__main__.py +.venv_translate/lib/python3.12/site-packages/charset_normalizer/api.py +.venv_translate/lib/python3.12/site-packages/charset_normalizer/cd.py +.venv_translate/lib/python3.12/site-packages/charset_normalizer/constant.py +.venv_translate/lib/python3.12/site-packages/charset_normalizer/legacy.py +.venv_translate/lib/python3.12/site-packages/charset_normalizer/md.py +.venv_translate/lib/python3.12/site-packages/charset_normalizer/models.py +.venv_translate/lib/python3.12/site-packages/charset_normalizer/py.typed +.venv_translate/lib/python3.12/site-packages/charset_normalizer/utils.py +.venv_translate/lib/python3.12/site-packages/charset_normalizer/version.py +.venv_translate/lib/python3.12/site-packages/charset_normalizer/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/charset_normalizer/__pycache__/__main__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/charset_normalizer/__pycache__/api.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/charset_normalizer/__pycache__/cd.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/charset_normalizer/__pycache__/constant.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/charset_normalizer/__pycache__/legacy.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/charset_normalizer/__pycache__/md.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/charset_normalizer/__pycache__/models.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/charset_normalizer/__pycache__/utils.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/charset_normalizer/__pycache__/version.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/charset_normalizer/cli/__init__.py +.venv_translate/lib/python3.12/site-packages/charset_normalizer/cli/__main__.py +.venv_translate/lib/python3.12/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/charset_normalizer/cli/__pycache__/__main__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/entry_points.txt +.venv_translate/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/INSTALLER +.venv_translate/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/METADATA +.venv_translate/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/RECORD +.venv_translate/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/top_level.txt +.venv_translate/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/WHEEL +.venv_translate/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/licenses/LICENSE +.venv_translate/lib/python3.12/site-packages/deep_translator/__init__.py +.venv_translate/lib/python3.12/site-packages/deep_translator/__main__.py +.venv_translate/lib/python3.12/site-packages/deep_translator/baidu.py +.venv_translate/lib/python3.12/site-packages/deep_translator/base.py +.venv_translate/lib/python3.12/site-packages/deep_translator/chatgpt.py +.venv_translate/lib/python3.12/site-packages/deep_translator/cli.py +.venv_translate/lib/python3.12/site-packages/deep_translator/constants.py +.venv_translate/lib/python3.12/site-packages/deep_translator/deepl.py +.venv_translate/lib/python3.12/site-packages/deep_translator/detection.py +.venv_translate/lib/python3.12/site-packages/deep_translator/engines.py +.venv_translate/lib/python3.12/site-packages/deep_translator/exceptions.py +.venv_translate/lib/python3.12/site-packages/deep_translator/google.py +.venv_translate/lib/python3.12/site-packages/deep_translator/libre.py +.venv_translate/lib/python3.12/site-packages/deep_translator/linguee.py +.venv_translate/lib/python3.12/site-packages/deep_translator/microsoft.py +.venv_translate/lib/python3.12/site-packages/deep_translator/mymemory.py +.venv_translate/lib/python3.12/site-packages/deep_translator/papago.py +.venv_translate/lib/python3.12/site-packages/deep_translator/pons.py +.venv_translate/lib/python3.12/site-packages/deep_translator/qcri.py +.venv_translate/lib/python3.12/site-packages/deep_translator/validate.py +.venv_translate/lib/python3.12/site-packages/deep_translator/yandex.py +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/__main__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/baidu.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/base.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/chatgpt.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/cli.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/constants.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/deepl.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/detection.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/engines.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/exceptions.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/google.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/libre.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/linguee.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/microsoft.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/mymemory.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/papago.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/pons.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/qcri.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/validate.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator/__pycache__/yandex.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/deep_translator-1.11.4.dist-info/entry_points.txt +.venv_translate/lib/python3.12/site-packages/deep_translator-1.11.4.dist-info/INSTALLER +.venv_translate/lib/python3.12/site-packages/deep_translator-1.11.4.dist-info/LICENSE +.venv_translate/lib/python3.12/site-packages/deep_translator-1.11.4.dist-info/METADATA +.venv_translate/lib/python3.12/site-packages/deep_translator-1.11.4.dist-info/RECORD +.venv_translate/lib/python3.12/site-packages/deep_translator-1.11.4.dist-info/REQUESTED +.venv_translate/lib/python3.12/site-packages/deep_translator-1.11.4.dist-info/WHEEL +.venv_translate/lib/python3.12/site-packages/idna/__init__.py +.venv_translate/lib/python3.12/site-packages/idna/codec.py +.venv_translate/lib/python3.12/site-packages/idna/compat.py +.venv_translate/lib/python3.12/site-packages/idna/core.py +.venv_translate/lib/python3.12/site-packages/idna/idnadata.py +.venv_translate/lib/python3.12/site-packages/idna/intranges.py +.venv_translate/lib/python3.12/site-packages/idna/package_data.py +.venv_translate/lib/python3.12/site-packages/idna/py.typed +.venv_translate/lib/python3.12/site-packages/idna/uts46data.py +.venv_translate/lib/python3.12/site-packages/idna/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/idna/__pycache__/codec.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/idna/__pycache__/compat.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/idna/__pycache__/core.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/idna/__pycache__/idnadata.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/idna/__pycache__/intranges.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/idna/__pycache__/package_data.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/idna/__pycache__/uts46data.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/idna-3.11.dist-info/INSTALLER +.venv_translate/lib/python3.12/site-packages/idna-3.11.dist-info/METADATA +.venv_translate/lib/python3.12/site-packages/idna-3.11.dist-info/RECORD +.venv_translate/lib/python3.12/site-packages/idna-3.11.dist-info/WHEEL +.venv_translate/lib/python3.12/site-packages/idna-3.11.dist-info/licenses/LICENSE.md +.venv_translate/lib/python3.12/site-packages/pip/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/__main__.py +.venv_translate/lib/python3.12/site-packages/pip/__pip-runner__.py +.venv_translate/lib/python3.12/site-packages/pip/py.typed +.venv_translate/lib/python3.12/site-packages/pip/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/__pycache__/__main__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/__pycache__/__pip-runner__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/build_env.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/cache.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/configuration.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/exceptions.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/main.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/pyproject.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/self_outdated_check.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/wheel_builder.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/__pycache__/build_env.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/__pycache__/cache.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/__pycache__/configuration.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/__pycache__/exceptions.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/__pycache__/main.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/__pycache__/pyproject.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/autocompletion.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/base_command.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/cmdoptions.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/command_context.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/main_parser.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/main.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/parser.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/progress_bars.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/req_command.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/spinners.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/status_codes.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/parser.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/cache.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/check.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/completion.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/configuration.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/debug.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/download.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/freeze.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/hash.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/help.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/index.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/inspect.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/install.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/list.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/search.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/show.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/uninstall.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/wheel.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/cache.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/check.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/completion.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/debug.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/download.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/hash.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/help.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/index.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/inspect.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/install.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/list.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/search.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/show.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/distributions/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/distributions/base.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/distributions/installed.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/distributions/sdist.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/distributions/wheel.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/base.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/index/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/index/collector.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/index/package_finder.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/index/sources.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/index/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/index/__pycache__/collector.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/index/__pycache__/sources.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/locations/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/locations/_distutils.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/locations/_sysconfig.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/locations/base.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/_distutils.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/_sysconfig.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/base.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/metadata/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/metadata/_json.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/metadata/base.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/metadata/pkg_resources.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/_json.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/base.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/pkg_resources.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_compat.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_dists.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_envs.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_compat.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_dists.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_envs.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/candidate.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/direct_url.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/format_control.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/index.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/installation_report.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/link.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/scheme.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/search_scope.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/selection_prefs.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/target_python.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/wheel.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/__pycache__/candidate.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/__pycache__/format_control.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/__pycache__/index.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/__pycache__/installation_report.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/__pycache__/link.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/__pycache__/scheme.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/__pycache__/target_python.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/models/__pycache__/wheel.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/network/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/network/auth.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/network/cache.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/network/download.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/network/lazy_wheel.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/network/session.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/network/utils.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/network/xmlrpc.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/network/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/network/__pycache__/auth.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/network/__pycache__/cache.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/network/__pycache__/download.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/network/__pycache__/lazy_wheel.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/network/__pycache__/session.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/network/__pycache__/utils.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/check.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/freeze.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/prepare.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/check.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/build/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/build/build_tracker.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/build/metadata_editable.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/build/metadata_legacy.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/build/metadata.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/build/wheel_editable.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/build/wheel_legacy.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/build/wheel.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/build_tracker.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata_editable.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel_editable.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/install/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/install/editable_legacy.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/install/wheel.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/req/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/req/constructors.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/req/req_file.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/req/req_install.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/req/req_set.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/req/req_uninstall.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/req/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/req/__pycache__/constructors.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_file.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_install.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_set.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/base.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/__pycache__/base.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/legacy/resolver.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/base.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/candidates.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/factory.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/provider.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/reporter.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/requirements.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/resolver.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/_jaraco_text.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/_log.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/appdirs.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/compat.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/compatibility_tags.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/datetime.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/deprecation.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/direct_url_helpers.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/egg_link.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/encoding.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/entrypoints.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/filesystem.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/filetypes.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/glibc.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/hashes.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/logging.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/misc.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/models.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/packaging.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/setuptools_build.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/subprocess.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/temp_dir.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/unpacking.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/urls.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/virtualenv.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/wheel.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/_jaraco_text.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/_log.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/compat.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/egg_link.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/logging.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/misc.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/models.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/urls.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/vcs/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/vcs/bazaar.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/vcs/git.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/vcs/mercurial.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/vcs/subversion.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/vcs/versioncontrol.py +.venv_translate/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/git.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/six.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/typing_extensions.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/vendor.txt +.venv_translate/lib/python3.12/site-packages/pip/_vendor/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/__pycache__/six.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/__pycache__/typing_extensions.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/_cmd.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/adapter.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/cache.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/controller.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/filewrapper.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/heuristics.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/serialize.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/wrapper.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/cache.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/controller.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/certifi/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/certifi/__main__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/certifi/cacert.pem +.venv_translate/lib/python3.12/site-packages/pip/_vendor/certifi/core.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/__main__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/core.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/big5freq.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/big5prober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/chardistribution.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/charsetgroupprober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/charsetprober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/codingstatemachine.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/codingstatemachinedict.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/cp949prober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/enums.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/escprober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/escsm.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/eucjpprober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/euckrfreq.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/euckrprober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/euctwfreq.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/euctwprober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/gb2312freq.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/gb2312prober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/hebrewprober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/jisfreq.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/johabfreq.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/johabprober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/jpcntx.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/langbulgarianmodel.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/langgreekmodel.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/langhebrewmodel.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/langhungarianmodel.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/langrussianmodel.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/langthaimodel.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/langturkishmodel.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/latin1prober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/macromanprober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/mbcharsetprober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/mbcsgroupprober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/mbcssm.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/resultdict.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/sbcharsetprober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/sbcsgroupprober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/sjisprober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/universaldetector.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/utf8prober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/utf1632prober.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/version.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/big5freq.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/big5prober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/chardistribution.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/charsetprober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachinedict.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/cp949prober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/enums.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/escprober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/escsm.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/euckrprober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/euctwprober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/jisfreq.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/johabfreq.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/johabprober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/jpcntx.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langhungarianmodel.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langrussianmodel.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/latin1prober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/macromanprober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/mbcssm.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/resultdict.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/sjisprober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/universaldetector.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/utf8prober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/utf1632prober.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/version.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/cli/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/cli/chardetect.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/cli/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/metadata/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/metadata/languages.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/metadata/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/chardet/metadata/__pycache__/languages.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/ansi.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/ansitowin32.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/initialise.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/win32.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/winterm.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/ansi.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/initialise.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/win32.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/winterm.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/tests/ansi_test.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/tests/ansitowin32_test.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/tests/initialise_test.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/tests/isatty_test.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/tests/utils.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/tests/winterm_test.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/ansi_test.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/ansitowin32_test.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/initialise_test.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/isatty_test.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/utils.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/winterm_test.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/compat.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/database.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/index.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/locators.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/manifest.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/markers.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/metadata.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/resources.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/scripts.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/util.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/version.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/wheel.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/locators.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/scripts.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distro/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distro/__main__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distro/distro.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/__main__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/distro.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/idna/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/idna/codec.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/idna/compat.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/idna/core.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/idna/idnadata.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/idna/intranges.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/idna/package_data.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/idna/uts46data.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/codec.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/compat.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/core.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/idnadata.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/intranges.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/package_data.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/msgpack/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/msgpack/exceptions.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/msgpack/ext.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/msgpack/fallback.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/exceptions.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/ext.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/fallback.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/__about__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/_manylinux.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/_musllinux.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/_structures.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/markers.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/requirements.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/specifiers.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/tags.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/utils.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/version.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/__about__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_musllinux.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_structures.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/requirements.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/specifiers.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/tags.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/utils.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/version.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pkg_resources/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/platformdirs/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/platformdirs/__main__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/platformdirs/android.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/platformdirs/api.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/platformdirs/macos.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/platformdirs/unix.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/platformdirs/version.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/platformdirs/windows.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/__main__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/android.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/api.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/macos.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/unix.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/version.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/windows.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__main__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/cmdline.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/console.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/filter.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatter.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/lexer.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/modeline.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/plugin.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/regexopt.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/scanner.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/sphinxext.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/style.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/token.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/unistring.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/util.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/__main__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/cmdline.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/console.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/filter.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/formatter.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/lexer.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/modeline.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/plugin.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/regexopt.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/scanner.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/sphinxext.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/style.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/token.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/unistring.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/util.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/filters/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/filters/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/_mapping.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/bbcode.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/groff.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/html.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/img.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/irc.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/latex.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/other.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/pangomarkup.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/rtf.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/svg.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/terminal.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/terminal256.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/_mapping.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/bbcode.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/groff.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/html.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/img.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/irc.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/latex.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/other.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/pangomarkup.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/rtf.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/svg.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/terminal.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/terminal256.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/_mapping.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/python.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/_mapping.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/python.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/styles/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pygments/styles/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/actions.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/common.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/core.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/exceptions.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/helpers.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/results.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/testing.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/unicode.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/util.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/actions.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/common.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/core.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/exceptions.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/helpers.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/results.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/testing.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/unicode.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/util.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/diagram/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyparsing/diagram/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_compat.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_impl.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/_compat.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/_impl.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/_in_process.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__version__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/_internal_utils.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/adapters.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/api.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/auth.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/certs.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/compat.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/cookies.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/exceptions.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/help.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/hooks.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/models.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/packages.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/sessions.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/status_codes.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/structures.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/utils.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/__version__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/_internal_utils.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/adapters.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/api.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/auth.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/certs.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/compat.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/cookies.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/exceptions.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/help.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/hooks.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/models.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/status_codes.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/resolvelib/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/resolvelib/providers.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/resolvelib/reporters.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/resolvelib/structs.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/providers.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/reporters.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/resolvers.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/structs.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/__pycache__/collections_abc.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__main__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_cell_widths.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_emoji_codes.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_emoji_replace.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_export_format.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_extension.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_fileno.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_inspect.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_log_render.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_loop.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_null_file.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_palettes.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_pick.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_ratio.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_spinners.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_stack.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_timer.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_win32_console.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_windows_renderer.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_windows.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/_wrap.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/abc.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/align.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/ansi.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/bar.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/box.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/cells.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/color_triplet.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/color.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/columns.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/console.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/constrain.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/containers.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/control.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/default_styles.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/diagnose.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/emoji.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/errors.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/file_proxy.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/filesize.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/highlighter.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/json.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/jupyter.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/layout.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/live_render.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/live.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/logging.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/markup.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/measure.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/padding.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/pager.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/palette.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/panel.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/pretty.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/progress_bar.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/progress.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/prompt.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/protocol.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/region.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/repr.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/rule.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/scope.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/screen.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/segment.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/spinner.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/status.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/style.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/styled.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/syntax.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/table.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/terminal_theme.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/text.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/theme.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/themes.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/traceback.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/tree.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/__main__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_cell_widths.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_emoji_codes.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_emoji_replace.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_export_format.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_extension.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_fileno.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_inspect.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_log_render.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_loop.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_null_file.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_palettes.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_pick.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_ratio.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_spinners.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_stack.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_timer.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_win32_console.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_windows_renderer.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_windows.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_wrap.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/abc.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/align.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/ansi.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/bar.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/box.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/cells.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/color_triplet.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/color.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/columns.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/console.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/constrain.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/containers.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/control.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/default_styles.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/diagnose.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/emoji.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/errors.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/file_proxy.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/filesize.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/highlighter.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/json.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/jupyter.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/layout.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/live_render.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/live.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/logging.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/markup.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/measure.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/padding.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/pager.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/palette.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/panel.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/pretty.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/progress_bar.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/progress.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/prompt.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/protocol.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/region.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/repr.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/rule.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/scope.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/screen.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/segment.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/spinner.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/status.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/style.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/styled.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/syntax.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/table.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/terminal_theme.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/text.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/theme.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/themes.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/traceback.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/tree.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/_asyncio.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/_utils.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/after.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/before_sleep.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/before.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/nap.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/retry.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/stop.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/tornadoweb.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/wait.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/_asyncio.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/_utils.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/after.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/before_sleep.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/before.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/nap.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/retry.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/stop.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/tornadoweb.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/wait.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tomli/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tomli/_parser.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tomli/_re.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tomli/_types.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tomli/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tomli/__pycache__/_parser.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tomli/__pycache__/_re.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/tomli/__pycache__/_types.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/truststore/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/truststore/_api.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/truststore/_macos.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/truststore/_openssl.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/truststore/_ssl_constants.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/truststore/_windows.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_api.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_macos.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_openssl.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_ssl_constants.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_windows.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/_collections.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/_version.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/connection.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/connectionpool.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/exceptions.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/fields.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/filepost.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/poolmanager.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/request.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/response.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/_collections.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/_version.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/connection.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/connectionpool.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/exceptions.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/fields.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/filepost.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/request.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/response.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/appengine.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/securetransport.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/socks.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/six.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/__pycache__/six.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/weakref_finalize.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/weakref_finalize.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/connection.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/proxy.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/queue.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/request.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/response.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/retry.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/ssl_.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/ssl_match_hostname.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/ssltransport.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/timeout.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/url.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/wait.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/connection.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/proxy.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/queue.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/request.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/response.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/retry.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_match_hostname.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/ssltransport.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/timeout.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/url.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/wait.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/webencodings/__init__.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/webencodings/labels.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/webencodings/mklabels.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/webencodings/tests.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/webencodings/x_user_defined.py +.venv_translate/lib/python3.12/site-packages/pip/_vendor/webencodings/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/webencodings/__pycache__/labels.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/webencodings/__pycache__/mklabels.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/webencodings/__pycache__/tests.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip/_vendor/webencodings/__pycache__/x_user_defined.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/pip-24.0.dist-info/AUTHORS.txt +.venv_translate/lib/python3.12/site-packages/pip-24.0.dist-info/entry_points.txt +.venv_translate/lib/python3.12/site-packages/pip-24.0.dist-info/INSTALLER +.venv_translate/lib/python3.12/site-packages/pip-24.0.dist-info/LICENSE.txt +.venv_translate/lib/python3.12/site-packages/pip-24.0.dist-info/METADATA +.venv_translate/lib/python3.12/site-packages/pip-24.0.dist-info/RECORD +.venv_translate/lib/python3.12/site-packages/pip-24.0.dist-info/REQUESTED +.venv_translate/lib/python3.12/site-packages/pip-24.0.dist-info/top_level.txt +.venv_translate/lib/python3.12/site-packages/pip-24.0.dist-info/WHEEL +.venv_translate/lib/python3.12/site-packages/requests/__init__.py +.venv_translate/lib/python3.12/site-packages/requests/__version__.py +.venv_translate/lib/python3.12/site-packages/requests/_internal_utils.py +.venv_translate/lib/python3.12/site-packages/requests/adapters.py +.venv_translate/lib/python3.12/site-packages/requests/api.py +.venv_translate/lib/python3.12/site-packages/requests/auth.py +.venv_translate/lib/python3.12/site-packages/requests/certs.py +.venv_translate/lib/python3.12/site-packages/requests/compat.py +.venv_translate/lib/python3.12/site-packages/requests/cookies.py +.venv_translate/lib/python3.12/site-packages/requests/exceptions.py +.venv_translate/lib/python3.12/site-packages/requests/help.py +.venv_translate/lib/python3.12/site-packages/requests/hooks.py +.venv_translate/lib/python3.12/site-packages/requests/models.py +.venv_translate/lib/python3.12/site-packages/requests/packages.py +.venv_translate/lib/python3.12/site-packages/requests/sessions.py +.venv_translate/lib/python3.12/site-packages/requests/status_codes.py +.venv_translate/lib/python3.12/site-packages/requests/structures.py +.venv_translate/lib/python3.12/site-packages/requests/utils.py +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/__version__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/_internal_utils.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/adapters.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/api.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/auth.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/certs.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/compat.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/cookies.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/exceptions.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/help.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/hooks.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/models.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/packages.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/sessions.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/status_codes.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/structures.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests/__pycache__/utils.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/requests-2.33.1.dist-info/INSTALLER +.venv_translate/lib/python3.12/site-packages/requests-2.33.1.dist-info/METADATA +.venv_translate/lib/python3.12/site-packages/requests-2.33.1.dist-info/RECORD +.venv_translate/lib/python3.12/site-packages/requests-2.33.1.dist-info/top_level.txt +.venv_translate/lib/python3.12/site-packages/requests-2.33.1.dist-info/WHEEL +.venv_translate/lib/python3.12/site-packages/requests-2.33.1.dist-info/licenses/LICENSE +.venv_translate/lib/python3.12/site-packages/requests-2.33.1.dist-info/licenses/NOTICE +.venv_translate/lib/python3.12/site-packages/soupsieve/__init__.py +.venv_translate/lib/python3.12/site-packages/soupsieve/__meta__.py +.venv_translate/lib/python3.12/site-packages/soupsieve/css_match.py +.venv_translate/lib/python3.12/site-packages/soupsieve/css_parser.py +.venv_translate/lib/python3.12/site-packages/soupsieve/css_types.py +.venv_translate/lib/python3.12/site-packages/soupsieve/pretty.py +.venv_translate/lib/python3.12/site-packages/soupsieve/py.typed +.venv_translate/lib/python3.12/site-packages/soupsieve/util.py +.venv_translate/lib/python3.12/site-packages/soupsieve/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/soupsieve/__pycache__/__meta__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/soupsieve/__pycache__/css_match.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/soupsieve/__pycache__/css_parser.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/soupsieve/__pycache__/css_types.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/soupsieve/__pycache__/pretty.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/soupsieve/__pycache__/util.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/INSTALLER +.venv_translate/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/METADATA +.venv_translate/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/RECORD +.venv_translate/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/WHEEL +.venv_translate/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/licenses/LICENSE.md +.venv_translate/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/INSTALLER +.venv_translate/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/METADATA +.venv_translate/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/RECORD +.venv_translate/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/WHEEL +.venv_translate/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/licenses/LICENSE +.venv_translate/lib/python3.12/site-packages/urllib3/__init__.py +.venv_translate/lib/python3.12/site-packages/urllib3/_base_connection.py +.venv_translate/lib/python3.12/site-packages/urllib3/_collections.py +.venv_translate/lib/python3.12/site-packages/urllib3/_request_methods.py +.venv_translate/lib/python3.12/site-packages/urllib3/_version.py +.venv_translate/lib/python3.12/site-packages/urllib3/connection.py +.venv_translate/lib/python3.12/site-packages/urllib3/connectionpool.py +.venv_translate/lib/python3.12/site-packages/urllib3/exceptions.py +.venv_translate/lib/python3.12/site-packages/urllib3/fields.py +.venv_translate/lib/python3.12/site-packages/urllib3/filepost.py +.venv_translate/lib/python3.12/site-packages/urllib3/poolmanager.py +.venv_translate/lib/python3.12/site-packages/urllib3/py.typed +.venv_translate/lib/python3.12/site-packages/urllib3/response.py +.venv_translate/lib/python3.12/site-packages/urllib3/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/__pycache__/_base_connection.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/__pycache__/_collections.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/__pycache__/_request_methods.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/__pycache__/_version.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/__pycache__/connection.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/__pycache__/connectionpool.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/__pycache__/exceptions.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/__pycache__/fields.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/__pycache__/filepost.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/__pycache__/poolmanager.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/__pycache__/response.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/contrib/__init__.py +.venv_translate/lib/python3.12/site-packages/urllib3/contrib/pyopenssl.py +.venv_translate/lib/python3.12/site-packages/urllib3/contrib/socks.py +.venv_translate/lib/python3.12/site-packages/urllib3/contrib/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/contrib/__pycache__/pyopenssl.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/contrib/__pycache__/socks.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/contrib/emscripten/__init__.py +.venv_translate/lib/python3.12/site-packages/urllib3/contrib/emscripten/connection.py +.venv_translate/lib/python3.12/site-packages/urllib3/contrib/emscripten/emscripten_fetch_worker.js +.venv_translate/lib/python3.12/site-packages/urllib3/contrib/emscripten/fetch.py +.venv_translate/lib/python3.12/site-packages/urllib3/contrib/emscripten/request.py +.venv_translate/lib/python3.12/site-packages/urllib3/contrib/emscripten/response.py +.venv_translate/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/connection.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/fetch.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/request.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/response.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/http2/__init__.py +.venv_translate/lib/python3.12/site-packages/urllib3/http2/connection.py +.venv_translate/lib/python3.12/site-packages/urllib3/http2/probe.py +.venv_translate/lib/python3.12/site-packages/urllib3/http2/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/http2/__pycache__/connection.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/http2/__pycache__/probe.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/util/__init__.py +.venv_translate/lib/python3.12/site-packages/urllib3/util/connection.py +.venv_translate/lib/python3.12/site-packages/urllib3/util/proxy.py +.venv_translate/lib/python3.12/site-packages/urllib3/util/request.py +.venv_translate/lib/python3.12/site-packages/urllib3/util/response.py +.venv_translate/lib/python3.12/site-packages/urllib3/util/retry.py +.venv_translate/lib/python3.12/site-packages/urllib3/util/ssl_.py +.venv_translate/lib/python3.12/site-packages/urllib3/util/ssl_match_hostname.py +.venv_translate/lib/python3.12/site-packages/urllib3/util/ssltransport.py +.venv_translate/lib/python3.12/site-packages/urllib3/util/timeout.py +.venv_translate/lib/python3.12/site-packages/urllib3/util/url.py +.venv_translate/lib/python3.12/site-packages/urllib3/util/util.py +.venv_translate/lib/python3.12/site-packages/urllib3/util/wait.py +.venv_translate/lib/python3.12/site-packages/urllib3/util/__pycache__/__init__.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/util/__pycache__/connection.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/util/__pycache__/proxy.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/util/__pycache__/request.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/util/__pycache__/response.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/util/__pycache__/retry.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/util/__pycache__/ssl_.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/util/__pycache__/ssl_match_hostname.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/util/__pycache__/ssltransport.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/util/__pycache__/timeout.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/util/__pycache__/url.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/util/__pycache__/util.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3/util/__pycache__/wait.cpython-312.pyc +.venv_translate/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/INSTALLER +.venv_translate/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/METADATA +.venv_translate/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/RECORD +.venv_translate/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/WHEEL +.venv_translate/lib/python3.12/site-packages/urllib3-2.6.3.dist-info/licenses/LICENSE.txt diff --git a/.translation_cache.json b/.translation_cache.json new file mode 100644 index 000000000..9a0f36966 --- /dev/null +++ b/.translation_cache.json @@ -0,0 +1 @@ +{"/ 缓存策略。": "/caching policy.", "/ 请尽量不要再BaseCache以外调用这个对象的方法,尤其Cache的Key在DictionaryCache中是会被重新定义的": "/ Please try not to call the method of this object outside BaseCache, especially the Key of Cache will be redefined in DictionaryCache", "/ 超时时间,1400分钟为1天。": "/ Timeout time, 1400 minutes is 1 day.", "设置缓存集合键,必须提供": "Set cache collection key, required", "/ Data不能在Update()方法中调用,否则会引发循环调用。Update()方法中应该使用SetData()方法": "/Data cannot be called in the Update() method, otherwise it will cause a loop call. SetData() method should be used in Update() method", "/ Data只适用于简单类型,如果缓存类型为列表,则不适用": "/Data only works with simple types, not applicable if the cache type is a list", "/ 设置整个缓存数据": "/Set the entire cache data", "/ 获取全部缓存数据": "/ Get all cached data", "/ 单位:分钟。1440为一天。": "/ Unit: minutes. 1440 is one day. ", "/ 获取缓存中最终的Key": "/ Get the final Key in the cache", "TODO:判断Key类型": "TODO: Determine Key type", "将对象序列化,然后拼接成字符串并转成MD5,确保唯一性。性能上可能会有一些损失,所以尽量不要太复杂的类型做Key": "Serialize the object, then concatenate it into a string and convert it to MD5 to ensure uniqueness. There may be some loss in performance, so try not to use too complex types as keys.", "有的缓存策略可能不允许:作为分隔符[LocalCache 存在问题]": "Some caching policies may not allow: as a delimiter [Problem with LocalCache]", "//.NET 8.0 中已标记为过时且无法编译": "// Marked obsolete and uncompilable in .NET 8.0", "/// 获取指定Key下所有的子Key的Value": "/// Get the Value of all sub-Keys under the specified Key", "强制初始化": "Force initialization", "var msg = \"System debug record cache a bug。发生错误:{0}。当前参数:base.Data:{1}(Count:{4}),key:{2},obj:{3}。Null情况分别是:{4},{5},{6}\"": "var msg = \"System debug record cache a bug. Error occurred: {0}. Current parameters: base.Data: {1} (Count: {4}), key: {2}, obj: {3}. Null cases are: {4}, {5}, {6}\"", "实际上这里base.Data还是为null": "In fact, base.Data here is still null.", "/ 缓存键": "/cache key", "协议": "protocol", "主机名(不带端口)": "Hostname (without port)", "端口(因为从.NET Framework移植,因此不直接使用urlData.Host)": "Port (does not use urlData.Host directly because it is ported from .NET Framework)", "Url中的端口部分": "Port part in URL", "协议(大写)": "Agreement (uppercase)", "子站点应用路径": "Subsite application path", "这个条件只有在 .net core 中, Host.Port == null 的情况下才会发生": "This condition only occurs when Host.Port == null in .net core", "使用默认值": "Use default value", "添加端口": "Add port", "尝试安装": "try to install", "/// 手机验证码": "/// Mobile phone verification code", "/ 手机验证码": "/Mobile phone verification code", "/ 插入缓存": "/insert cache", "/ 如果为null将自动生成16位Guid": "/ If it is null, a 16-bit Guid will be automatically generated", "/ 小于等于0则没有过期时间": "/ If it is less than or equal to 0, there will be no expiration time", "/ 获取MessageContext,如果不存在,返回null": "/ Get MessageContext, if it does not exist, return null", "/ 这个方法的更重要意义在于操作TM队列,及时移除过期信息,并将最新活动的对象移到尾部": "/ The more important significance of this method is to operate the TM queue, remove expired information in time, and move the latest active objects to the end.", "/ 用户名(OpenId)": "/ Username (OpenId)", "/ 是否清除key": "/ Whether to clear key", "从队列中移除过期对象": "Remove expired objects from queue", "从集合中删除过期对象": "Remove expired objects from collection", "* 全局只有在这里用到MessageCollection.ContainsKey": "*Global MessageCollection.ContainsKey is only used here", "* 充分分离MessageCollection内部操作,": "* Fully separate the internal operations of MessageCollection,", "* 为以后变化或扩展MessageCollection留余地": "* Leave room for future changes or extensions to MessageCollection", "全局只在这一个地方使用MessageCollection[Key]写入": "Globally only use MessageCollection[Key] to write in this one place", "插入列队": "insert queue", "最新的排到末尾": "Newest at the end", "MessageQueue.RemoveAt(0);//从队列中移除过期对象": "MessageQueue.RemoveAt(0);//Remove expired objects from the queue", "MessageCollection.Remove(firstMessageContext.Key);//从集合中删除过期对象": "MessageCollection.Remove(firstMessageContext.Key);//Remove expired objects from the collection", "如果不是新建的对象,把当前对象移到队列尾部(新对象已经在底部)": "If it is not a new object, move the current object to the end of the queue (the new object is already at the bottom)", "移除当前对象": "Remove current object", "插入到末尾": "insert at end", "记录请求时间": "Log request time", "/ AdminUserInfo 创建和更新": "/AdminUserInfo Create and Update", "/ 数据库 Dto 基类": "/ Database Dto base class", "/ 是否软删除": "/ Whether to soft delete", "/ 添加时间": "/add time", "/ 上次更新时间": "/ last updated time", "/ 租户 ID": "/Tenant ID", "/ 如果为-1,则本系统不启用多租户": "/ If it is -1, multi-tenancy is not enabled in this system", "/ 如果为0,则为系统公共数据(特殊情况使用)": "/ If it is 0, it is system public data (used in special circumstances)", "/ 所有 DTO 接口或类的基类": "/ Base class for all DTO interfaces or classes", "/ 菜单id": "/ menu id", "/ 操作名称": "/ operation name", "/ 操作标识": "/ operation identifier", "/ 按钮对应的请求地址": "/Request address corresponding to the button", "/ 是否是菜单": "/ Whether it is a menu", "/ 父菜单": "/parent menu", "/ 图标": "/ icon", "/ 是否可见": "/ is visible", "/ 菜单树": "/menu tree", "/ 角色代码": "/role code", "/ 资源(按钮)代码": "/ Resource (button) code", "/ 角色名称": "/ role name", "/ 角色编号": "/ role number", "/ 用户编号": "/user number", "/ 当前用户是否有此角色": "/ Whether the current user has this role", "/ 启用": "/enable", "/ 跟新菜单Id": "/ Follow the new menu ID", "/ 空的实现 IEntityTypeConfiguration 接口的类": "/ Empty class that implements the IEntityTypeConfiguration interface", "/ 设置 TEntity 的实体": "/Set the entity of TEntity", "/ 用于设置实体类型(entity type)的 builder": "/ Builder used to set entity type (entity type)", "/ 包含 Id(Key)的 ConfigurationMapping 基类": "/ ConfigurationMapping base class containing Id (Key)", "/ 配置 实例": "/ Configure instance", "/ 不包含 Id(Key)的 ConfigurationMapping 基类": "/ ConfigurationMapping base class that does not contain Id (Key)", "/ 菜单对应的按钮": "/ button corresponding to the menu", "/ 菜单表": "/ menu table", "/ 是否锁定, 锁定后不能 修改和删除": "/ Whether to lock or not, it cannot be modified or deleted after locking.", "/ 类型": "/ type", "/ 操作资源": "/operating resources", "/ 系统角色": "/system role", "/ 启用状态": "/ enabled status", "/ 角色人员表": "/Character list", "/ 管理员Id": "/adminId", "/ 角色Id": "/ roleId", "/ 角色菜单表": "/Character menu table", "注意:这里 Table 如果用 SysPermissions,将和 SQL Server 的系统表冲突": "Note: If SysPermissions is used in Table here, it will conflict with the system tables of SQL Server.", "* 参考:https://docs.microsoft.com/zh-cn/sql/relational-databases/system-compatibility-views/sys-syspermissions-transact-sql?redirectedfrom=MSDN&view=sql-server-ver15": "* Reference: https://docs.microsoft.com/zh-cn/sql/relational-databases/system-compatibility-views/sys-syspermissions-transact-sql?redirectedfrom=MSDN&view=sql-server-ver15", "/ 权限Id(菜单或者是按钮)": "/ Permission ID (menu or button)", "/ 是否隐藏模块管理": "/ Whether to hide module management", "/ 更新": "/ renew", "/ 扩展模块信息": "/Extension module information", "/ 使用 FontAwesome 图标,如:fa fa-star": "/ Use FontAwesome icons, such as: fa fa-star", "/ 添加日志": "/Add log", "/ 数据库实体基类": "/ Database entity base class", "/ 仅管理员备注": "/Admin comments only", "/ 前台用户可见备注": "/ Remarks visible to front-end users", "/ 更新最后更新时间": "/Update last update time", "通常在添加的时候发生": "Usually happens when adding", "/ 带单一主键的数据库实体基类": "/ Database entity base class with single primary key", "默认支持多租户接口": "Supports multi-tenant interface by default", "/ 主键": "/ primary key", "/ 聚合根": "/ aggregate root", "/ 所有实体的最底层接口(支持软删除)": "/ The lowest level interface of all entities (supports soft deletion)", "/ 更新时间": "/Update time", "/ 多租户接口": "/Multi-tenant interface", "/ 租户Id": "/TenantId", "/ 数据库数据软删除接口": "/ Database data soft deletion interface", "/ 多数据库配置工厂": "/Multi-database configuration factory", "/ DatabaseConfigurationFactory 的全局单例": "/Global singleton of DatabaseConfigurationFactory", "TODO:如果是分布式,需要存储到缓存中": "TODO: If it is distributed, it needs to be stored in the cache.", "/// 给 design time(设计时)操作数据库(如migration)使用。指定当前正在操作的 XNCF 数据库信息(如果是直接继承自 DbContext 的类,需要模拟此参数)": "/// Used for design time (design time) operation database (such as migration). Specify the XNCF database information currently being operated (if it is a class directly inherited from DbContext, this parameter needs to be simulated)", "/ NCF 数据库帮助类": "/NCF database help class", "/ 系统表的数据库前缀,使用此前缀,将使用 __EFMigrationsHistory 表储存迁移记录,取代模块自定义表": "/ The database prefix of the system table. Using this prefix, the __EFMigrationsHistory table will be used to store migration records instead of the module custom table.", "/ 获取 EF Code First MigrationHistory 数据库表名": "/ Get EF Code First MigrationHistory database table name", "也可以抛出异常": "Exceptions can also be thrown", "/ 数据库配置接口": "/Database configuration interface", "/ 官方推荐的数据库提供程序:https://docs.microsoft.com/zh-cn/ef/core/providers/?tabs=dotnet-core-cli ": "/ Officially recommended database provider: https://docs.microsoft.com/zh-cn/ef/core/providers/?tabs=dotnet-core-cli ", "/ DbContextOptionsBuilder 的类型,如:SQL Server 使用 SqlServerDbContextOptionsBuilder": "/ The type of DbContextOptionsBuilder, such as: SQL Server uses SqlServerDbContextOptionsBuilder", "/ 数据库类型": "/ database type", "/ 操作 DbContextOptions 的基础方法": "/ Basic methods for operating DbContextOptions", "/ 操作 DbContextOptions 的扩展方法": "/ Extension method to operate DbContextOptions", "/ 设定 UseSLite、UseSQLServer 等方法": "/ Set methods such as UseSLite and UseSQLServer", "/ 使用数据库,如:": "/ Use a database, such as:", "/ 连接字符串": "/ Connection string", "/ 额外配置操作": "/ Additional configuration operations", "/ IXncfDatabase 信息(仅在针对 XNCF 进行数据库迁移时有效)": "/ IXncfDatabase information (valid only when doing database migration for XNCF)", "/ 备份数据库方法": "/ Backup database method", "/ 如果返回null,则在方法内部完成备份程序": "/ If null is returned, the backup procedure is completed inside the method", "/ 删除指定表Sql": "/ Delete the specified table Sql", "/ 如果返回null,则在方法内部完成删除操作": "/ If null is returned, the deletion operation is completed inside the method", "/ 多数据库系统中,特定的某个数据库类型": "/ In a multi-database system, a specific database type", "TODO:更多": "TODO:More", "/ XNCF 模块数据库配置": "/XNCF module database configuration", "/ 数据库表全局唯一的前缀,务必避免和其他模块重复": "/ Globally unique prefix for database tables, be sure to avoid duplication with other modules", "/ 创建数据库模型": "/Create database model", "/// 设置数据库,主要提供给使用": "/// Set up the database, mainly provided for use", "/// MigrationsAssembly 的程序集名称,如果为 null,为默认使用当前 XncfDatabaseDbContextType 所在的程序集": "/// The assembly name of MigrationsAssembly. If it is null, the assembly where the current XncfDatabaseDbContextType is located will be used by default", "/// XncfDatabaseDbContext 类型": "/// XncfDatabaseDbContext type", "/ 添加数据库模块": "/Add database module", "/ 尝试获取 当前配置下的数据库上下文类型,如果未找到对应数据库,会抛出异常:)": "/ Try to obtain the database context type under the current configuration. If the corresponding database is not found, an exception will be thrown: )", "/ XNCF 数据库模块信息,仅在单独操作特定 XNCF 数据库模块时有用,其他情况下对象可能为 null": "/ XNCF database module information, only useful when operating a specific XNCF database module alone, the object may be null in other cases", "/ DbContext 类型": "/DbContext type", "/ 指定程序集名称": "/Specify the assembly name", "/ Migration History 表名": "/Migration History table name", "/ 强制手动更改DetectChange": "/ Force manual change of DetectChange", "/ NcfDbData,NCF 的数据库上下文封装,基础类": "/ NcfDbData, NCF database context encapsulation, base class", "/ 当前上下文中租户信息 ": "/ Tenant information in the current context ", "/ 重置合并状态": "/Reset merge status", "/ 执行 EF Core 的合并操作(等价于 update-database)": "/ Perform the merge operation of EF Core (equivalent to update-database)", "/ 出于安全考虑,每次执行 Migrate() 方法之前,必须先执行 ResetMigrate() 开启允许 Migrate 执行的状态。": "/ For security reasons, before each execution of the Migrate() method, ResetMigrate() must be executed to enable the state that allows Migrate execution. ", "/ SenparcEntities 的基类": "/ Base class for SenparcEntities", "/ SenparcDI 储存的 GlobalServiceCollection 生成的 ServiceProvider": "/ ServiceProvider generated by GlobalServiceCollection stored in SenparcDI", "/ 自动添加多租户Id": "/ Automatically add multi-tenant ID", "//如果未设置,则进行设定": "//If not set, set it", "/ 是否启用多租户,默认读取 SiteConfig.SenparcCoreSetting.EnableMultiTenant": "/ Whether to enable multi-tenancy, the default is to read SiteConfig.SenparcCoreSetting.EnableMultiTenant", "/ 【核心】 将当前动态模块的 DbContext 对象注入到 DbContext": "/ [Core] Inject the DbContext object of the current dynamic module into the DbContext", "设置需要添加的全局查询": "Set the global query that needs to be added", "/ 设置全局查询": "/Set global query", "/ 全局查询": "/ Global query", "多租户": "multi-tenant", "多租户 + 软删除": "Multi-tenancy + soft delete", "仅软删除": "Soft delete only", "/ 设置当前 DbContext 是否启用上下文": "/ Set whether the current DbContext enables context", "/ 多租户状态重置为 SiteConfig.SenparcCoreSetting.EnableMultiTenant": "/Multi-tenant status reset to SiteConfig.SenparcCoreSetting.EnableMultiTenant", "return base.SaveChangesAsync(cancellationToken);//底层引用的就是 SaveChangesAsync,所以不用处理": "return base.SaveChangesAsync(cancellationToken);//The underlying reference is SaveChangesAsync, so there is no need to process it", "/ 地区数据(省、市、区)": "/Regional data (province, city, district)", "/ ID,Name前缀": "/ID,Name prefix", "如果首项为空,则ID设为-1": "If the first item is empty, the ID is set to -1", "另外一种写法:": "Another way to write it:", "获取缓存系统信息": "Get cache system information", "如数据库未创建": "If the database is not created", "需要进行安装": "Requires installation", "/ 检查是否在特定 Scheme 下已登录": "/ Check if you are logged in under a specific Scheme", "/ Scheme 名称": "/ Scheme name", "/ 设计时(或运行时进行 Database.Migrate() 操作的)所使用的 XncfDatabaseDbContext 类型": "/ The XncfDatabaseDbContext type used at design time (or runtime for Database.Migrate() operations)", "/ 运行时所使用的 XncfDatabaseDbContext 类型(通常为进行查询时)": "/ The XncfDatabaseDbContext type used at runtime (usually when querying)", "/ 多数据库配置池": "/Multiple database configuration pool", "/ Value 为 Dictionary": "/ Value is Dictionary", "IXncfDatabase Register 类型": "IXncfDatabaseRegisterType", "数据库 XncfDatabaseDbContext 类型": "Database XncfDatabaseDbContext type", "/ 单元测试用的 DbContext": "/ DbContext for unit testing", "/ 添加配置": "/Add configuration", "/ 实现了 IXncfDatabase 接口的类型": "/ Type that implements the IXncfDatabase interface", "/ 输出日志表格的宽度": "/ Width of the output log table", "查看是否已经包含 MultipleDatabaseType": "Check if MultipleDatabaseType is already included", "添加 MultipleDatabaseType 对应集合": "Add MultipleDatabaseType corresponding collection", "加入配置": "Add configuration", "同步添加到 XncfDatabaseDbContextPool": "Synchronously added to XncfDatabaseDbContextPool", "/ 获取指定 IXncfDatabase 关联的当前数据库上下文(DbContext)": "/ Get the current database context (DbContext) associated with the specified IXncfDatabase", "/ 实现了 IXncfDatabase 的实体": "/ Entity that implements IXncfDatabase", "/ 实现了 IXncfDatabase 的具体类型": "/ Implements the specific type of IXncfDatabase", "数据库配置工厂": "Database configuration factory", "当前数据库配置": "Current database configuration", "当前数据库类型": "Current database type", "单元测试": "Unit testing", "/ 获取指定 DbContext 的数据库实例": "/ Get the database instance of the specified DbContext", "/ 连接字符串,如果为 null,则默认使用 SenparcDatabaseConfigs.ClientConnectionString": "/ Connection string, if null, defaults to SenparcDatabaseConfigs.ClientConnectionString", "带泛型": "With generics", "准备创建 DbContextOptionsBuilder 实例,定义类型": "Prepare to create a DbContextOptionsBuilder instance and define the type", "获取泛型对象类型,如:DbContextOptionsBuilder": "Get the generic object type, such as: DbContextOptionsBuilder", "创建 DbContextOptionsBuilder 实例": "Create a DbContextOptionsBuilder instance", "不带泛型": "Without generics", "不是单元测试,需要读取数据库": "Not a unit test, needs to read the database", "获取当前数据库配置": "Get the current database configuration", "指定使用当前数据库": "Specify to use the current database", "实例化 DbContext": "Instantiate DbContext", "/ 获取指定 xncfDatabaseRegister 关联的当前数据库实例": "/ Get the current database instance associated with the specified xncfDatabaseRegister", "获取 DbContext 上下文类型": "Get the DbContext context type", "/ 以 IXncfDatabase Register 类型为 Key 的多数据库上下文(DbContext)配置池": "/ Multiple database context (DbContext) configuration pool with IXncfDatabase Register type as Key", "查看是否已经包含 IDatabaseRegister": "Check if IDatabaseRegister is already included", "/ 获取当前数据库连接信息": "/ Get current database connection information", "/ 尝试获取数据库连接信息的值,如果获取不到,则返回 null": "/ Try to get the value of the database connection information, if not, return null", "/ 数据库信息": "/ Database information", "/ 名称,如 Database、UserName,不区分大小写": "/ Names, such as Database, UserName, are not case sensitive", "/ 多数据库生成 Migration 的实体类接口": "/ Entity class interface for multi-database generation Migration", "///// 数据库类型": "///// Database type", "/ 多数据库生成 Migration 的实体类,无需考虑": "/ Multiple databases generate Migration entity classes, no need to consider", "暂时不支持": "Not supported at the moment", "/ MultipleMigrationDbContext 构造函数。指定多数据库配置。": "/ MultipleMigrationDbContext constructor. Specify a multi-database configuration.", "/ MultipleDatabaseType 数据库类型": "/ MultipleDatabaseType database type", "/ XncfDatabase 注册类类型": "/ XncfDatabase registration class type", "/// 当运行时使用的统一数据库上下文类型": "/// The unified database context type used when running", "/ 弥补 MySQL 库暂时的缺陷": "/ Make up for the temporary shortcomings of the MySQL library", "/ 获取 MySqlValueGenerationStrateg.IdentityColumn 等枚举": "/ Get enumerations such as MySqlValueGenerationStrateg.IdentityColumn", "抛出异常": "throw an exception", "/ 数据库注册类": "/ Database registration class", "/// 使用指定数据库": "/// Use the specified database", "//必须实现 IDatabaseConfiguration 接口的类型才能进行下一步配置": "//The type that must implement the IDatabaseConfiguration interface can be used for the next step of configuration.", "throw new NcfDatabaseException($\"类型{databaseConfigurationType.Name} 必须实现接口:IDatabaseConfiguration\", databaseConfigurationType);": "throw new NcfDatabaseException($\"Type {databaseConfigurationType.Name} must implement the interface: IDatabaseConfiguration\", databaseConfigurationType);", "throw new NcfDatabaseException($\"{nameof(databaseConfiguration)} 参数不能为 null\", null);": "throw new NcfDatabaseException($\"{nameof(databaseConfiguration)} parameter cannot be null\", null);", "/// DatabaseConfiguration 程序集名称": "/// DatabaseConfiguration assembly name", "/// DatabaseConfiguration 命名空间": "/// DatabaseConfiguration namespace", "/// DatabaseConfiguration 类名": "/// DatabaseConfiguration class name", "//TODO:集成到 CO2NET": "//TODO: Integrated into CO2NET", "string fullName = nameSpace + \".\" + className;//命名空间.类型名": "string fullName = nameSpace + \".\" + className;//Namespace.Type name", "var databaseConfiguration = Assembly.Load(assemblyName).CreateInstance(fullName) as IDatabaseConfiguration;//加载程序集,创建程序集里面的 命名空间.类型名 实例": "var databaseConfiguration = Assembly.Load(assemblyName).CreateInstance(fullName) as IDatabaseConfiguration;//Load the assembly and create the namespace.type name instance in the assembly", "/ UseNcfDatabase() 方法是否已经执行完毕": "/ Whether the UseNcfDatabase() method has been executed", "/ 使用指定数据库": "/ Use the specified database", "/ 可用的 DatabaseConfiguration,非 ": "/ Available DatabaseConfiguration, not ", "必须实现 IDatabaseConfiguration 接口的类型才能进行下一步配置": "The type that must implement the IDatabaseConfiguration interface can be used for the next step of configuration.", "/ DatabaseConfiguration 程序集名称": "/ DatabaseConfiguration assembly name", "/ DatabaseConfiguration 命名空间": "/ DatabaseConfiguration namespace", "/ DatabaseConfiguration 类名": "/ DatabaseConfiguration class name", "TODO:集成到 CO2NET": "TODO: Integrate into CO2NET", "命名空间.类型名": "namespace.typename", "加载程序集,创建程序集里面的 命名空间.类型名 实例": "Load the assembly and create the namespace.typename instance in the assembly", "/ 使用指定数据库(必须在 UseXncfModule() 方法之后执行)": "/ Use the specified database (must be executed after the UseXncfModule() method)", "判断是否为默认数据库配置(使用 appsettings.json 文件)": "Determine whether it is the default database configuration (using the appsettings.json file)", "此时还没有设置完成": "The setting has not been completed yet", "//供单元测试使用": "//for unit testing", "Console.WriteLine(\"进入单元测试环境\");": "Console.WriteLine(\"Enter the unit test environment\");", "添加数据库": "Add database", "/ 数据库连接信息配置": "/ Database connection information configuration", "/ 主站客户数据库连接字符串(根据当前配置数据库动态获得)": "/ Main site customer database connection string (obtained dynamically based on the current configuration database)", "/ 获取完整名称": "/ get full name", "/ SQLite 数据库配置": "/SQLite database configuration", "其他更多配置": "Other more configurations", "执行 UseInMemory(必须)": "Execute UseInMemory (required)", "更多数据库操作独立配置(非必须)": "More independent configuration of database operations (not required)", "不需要用到": "No need to use", "摘要:": "summary:", "返回结果:": "Return results:", "参数:": "parameter:", "言论:": "Speech:", "/ MySQL 数据库配置": "/MySQL database configuration", "ServerVersion 用法:https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/pull/1233": "ServerVersion usage: https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/pull/1233", "/ Oracle 数据库配置,Oracle 版本不小于 V12": "/Oracle database configuration, Oracle version is not less than V12", "/ 注意:如果使用 Oracle 12 以下的版本(11),请直接使用 OracleDatabaseConfigurationForV11。或使用手动方法控制(不推荐):在调用 services.AddDatabase<OracleDatabaseConfiguration>(); 之前,使用 SetUseOracleSQLCompatibility(string useOracleSQLCompatibility) 方法设置版本号,如 11.2,则输入 \"11\"": "/ Note: If you use a version below Oracle 12 (11), please use OracleDatabaseConfigurationForV11 directly. Or use manual method control (not recommended): Before calling services.AddDatabase<OracleDatabaseConfiguration>();, use the SetUseOracleSQLCompatibility(string useOracleSQLCompatibility) method to set the version number, such as 11.2, enter \"11\"", "/ 设置 UseOracleSQLCompatibility 的参数,如 11.2g,输入\"11\",12g,输入\"12\"(默认为 >= 12,此时可不输入)": "/ Set the parameters of UseOracleSQLCompatibility, such as 11.2g, enter \"11\", 12g, enter \"12\" (the default is >= 12, you don’t need to enter it at this time)", "/ 注意:此设置应该在执行 .AddDatabase<OracleDatabaseConfiguration>(); 之前执行": "/ Note: This setting should be performed before executing .AddDatabase<OracleDatabaseConfiguration>();", "/ 设置 OracleSQLCompatibility 的参数,默认为 19": "/Set the parameters of OracleSQLCompatibility, the default is 19", "TODO: 增加 schma": "TODO: Add schma", "/ Oracle V11 数据库配置(包括 V11.2 等版本)": "/Oracle V11 database configuration (including V11.2 and other versions)", "/ 注意:如果使用 Oracle 12 或以上版本,请直接使用 OracleDatabaseConfiguration": "/ Note: If using Oracle 12 or above, please use OracleDatabaseConfiguration directly", "/ PostgreSQL 数据库配置": "/ PostgreSQL database configuration", "解决 .NET 6.0 timestamp 问题: https://qiita.com/k-yamamoto/items/a87989569cb6f0415fbb": "Resolving .NET 6.0 timestamp issues: https://qiita.com/k-yamamoto/items/a87989569cb6f0415fbb", "需要执行exit后才能读取 StandardOutput": "You need to execute exit before you can read StandardOutput.", "/ SQL Server 数据库配置": "/ SQL Server database configuration", "检查并调整路径": "Check and adjust paths", "去掉 \"Filename=\" 前缀": "Remove the \"Filename=\" prefix", "执行 UseSqlite(必须)": "Execute UseSqlite (required)", "新建的领域可以在这里继续添加": "New fields can be added here", "//没有载入log成功": "//No log loaded successfully", "/ 记录错误信息的扩展方法": "/ Extension method for recording error information", "/ “上移”“下移”箭头": "/ \"Move up\" \"Move down\" arrows", "/// 返回由枚举类型生成的自定义样式下拉选项": "/// Returns the custom style drop-down option generated by the enumeration type", "/ 返回由枚举类型生成的下拉选项": "/ Returns the drop-down options generated by the enumeration type", "#region 直接引用代码(仅测试):http://www.veryhuo.com/a/view/7590.html": "#region Directly quote the code (testing only): http://www.veryhuo.com/a/view/7590.html", "/ Bootstrap当前菜单": "/Bootstrap current menu", "/ 数据源": "/ Data source", "/ Table的属性": "/ Table attributes", "/ 没有数据时显示。没有数据时,Footer不显示": "/ Displayed when there is no data. When there is no data, Footer does not display", "/ HTML代码换行(供调试状态下使用)。如果为false,自动生成的代码将不换行": "/ HTML code line wrapping (for use in debugging state). If false, automatically generated code will not wrap.", "/ 模板数据": "/ Template data", "属性,内容": "properties, content", "整个Table": "Entire Table", "thead内单元格": "cells within thead", "行": "OK", "tfoot内的单元格": "cells within tfoot", "* 构造Table开始 *": "* Start constructing Table *", "head行": "head line", "tbody 开始": "tbody start", "如果数据为空,则创建一个空的记录,用于获取Count": "If the data is empty, create an empty record for getting Count", "tbody一行内的单元格": "cells within a row of tbody", "本行模板数据": "Template data of the Bank", "判断body单元格是否自动绑定": "Determine whether the body cell is automatically bound", "自动绑定": "Automatic binding", "单元格属性": "Cell properties", "单元格": "cell", "整个tbody": "the whole tbody", "tbody 结束": "tbody end", "footer没有?": "There is no in footer?", "foot行": "foot row", "空数据": "empty data", "整合head,body,foot": "Integrate head, body, foot", "属性": "property", "整合整个Table,包括属性": "Integrate the entire Table, including attributes", "* 构造Table结束 *": "* End of constructing Table *", "/ 自动绑定GridView": "/ Automatically bind GridView", "过滤为值类型": "Filter to value type", "/ 供Onclick操作的span(样式默认为“onclick”)": "/ span for Onclick operation (the style defaults to \"onclick\")", "/ 事件名称(onclick中的所有内容)": "/ Event name (everything in onclick)", "/ 文字": "/ Text", "/ 供Onclick操作的span": "/ span for Onclick operation", "/ class样式,如果为空,则使用“onclick”": "/ class style, if empty, use \"onclick\"", "样式": "style", "默认": "default", "/ 项目中处在iframe中的页码条": "/ The page number bar in the iframe in the project", "设置默认值": "Set default value", "判断空记录": "Determine empty records", "总页数": "Total pages", "从第1页显示到xx页": "Display from page 1 to page xx", "当前页临近最左页码": "The current page is adjacent to the leftmost page number", "当前页临近最右页码": "The current page is adjacent to the rightmost page number", "从第xx页显示到最后一页": "Display from page xx to the last page", "设定 bodyDisplayPageStart": "Set bodyDisplayPageStart", "设定 bodyDisplayPageEnd": "Set bodyDisplayPageEnd", "设定 firstDisplayPageEnd": "Set firstDisplayPageEnd", "设定 endDisplayPageStart": "Set endDisplayPageStart", "页面参数设定结束": "Page parameter setting ends", "开始输出": "Start output", "上一条": "Previous article", "省略号": "Ellipsis", "下一条": "Next article", "总数": "total", "如果超出最大值,则修正为最大值": "If it exceeds the maximum value, correct it to the maximum value", "var pageData = \"?page=\";//string.Format(\"{0}page=\", (Request.QueryString.Count == 0) ? \"?\" : \"&\") + \"{0}\";//页码参数": "var pageData = \"?page=\";//string.Format(\"{0}page=\", (Request.QueryString.Count == 0) ? \"?\" : \"&\") + \"{0}\";//Page number parameter", "判断是{0}形式还是自定义替换字符串。": "Determine whether it is in {0} form or a custom replacement string.", "开始、结束标签": "start and end tags", "单项开始、结束标签": "Individual start and end tags", "目前没有用到,如果直接在这里用context.Response.Write输出则需要。": "It is not used currently. It is required if you use context.Response.Write to output directly here.", "/ 第一个单项循环开始之前的内容": "/ Content before the start of the first single item loop", "/ 单项内容": "/ Single item content", "/ 每次循环分隔内容(Table慎用)": "/ Separate content in each loop (use Table with caution)", "/ 最后一个单项循环结束之后的内容": "/ Content after the end of the last single item loop", "/ repeater模式": "/ repeater mode", "/ 每行烈数(只在repeaterMode为Table时有效)": "/ Count of each row (only valid when repeaterMode is Table)", "/ 标签属性": "/ Tag attributes", "* 单项循环开始 *": "* Single cycle starts *", "仅适用Table": "Applies only to Table", "添加行开始标记": "Add line start tag", "单项开头": "Beginning with a single item", "主体内容": "Main content", "单项结尾": "single ending", "添加行结束标记": "Add end of line tag", "添加分隔符": "Add separator", "* 单项循环结束 *": "* End of single cycle *", "为Table添加结束标记": "Add closing tag to Table", "空白单元格": "blank cell", "/ 不自动加任何多于标记,等同于foreach。但header和footer仍然有效": "/ Does not automatically add any more than tags, equivalent to foreach. But header and footer are still valid", "/ 获取分页列表": "/ Get paginated list", "/ 搜索条件": "/ Search conditions", "/ 当pageCount小于等于0时不分页": "/ No paging when pageCount is less than or equal to 0", "/ 此方法会自动判断应当执行更新(Update)还是添加(Add)": "/ This method will automatically determine whether to perform Update (Update) or Add (Add)", "/ 批量删除": "/ Batch delete", "/ 删除每一个对象的操作": "/ The operation of deleting each object", "/ 批量添加": "/ Batch add", "/ 动态字段排序": "/Dynamic field sorting", "/ 开启事务": "/ start transaction", "/ 回滚事务": "/ rollback transaction", "/ 提交事务": "/ Commit transaction", "DB = db ?? ObjectFactory.GetInstance();//如果没有定义,取默认数据库": "DB = db ?? ObjectFactory.GetInstance();//If not defined, take the default database", "TODO:EF5、Core 验证正确性": "TODO: EF5, Core verification correctness", "catch (ArgumentException ex)//DbArithmeticExpression 参数必须具有数值通用类型。": "catch (ArgumentException ex)//DbArithmeticExpression parameter must have a numeric generic type.", "//通常是ordery by的问题 TODO:重新整理是否需要Skip等操作": "//Usually a problem with order by TODO: Does reorganizing require operations such as Skip?", "// .OrderByIEnumerable(orderBy.Compile(), orderingType)//改用非延时地方法,效率最低": "// .OrderByIEnumerable(orderBy.Compile(), orderingType)//Switch to non-delayed method, the lowest efficiency", "// .Where(where.Compile())//保险起见用where.Compile(),但是会影响效率": "// .Where(where.Compile())//Use where.Compile() to be on the safe side, but it will affect efficiency", "/ 删除对象": "/ delete object", "/ 是否使用软删除": "/ Whether to use soft delete", "软删除": "soft delete", "硬删除": "hard delete", "/ 批量添加, 待优化": "/ Batch addition, to be optimized", "//BaseDB.BaseDataContext.Set().(obj);//硬删除": "//BaseDB.BaseDataContext.Set().(obj);//Hard deletion", "/ 动态排序": "/ dynamic sorting", "/ 开启事物": "/turn on things", "/ 回滚事物": "/ rollback transaction", "/ 删除某个页面下的所有按钮": "/ Delete all buttons under a page", "升级至 EF Core 5.0 后方法无效": "Method not working after upgrading to EF Core 5.0", "/ 获取某个用户下面的所有资源": "/ Get all resources under a user", "查询角色": "Query roles", "查询所拥有的菜单": "Query the menu you own", "/ 短信平台类型": "/ SMS platform type", "/ 短信发送状态": "/ SMS sending status", "/ 发送": "/ send", "/ 获取剩余短信数量": "/ Get the remaining number of text messages", "组装消息,判断内容长度,太长则分开发": "Assemble the message and determine the length of the content. If it is too long, develop it separately.", "分批发送": "Send in batches", "msg += \"【盛派网络】\";": "msg += \"[Shengpai Network]\";", "/ 发送结果": "/Send results", "/ 获取剩余短信条数结果": "/ Get the result of the number of remaining text messages", "/ 格式:{\"Code\":1,\"Msg\":\"成功!\",\"Value\":\"{\\\"UserName\\\":\\\"test\\\",\\\"OperId\\\":\\\"sms0013\\\",\\\"SMSNum\\\":5846,\\\"EmergentSMSNum\\\":10,\\\"IsEnable\\\":1}\"}": "/ Format: {\"Code\":1,\"Msg\":\"Success!\",\"Value\":\"{\\\"UserName\\\":\\\"test\\\",\\\"OperId\\\":\\\"sms0013\\\",\\\"SMSNum\\\":5846,\\\"EmergentSMSNum\\\":10,\\\"IsEnable\\\":1}\"}", "/ Fissoft_LastCountResult的Value类型": "/Value type of Fissoft_LastCountResult", "/ 接收回复信息": "/Receive reply message", "/ 通用加密": "/Universal Encryption", "/ 通用解密": "/ universal decryption", "指定秒后失效": "Expires after specified seconds", "/ 临时开放BaseDataContext.Configuration.AutoDetectChangesEnabled属性,用于大批量更新数据的环境,结束后还原到false状态。": "/ Temporarily open the BaseDataContext.Configuration.AutoDetectChangesEnabled property, which is used in environments where data is updated in large batches. It will be restored to the false state after completion.", "/ 创建AutoDetectChangeContext的实例": "/ Create an instance of AutoDetectChangeContext", "/ 创建CloseAutoDetectChangeContext的实例": "/ Create an instance of CloseAutoDetectChangeContext", "/ AutoDetectChangeContextWrap实例": "/ AutoDetectChangeContextWrap instance", "/// 开启事务": "/// Start transaction", "/ 获取分页数据": "/ Get paging data", "/ 页码": "/ Page number", "/ 每页数量": "/ Number per page", "/ 条件": "/ Condition", "/ 排序字段": "/ Sort field", "/ 正序|倒叙": "/ Forward sequence | Flashback", "/ 强制将实体设置为Modified状态": "/ Force the entity to Modified state", "TODO: 提供异步版本": "TODO: Provide an asynchronous version", "/ 使用 Mapper.Map<TDto>(entity) 快速返回": "/ Use Mapper.Map<TDto>(entity) to return quickly", "/ 强制设置租户信息": "/ Force tenant information to be set", "/ 默认事务策略,微软文档用法": "/Default transaction policy, Microsoft document usage", "确保 Mapper 中有值": "Make sure there is a value in the Mapper", "/ 获取所有数据": "/ Get all data", "/ 排序字段 eg.(xxx desc, bbb aec),默认升序": "/ Sort field eg.(xxx desc, bbb aec), default ascending order", "/ 开启事务, 此方法回自动提交事务,失败则回滚": "/ Start the transaction. This method will automatically commit the transaction. If it fails, it will be rolled back.", "/ 开启事务, 此方法会自动提交事务,失败则回滚": "/ Start the transaction. This method will automatically commit the transaction and roll it back if it fails.", "/ 处理一个异常并抛出自定义的异常": "/ Handle an exception and throw a custom exception", "/ 将 PageList 转为 DTO 对象": "/ Convert PageList to DTO object", "/ 在指定对象保存后执行(无论是否成功),将影响全局所有保存过程": "/ Executed after the specified object is saved (whether successful or not), it will affect all save processes globally.", "/ 在指定对象删除后执行(无论是否成功),将影响全局所有保存过程": "/ Executed after the specified object is deleted (whether successful or not), it will affect all global save processes", "/ 在所有对象保存后执行(无论是否成功),将影响全局所有保存过程": "/ Executed after all objects are saved (whether successful or not), it will affect all save processes globally", "/ 记录 SignalR 全局上下文的类,可轮询执行操作": "/ Class that records the SignalR global context and can be polled to perform operations", "/ 静态实例对象": "/ static instance object", "/ 轮询间隔时间(毫秒),默认值:500": "/ Polling interval (milliseconds), default value: 500", "/ 参数:SignalrTicker,当前轮询的次数": "/ Parameter: SignalrTicker, the current number of polls", "/ 构造函数": "/Constructor", "这里不能直接调用Sender,因为Sender是一个不退出的“死循环”,否则这个构造函数将不会退出。": "Sender cannot be called directly here because Sender is an \"infinite loop\" that does not exit, otherwise the constructor will not exit.", "其他的流程也将不会再执行下去了。所以要采用异步的方式。": "Other processes will no longer be executed. So use an asynchronous approach.", "/ 注册 SignalrTicker,激活静态变量": "/Register SignalrTicker, activate static variables", "可以写一些初始化": "You can write some initialization", "/ TODO...重建菜单角色缓存": "/TODO...Rebuild menu character cache", "TODO:需要给出提示": "TODO: Need to give hints", "/ 获取缓存中的数据": "/ Get data from cache", "/ 获取缓存中的数据 TODO...": "/ Get the data in the cache TODO...", "/ 初始化菜单及其权限": "/Initialize menu and its permissions", "设置 TenantId 前缀,避免不同租户之间的 ID 冲突": "Set TenantId prefix to avoid ID conflicts between different tenants", "确保 DbContext 实例一致": "Ensure DbContext instances are consistent", "/ 获取数据库的菜单集合(线性)": "/ Get the menu collection of the database (linear)", "/ 添加角色信息": "/Add character information", "/ 添加权限信息": "/Add permission information", "await DbToCacheAsync();//暂时": "await DbToCacheAsync();//temporarily", "此处会调用SaveChangeAsync": "SaveChangeAsync will be called here", "暂时": "temporary", "/ 获取当前用户的权限": "/ Get the permissions of the current user", "/ 验证权限": "/Verify permissions", "/ 获取当前用户可以看见的菜单(可见)树形结构": "/ Get the menu (visible) tree structure visible to the current user", "/ 获取当前用户可以看见的菜单(可见)": "/ Get the menu that the current user can see (visible)", "/ 获取用户可见的所有资源(除菜单外)": "/ Get all resources visible to the user (except menus)", "/ 获取用户得权限(包括按钮)": "/ Get user permissions (including buttons)", "/ 检查当前用户是否权限": "/ Check whether the current user has permissions", "/ 资源codes": "/ Resource codes", "/ 当前用户Id": "/ Current user Id", "缓存当前用户的所有资源": "Cache all resources of the current user", "尝试从缓存读取用户的资源": "Try to read the user's resource from cache", "缓存 8 小时": "Cache for 8 hours", "获取当前用户的所有资源code": "Get all resource codes of the current user", "/ 检查并更新版本": "/ Check and update version", "/ 返回是否需要新增或更新": "/ Return whether new addition or update is required", "新增模块": "New module", "检查更新": "Check for updates", "清除缓存": "clear cache", "同步缓存锁": "Synchronous cache lock", "/ 事件总线发布接口": "/Event bus publishing interface", "/ 异步发布事件(非阻塞,写入 Channel 即返回)": "/ Publish events asynchronously (non-blocking, write to Channel and return)", "/ 异步发布派生事件(自动继承父事件的链信息,用于防止循环引用)": "/ Publish derived events asynchronously (automatically inherit the chain information of the parent event to prevent circular references)", "/ 反射帮助类": "/Reflection helper class", "/ 创建对象实例": "/Create object instance", "/ 要创建对象的类型": "/ The type of object to be created", "/ 类型所在程序集名称": "/ The name of the assembly where the type is located", "/ 类型所在命名空间": "/ The namespace where the type is located", "/ 类型名": "/ Type name", "强制转换类型": "cast type", "发生异常,返回类型的默认值": "An exception occurs, returning the default value of the type", "此为第一种写法": "This is the first way of writing", "返回": "return", "下面是第二种写法": "The following is the second way of writing", "string path = fullName + \",\" + assemblyName;//命名空间.类型名,程序集": "string path = fullName + \",\" + assemblyName;//namespace.typename,assembly", "Type o = Type.GetType(path);//加载类型": "Type o = Type.GetType(path); //Load type", "object obj = Activator.CreateInstance(o, true);//根据类型创建实例": "object obj = Activator.CreateInstance(o, true);//Create an instance based on the type", "return (T)obj;//类型转换并返回": "return (T)obj;//Type conversion and return", "发生异常,返回null": "An exception occurs and null is returned.", "/ 根据程序集、命名空间、类名得到类型": "/ Get the type based on assembly, namespace, and class name", "命名空间.类型名,程序集": "Namespace.Type name,assembly", "加载类型": "Load type", "/ 验证身份证号码": "/Verify ID number", "/ 身份证号码": "/ ID card number", "/ 验证成功为True,否则为False": "/ True if verification is successful, False otherwise", "/ 验证15位身份证号": "/ Verify 15-digit ID number", "/ 身份证号": "/ ID card number", "数字验证": "digital verification", "省份验证": "Province verification", "生日验证": "Birthday verification", "校验码验证": "Check code verification", "符合GB11643-1999标准": "Comply with GB11643-1999 standard", "/ 验证18位身份证号": "/ Verify 18-digit ID number", "符合15位身份证标准": "Meet the 15-digit ID card standard", "对进行了带符号扩展的操作数使用了按位或运算符": "Bitwise OR operator used on sign-extended operands", "/ QQWry 的摘要说明。": "/ Summary description of QQWry.", "第一种模式": "first mode", "/ 第一种模式": "/ first mode", "第二种模式": "Second mode", "/ 第二种模式": "/ Second mode", "每条记录长度": "length of each record", "/ 每条记录长度": "/ Length of each record", "数据库文件": "database file", "/ 文件对象": "/file object", "索引开始位置": "index start position", "/ 索引开始位置": "/ Index starting position", "索引结束位置": "index end position", "/ 索引结束位置": "/ index end position", "IP地址对象": "IP address object", "/ IP对象": "/IP object", "存储文本内容": "Store text content", "/ 存储文本内容": "/ store text content", "存储3字节": "Store 3 bytes", "/ 存储3字节": "/ store 3 bytes", "存储4字节": "Store 4 bytes", "/ 存储4字节IP地址": "/ stores 4-byte IP address", "#region 单件模式——TNT2": "#region Singleton Mode - TNT2", "/// 用于锁定单进程访问": "/// Used to lock single-process access", "构造函数": "Constructor", "/ IP数据库文件绝对路径": "/ IP database file absolute path", "数据库地址": "Database address", "FileAccess.Read,,FileShare.Read为后添加": "FileAccess.Read,,FileShare.Read is added after", "/ 根据客户端IP地址搜索": "/Search based on client IP address", "COCONET 另外可参考:https://blog.csdn.net/yzj_xiaoyue/article/details/79200714": "COCONET can also refer to: https://blog.csdn.net/yzj_xiaoyue/article/details/79200714", "根据IP地址搜索": "Search by IP address", "/ 搜索IP地址搜索": "/Search IP address search", "验证IP合法性": "Verify IP legitimacy", "将字符IP转换为字节": "Convert character IP to bytes", "取得具体信息": "Get specific information", "/ 取得具体信息": "/Get specific information", "读取第一个字节判断是否是标志字节": "Read the first byte to determine whether it is a flag byte", "读取国家偏移": "Read country offset", "转至偏移处": "Go to offset", "再次检查标志字节": "Check the flag byte again", "读取地区标志": "Read area flag", "普通模式": "Normal mode", "替换 By TNT2": "Replace By TNT2", "取得地区信息": "Get area information", "/ 读取地区名称": "/Read region name", "读取字符串": "Read string", "/ 读取字符串": "/ read string", "查找IP地址所在的绝对偏移量": "Find the absolute offset at which an IP address is located", "/ 查找IP地址所在的绝对偏移量": "/ Find the absolute offset at which the IP address is located", "比较第一个IP项": "Compare the first IP entry", "开始二分搜索": "Start binary search", "读出4字节的IP地址": "Read out the 4-byte IP address", "/ 从当前位置读取四字节,此四字节是IP地址": "/ Read four bytes from the current location, these four bytes are the IP address", "比较IP地址是否相同": "Compare IP addresses to see if they are the same", "/ 比较IP地址是否相同": "/ Compare IP addresses to see if they are the same", "/ 0:相等,1:ip大于beginIP,-1:小于": "/ 0: equal, 1: ip is greater than beginIP, -1: less than ", "比较两个字节是否相等": "Compare two bytes for equality", "/ 比较两个字节是否相等": "/ Compare two bytes to see if they are equal", "根据当前位置读取4字节": "Read 4 bytes based on the current position", "/ 从当前位置读取4字节,转换为长整型": "/ Read 4 bytes from the current position and convert to long integer", "根据当前位置,读取3字节": "According to the current position, read 3 bytes", "/ 根据当前位置,读取3字节": "/ According to the current position, read 3 bytes", "从当前位置读取3字节": "Read 3 bytes from current position", "/ 从当前位置读取3字节": "/ Read 3 bytes from the current position", "取得begin和end之间的偏移量": "Get the offset between begin and end", "/ 取得begin和end中间的偏移": "/ Get the offset between begin and end", "/// 获取第一个错误提示": "/// Get the first error message", "/// 通常是以/开头的完整路径": "/// Usually a full path starting with /", "/// 获取来源页面": "/// Get the source page", "/ 返回绝对地址": "/ Returns the absolute address", "/ 获取客户端信息": "/ Get client information", "/ 获取客户端地址(IP)": "/ Get client address (IP)", "/ 获取正确可用的生日": "/ Get the correct available birthday", "确保年在1921到今年之间": "Make sure the year is between 1921 and this year", "确保月份在1-12月之间": "Make sure the month is between January and December", "确保天在1-31日之间": "Make sure the day is between 1-31", "如果日期不正确,则减一": "If the date is incorrect, subtract one", "/ 获取本周六日期(周六为一周最后一天)": "/ Get the date of this Saturday (Saturday is the last day of the week)", "/ 获取本周日日期(周日为一周的第一天)": "/ Get the date of this Sunday (Sunday is the first day of the week)", "/ 获取当天是本年度第几周": "/ Get the current week of the year", "第一个周末(周六)是几号": "When is the first weekend (Saturday)?", "/ 获取指定周的第一天(周日)": "/ Get the first day of the specified week (Sunday)", "默认密钥向量": "Default key vector", "/ DES加密字符串": "/DES encrypted string", "/ 待加密的字符串": "/ String to be encrypted", "/ 加密密钥,要求为8位": "/ Encryption key, required to be 8 bits", "/ 加密成功返回加密后的字符串,失败返回源串": "/ Encryption successfully returns the encrypted string, failure returns the source string", "/ DES解密字符串": "/DES decrypt string", "/ 待解密的字符串": "/ String to be decrypted", "/ 解密密钥,要求为8位,和加密密钥相同": "/ The decryption key, which is required to be 8 bits, is the same as the encryption key", "/ 解密成功返回解密后的字符串,失败返源串": "/ Returns the decrypted string if decryption is successful, otherwise returns the source string", "/ 获取翻页时跳过的记录数": "/ Get the number of records skipped when turning pages", "/ 当前页码": "/ Current page number", "/ 每页记录数": "/ Number of records per page", "/ 获取数组的字典类型(key为index,value为数组内容)。通常用于配合枚举类型": "/ Get the dictionary type of the array (key is index, value is array content). Usually used with enumeration types", "/ 获取枚举成员,转为Dictionary类型": "/ Get the enumeration members and convert them to Dictionary type", "/ 是否使用枚举类型的描述": "/ Whether to use the description of the enumeration type", "添加空白项": "Add blank items", "COCONET .net core不支持FileSystemRights,需要继续改进": "COCONET .net core does not support FileSystemRights and needs to continue to be improved.", "/ MD5 加密": "/MD5 encryption", "/ 获得 NCF 系统内全局一致的加盐的 MD5 加密结果": "/ Obtain globally consistent salted MD5 encryption results within the NCF system", "/ 原始密码": "/ Original password", "/ 盐": "/ Salt", "/ 默认为 UTF8": "/ Default is UTF8", "COCONET .net core不支持System.Management,需要继续改进": "COCONET .net core does not support System.Management and needs to continue to be improved.", "参考:https://www.cnblogs.com/yuangang/archive/2016/08/08/5743660.html": "Reference: https://www.cnblogs.com/yuangang/archive/2016/08/08/5743660.html", "/ 目录分隔符:Windows下“\\”,Mac OS和Linux下“\\”": "/ Directory separator: \"\\\" under Windows, \"\\\" under Mac OS and Linux", "/ 包含引用程序的目录绝对路径": "/ Absolute path to the directory containing the referencing program", "/ 获取文件绝对路径": "/ Get the absolute path of the file", "/ 文件路径": "/ File path", "/ 是否是绝对路径": "/ Is it an absolute path?", "/ windows下判断 路径是否包含 \":\"": "/ Determine whether the path contains \":\" under windows", "/ Mac OS、Linux下判断 路径是否包含 \"\\\"": "/ Determine whether the path contains \"\\\" under Mac OS and Linux", "/ 路径": "/ Path", "/ 自动配置 ConfigurationMapping 特性": "/ Automatically configure the ConfigurationMapping attribute", "/ 注意:添加此属性后,ConfigurationMapping 中的配置会被优先注入到 SenparcEntities 系统对象,

": "/ Note: After adding this attribute, the configuration in ConfigurationMapping will be injected into the SenparcEntities system object first,

", "/ 否则,当某实体没有创建 ConfigurationMapping 时,会将其默认属性注入到 SenparcEntities 系统对象。
": "/ Otherwise, when no ConfigurationMapping is created for an entity, its default properties will be injected into the SenparcEntities system object.
", "/ Xncf 模块特性 - 扩展方法": "/Xncf module attributes - extension methods", "/ Xncf 模块执行顺序,Order 数字越大,执行越靠前,如果非系统关键模块,尽量靠后": "/ Xncf module execution order. The larger the Order number, the earlier the execution. If it is not a system-critical module, try to go as far back as possible.", "/ 加载顺序,数字越大加载顺序越靠前。请严格按照参考数值:0:普通(默认),1-5000:需要预加载的重要模块,>5000:系统及基础模块": "/ Loading order, the larger the number, the higher the loading order. Please strictly follow the reference values: 0: normal (default), 1-5000: important modules that need to be preloaded, >5000: system and basic modules", "/ 加载顺序,数字越大加载顺序越靠前。请严格按照参考数值:0:普通(默认),1-5000:需要预加载的重要模块,>5000:系统及基础模块": "/ Loading order, the larger the number, the higher the loading order. Please strictly follow the reference values: 0: normal (default), 1-5000: important modules that need to be preloaded, >5000: system and basic modules", "/ Xncf 模块特性 - 注册": "/Xncf Module Properties - Register", "/ Xncf 模块使用的 AutoMap 配置": "/AutoMap configuration used by the Xncf module", "/ 提供给数据库 Migration 使用的 DesignTimeDbContextFactory": "/ DesignTimeDbContextFactory provided to database Migration", "获取 XncfDatabase 对象": "Get the XncfDatabase object", "获取当前适用的 DbContext 类型": "Get the currently applicable DbContext type", "判断构造函数参数个数": "Determine the number of constructor parameters", "添加第二个参数(目前只为系统基础对象,如 SystemServiceEntities 使用)": "Add a second parameter (currently only used by system base objects such as SystemServiceEntities)", "获取 XncfSenparcEntities 实例": "Get XncfSenparcEntities instance", "/ (针对非 XNCF 模块的普通 DbContext,在Senparc.Web 项目下进行 Add-Migration 等操作)": "/ (For ordinary DbContext of non-XNCF modules, perform Add-Migration and other operations under the Senparc.Web project)", "/ 数据库 连接字符串": "/database connection string", "/ 返回 DbContext 实例": "/ Returns the DbContext instance", "/ 指定程序集等配置,如:": "/ Specify assembly and other configurations, such as:", "/ 注意:如果重写,最后一定要执行 base.DbContextOptionsAction() ": "/ Note: If you rewrite, you must execute base.DbContextOptionsAction() at the end ", "/// 特殊类型,例如 SystemServiceEntities": "/// Special types, such as SystemServiceEntities", "/ SenparcDesignTimeDbContextFactoryBase 构造函数": "/ SenparcDesignTimeDbContextFactoryBase constructor", "/ NCF 版本号": "/ NCF version number", "/ 将要设置的CO2NET.Config.RootDirectoryPath,一般为 Senparc.Web 或具有 App_Data/Database/SenparcConfig.config 配置文件的目录": "/ CO2NET.Config.RootDirectoryPath to be set, usually Senparc.Web or the directory with the App_Data/Database/SenparcConfig.config configuration file", "/ 数据库名称,默认为 Local,即 Senparc.Web/appsettings.json 中的 DatabaseName": "/ Database name, default is Local, that is, DatabaseName in Senparc.Web/appsettings.json", "/ 在日志中输出额外信息": "/ Output additional information in the log", "注释可能出现中文,对中文环境可以配置使用 GB2312": "Comments may appear in Chinese, and the Chinese environment can be configured to use GB2312", "/ 创建过程的其他代码": "/ Other code for the creation process", "获取数据库配置": "Get database configuration", "TODO:env 参数从 v0.11 开始使用,需要进一步测试 —— Jeffrey 2021.12.15": "TODO:env parameter is used starting from v0.11 and needs further testing - Jeffrey 2021.12.15", "使用内存缓存": "Use memory cache", "修复 https://github.com/NeuCharFramework/NCF/issues/13 发现的问题(在非Web环境下无法得到网站根目录路径)": "Fix the problem found in https://github.com/NeuCharFramework/NCF/issues/13 (the website root directory path cannot be obtained in non-Web environment)", "如果运行 Add-Migration 命令,并且获取不到正确的网站根目录,此处可能无法自动获取到连接字符串(上述#13问题),": "If you run the Add-Migration command and cannot obtain the correct website root directory, the connection string may not be automatically obtained here (issue #13 above),", "也可通过下面已经注释的的提供默认值方式解决(不推荐)": "It can also be solved by providing default values ​​as commented below (not recommended)", "创建 DbContextOptionsBuilder 对象": "Create a DbContextOptionsBuilder object", "注意:这里不能用 this.DbContextOptionsAction,否则子类重写将无效!": "Note: this.DbContextOptionsAction cannot be used here, otherwise subclass rewriting will be invalid!", "单一使用 SQL Server 的方法:builder.UseSqlServer(sqlConnection, DbContextOptionsAction);//beta6": "Single method of using SQL Server: builder.UseSqlServer(sqlConnection, DbContextOptionsAction);//beta6", "/ IXncfDatabase 使用的 DbContext 基类": "/DbContext base class used by IXncfDatabase", "/ 当前 IXncfDatabase 注册类实例": "/ Current IXncfDatabase registration class instance", "/ 安装过程中使用的 Migrate 系列操作": "/ Migrate series of operations used during installation", "/ 实现了数据库接口的 Register,databaseRegister.TryGetXncfDatabaseDbContextType 必须返回 XncfDatabaseDbContext 的子类": "/ Register that implements the database interface, databaseRegister.TryGetXncfDatabaseDbContextType must return a subclass of XncfDatabaseDbContext", "/ 是否需要对 dbContextType 类型进行检查": "/ Whether the dbContextType type needs to be checked", "更新数据库": "Update database", "重置合并状态": "Reset merge status", "进行合并": "merge", "/ 版本更新类型": "/ version update type", "/ Xncf 模块方法执行异常": "/Xncf module method execution exception", "/ FunctionAppRequest 基类": "/ FunctionAppRequest base class", "/ 记录日志": "/log", "/ FunctionRender 集合。Key:唯一标识,value:MethodInfo": "/FunctionRender collection. Key: unique identifier, value: MethodInfo", "/ 获取 单个 Group 的 Key": "/ Get the Key of a single Group", "/ 设置 FunctionRenderBag": "/Set FunctionRenderBag", "/ 获取指定注册类型下的 FunctionRender 集合。": "/ Get the FunctionRender collection under the specified registration type.", "/ 通过模块 UID 获取 FunctionRender 集合。": "/ Get the FunctionRender collection by module UID.", "/ 当输入包含 [#sym:FunctionRender] 时,将指定模块中的 FunctionRender 方法自动转换为可导入的插件对象。": "/ When the input contains [#sym:FunctionRender], automatically convert the FunctionRender method in the specified module into an importable plug-in object.", "/ 枚举类型": "/ enum type", "/ Function 帮助类": "/Function helper class", "/ 获取所有参数的信息列表": "/ Get the information list of all parameters", "/ 是否尝试载入数据(参数必须实现 IFunctionParameterLoadDataBase 接口)": "/ Whether to try to load data (the parameter must implement the IFunctionParameterLoadDataBase interface)", "包含不带参数的构造函数": "Contains a constructor with no parameters", "TODO:通过 DI 生成": "TODO: generated through DI", "载入原有信息": "Load original information", "默认为文本内容": "Defaults to text content", "判断是否存在选项": "Determine if option exists", "分割:名称||说明": "Split: Name||Description", "/ 给 SelectionList 对象添加当前解决方案中的 XNCF 项目": "/ Add the XNCF items in the current solution to the SelectionList object", "/ (扫描当前解决方案包含的所有领域项目)": "/ (Scan all domain projects contained in the current solution)", "/ 当前解决方案是否必须包含 XNCF 项目": "/ Whether the current solution must contain the XNCF project", "/ 查找 XNCF 模块根目录,如果留 null,则使用 System.IO.Directory.GetCurrentDirectory()获取,并且查找 .sln 所在文件夹": "/ Find the XNCF module root directory. If left null, use System.IO.Directory.GetCurrentDirectory() to obtain it, and find the folder where the .sln is located", "/ 除了标准的 XNCF 以外额外的项目": "/ Additional projects besides standard XNCF", "找到了 SLN 文件,开始展开地毯式搜索": "Found the SLN file and started searching", "第一步:查找 XNCF": "Step 1: Find XNCF", "第二步:查看 Register 文件是否存在": "Step 2: Check whether the Register file exists", "不存在则跳过": "If it does not exist, skip it.", "第三步:检查 Register 文件是否合格": "Step 3: Check whether the Register file is qualified", "/ 获取 xxxSenparcEntities.cs 数据库文件": "/ Get xxxSenparcEntities.cs database file", "/ 数据库类型": "/ Database type", "/ 参数类型": "/ parameter type", "/ 注意:请勿更改已经定义的顺序和值!": "/ Note: Do not change the defined order and values! ", "/ FunctionParameter 信息(供输出用)": "/FunctionParameter information (for output)", "/ 参数名称": "/ parameter name", "/ 标题(标签内容)": "/ title (tag content)", "/ 备注": "/ Remark", "/ 是否必须": "/ is it necessary", "/ 系统类型": "/ system type", "/ 最大长度(一般应用于字符串)": "/ Maximum length (generally applied to strings)", "/ 文本值(当文本类型时使用)": "/ Text value (used when text type)", "/ 选项(当出现非文本内容时使用)": "/ option (used when non-text content is present)", "/ 选项列表": "/option list", "/ 选项类型": "/ option type", "/// 选中的项的值(从客户端传入)": "/// The value of the selected item (passed in from the client)", "/ 选项参数": "/ option parameters", "/ 判断 SelectedValues 中是否存在值": "/ Determine whether there is a value in SelectedValues", "/ 获取 Items 中的索引项对应的 Value": "/ Get the Value corresponding to the index item in Items", "/ 选项": "/options", "/ 文字标签": "/ text label", "/ 值": "/ value", "/ (仅供显示时使用),是否默认选中": "/ (for display only), whether selected by default", "/ 说明": "/ illustrate", "/ 选项集合类型": "/ option collection type", "/ 未知参数": "/ unknown parameters", "/ 下拉列表(单选)": "/ drop-down list (single selection)", "/ 复选框列表(多选)": "/ Checkbox list (multiple selection)", "/// 单选列表(单选)": "/// Single choice list (single choice)", "/ 获取模块版本号": "/ Get module version number", "/ 中间件接口": "/ middleware interface", "/ 使用中间件": "/ use middleware", "/ 需要使用 AddRazorRuntimeCompilation() 方法时,需要设置对应当前项目相对于 Senparc.Web 的路径": "/ When you need to use the AddRazorRuntimeCompilation() method, you need to set the path corresponding to the current project relative to Senparc.Web", "/ 相对路径,如:Path.GetFullPath(Path.Combine(SiteConfig.WebRootPath, \"..\", \"..\", \"Senparc.Areas.Admin\"));": "/ Relative path, such as: Path.GetFullPath(Path.Combine(SiteConfig.WebRootPath, \"..\", \"..\", \"Senparc.Areas.Admin\"));", "/ 是否忽略安装(但不影响执行注册代码)": "/ Whether to ignore installation (but does not affect the execution of registration code)", "/ 是否启用 MCP 服务器(MCP Server)": "/ Whether to enable MCP server (MCP Server)", "/ 模块名称,要求全局唯一": "/Module name, required to be globally unique", "/ 编号,要求全局唯一": "/ Number, required to be globally unique", "/ 版本号": "/ version number", "/ 菜单名称": "/menu name", "/ Icon图标": "/icon", "/// 注册方法,注册的顺序决定了界面中排列的顺序": "/// Registration method, the order of registration determines the order of arrangement in the interface", "/ 添加 AutoMap 映射": "/Add AutoMap mapping", "/ 获取当前模块的已注册线程信息": "/ Get the registered thread information of the current module", "/ 安装代码": "/Installation code", "/ 卸载代码": "/uninstall code", "/ 获取首页Url": "/ Get homepage Url", "/ 仅限实现了 IAreaRegister 接口之后的 Register,否则将返回 null": "/ Only Register after implementing the IAreaRegister interface, otherwise null will be returned", "/ 获取 Area 其他页面的 URL": "/ Get the URLs of other pages in Area", "/ URL 路径(不带 uid 参数)": "/ URL path (without uid parameter)", "/ 在 ConfigureServices 启动时注册当前模块": "/ Register the current module when ConfigureServices starts", "/ 添加AutoMap的映射关系": "/ Add AutoMap mapping relationship", "/ 执行 AutoMapper 映射": "/Execute AutoMapper mapping", "/ 在 startup.cs 的 Configure() 方法中执行配置": "/ Perform configuration in the Configure() method of startup.cs", "/ CO2NET 注册对象": "/ CO2NET registration object", "/ 添加 MCP 服务器": "/Add MCP server", "/ 使用 MCP 服务器": "/ use MCP server", "/ XNCF 线程模块接口": "/XNCF thread module interface", "/ 线程配置": "/thread configuration", "/ 用于识别 Thread,请确保单个 XNCF 模块中唯一": "/ is used to identify Thread, please ensure it is unique in a single XNCF module", "/ 间隔时间": "/interval time", "/ 执行任务": "/ perform tasks", "/ 发生异常时的处理": "/ Handling when an exception occurs", "/ 最后故事记录": "/Last story record", "/ 获取故事 HTML代码": "/ Get story HTML code", "/ 记录故事": "/record story", "TODO: 线程备份及后台操作需要考虑租户问题": "TODO: Thread backup and background operations need to consider tenant issues", "/ XNCF Thread 模块,线程配置": "/XNCF Thread module, thread configuration", "return;//TODO:多租户完成之前暂时不启用后台线程,需要解决线程和租户的对应关系": "return;//TODO: Background threads will not be enabled until multi-tenancy is completed. The corresponding relationship between threads and tenants needs to be resolved.", "遍历单个 XNCF 内所有线程配置": "Traverse all thread configurations within a single XNCF", "定义线程": "Define thread", "建议开发者自己在内部做好线程内的异常处理": "It is recommended that developers handle exceptions within threads internally", "进行延迟": "delay", "启动": "start up", "1、请为我编写一个能够识别软件版本号的正则表达式": "1. Please write me a regular expression that can identify the software version number.", "2、创建一个独立的类:VersionInfo,用于储存版本信息": "2. Create an independent class: VersionInfo, used to store version information", "3、为这个正则表达式编写一个尽量涵盖各种版本情况的完整的单元测试(使用 .NET 自带的单元测试)。": "3. Write a complete unit test for this regular expression that covers various versions as much as possible (use the unit test that comes with .NET).", "这个正则表达式匹配遵循语义化版本规范(semver)的版本号。它包括主版本号、次版本号、修订号、可选的构建号、可选的预发布版本标签和可选的元数据标签。": "This regular expression matches version numbers that follow the semantic versioning specification (semver). It includes a major version number, a minor version number, a revision number, an optional build number, an optional pre-release tag, and an optional metadata tag.", "/ 解析版本字符串并返回一个 VersionInfo 对象。": "/ Parses the version string and returns a VersionInfo object.", "/ 要解析的版本字符串。": "/ The version string to parse. ", "/ 表示解析后的版本信息的 VersionInfo 对象。": "/ A VersionInfo object representing the parsed version information. ", "如果不存在 Patch,则使用默认值 0": "If no patch exists, the default value 0 is used", "/ 从 Register.cs 代码中获取版本号": "/ Get the version number from the Register.cs code", "/ 替换版本号": "/ Replace version number", "/ 原始版本定位字符串,如:Version => \"0.1.1\"": "/ Original version positioning string, such as: Version => \"0.1.1\"", "使用 Roslyn 解析 C# 代码": "Parse C# code with Roslyn", "查找 Version 属性所在的节点": "Find the node where the Version property is located", "获取旧的版本号": "Get old version number", "如果找到了 Version 属性,修改其值": "If the Version property is found, modify its value", "将修改后的语法树规范化并转换为字符串,并写回到原始的 .cs 文件中": "Normalize and convert the modified syntax tree to a string and write it back to the original .cs file", "/ 软件版本信息": "/Software version information", "/ 主版本": "/major version", "/ 次版本": "/minor version", "/ 修订版本": "/revision", "/ 可选的构建版本": "/ optional build version", "/ 可选的预发布版本标签": "/ optional pre-release tag", "/ 可选的元数据标签": "/ Optional metadata tag", "/ 将 VersionInfo 对象转换为版本字符串。": "/ Convert a VersionInfo object to a version string.", "/ 表示版本信息的字符串。": "/A string representing version information. ", "如果存在 Build 属性,则将其添加到版本字符串中": "If the Build attribute is present, add it to the version string", "/ XncfRegister 管理器": "/XncfRegister Manager", "/ 模块和方法集合。模块已经按照 Order 倒叙排序(优先级从高到低)": "/ Collection of modules and methods. Modules have been sorted backwards according to Order (priority from high to low)", "/ 带有数据库的模块 TODO:可放置到缓存中 / TODO:可移除": "/ Module with database TODO: can be placed in cache / TODO: can be removed", "/ 判断指定名称的模块是否已注册": "/ Determine whether the module with the specified name has been registered", "/ 判断指定名称的模块是否已注册(推荐)": "/ Determine whether the module with the specified name has been registered (recommended)", "/ 检查数据库(缓存)中的模块信息,是否开放,并为开放状态": "/ Check the module information in the database (cache) to see if it is open and in the open state", "检查数据库中的安装状态": "Check installation status in database", "/ 检查 XNCF 模块是否可用(推荐)": "/ Check if XNCF module is available (recommended)", "检查内存中是否存在": "Check if it exists in memory", "/ 检查 XNCF 模块是否可用": "/ Check if XNCF module is available", "dialog for 导入 NeuCharAI 云端模型算力": "dialog for importing NeuCharAI cloud model computing power", "统计数据": "Statistics", "左侧统计数据 2x2 布局": "Left statistics 2x2 layout", "第一行": "first line", "DOT REMOVE OR MODIFY THIS LINE 请勿移除或修改本行 - Entities Point": "DOT REMOVE OR MODIFY THIS LINE Do not remove or modify this LINE - Entities Point", "如无特殊需需要,OnModelCreating 方法可以不用写,已经在 Register 中要求注册": "If there is no special need, the OnModelCreating method does not need to be written. Registration is already required in Register.", "/ LlmModel 数据库实体": "/LlmModel database entity", "必须添加前缀,防止全系统中发生冲突": "The prefix must be added to prevent conflicts system-wide.", "/ 代号": "/ code name", "/ 模型名称(必须)": "/Model name (required)", "/ 部署名称": "/deployment name", "/ Endpoint(可选)": "/Endpoint (optional)", "/ 模型的类型(必须), 例如:NeuCharAI, OpenAI, Azure OpenAI, HuggingFace": "/ Type of model (required), for example: NeuCharAI, OpenAI, Azure OpenAI, HuggingFace", "/ 模型类别": "/ model category", "/ OrganizationId(可选)": "/OrganizationId (optional)", "/ ApiKey(可选)": "/ApiKey (optional)", "/ ApiVersion(可选)": "/ApiVersion (optional)", "/ Note(可选)": "/Note (optional)", "/ MaxToken(可选)": "/MaxToken (optional)", "/ 是否共享": "/ Whether to share", "/ 是否展示": "/ Whether to display", "/ 切换模型可见状态": "/ Switch model visible state", "/ 更新模型": "/update model", "/ 向量数据库实体": "/Vector database entity", "/ 向量数据库名称(必须)": "/ Vector database name (required)", "/ 名称": "/name", "/ 连接字符串": "/ connection string", "/ 向量数据库的类型(必须), 例如:Memory, HardDisk, Redis, Mulivs, Chroma, PostgreSQL, Sqlite, SqlServer, Default": "/ Type of vector database (required), for example: Memory, HardDisk, Redis, Mulivs, Chroma, PostgreSQL, Sqlite, SqlServer, Default", "/ 主键 ID": "/ primary key ID", "/ Endpoint(必须)": "/Endpoint (required)", "/ 模型类型": "/ model type", "/ 和 Senaprc.AI 中的 ConfigModel 匹配(包括值),是其子集": "/ matches (including the value) the ConfigModel in Senaprc.AI and is a subset of it", "/ NeuChar 平台对应当前用户支持的模型参数": "/ NeuChar platform corresponds to the model parameters supported by the current user", "/ 模型名称": "/ model name", "/ 模型权重参考(以text-davinci-003为基准)": "/ Model weight reference (based on text-davinci-003)", "/ 是否激活": "/ Whether to activate", "/// 此属性是本地新增": "/// This attribute is newly added locally", "/ 设计时 DbContext 创建(仅在开发时创建 Code-First 的数据库 Migration 使用,在生产环境不会执行)": "/ Design-time DbContext creation (Code-First database migration is only used during development and will not be executed in the production environment)", "/ 1、切换至 Debug 模式": "/ 1. Switch to Debug mode", "/ 2、运行:PM> add-migration [更新名称] -c AIKernelSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ": "/ 2. Run: PM> add-migration [update name] -c AIKernelSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ", "指定其他数据库": "Specify another database", "/ 2、运行:PM> add-migration [更新名称] -c AIKernelSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ": "/ 2. Run: PM> add-migration [update name] -c AIKernelSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ", "/ 2、运行:PM> add-migration [更新名称] -c AIKernelSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ": "/ 2. Run: PM> add-migration [update name] -c AIKernelSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ", "/ 2、运行:PM> add-migration [更新名称] -c AIKernelSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ": "/ 2. Run: PM> add-migration [update name] -c AIKernelSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ", "/ 2、运行:PM> add-migration [更新名称] -c AIKernelSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ": "/ 2. Run: PM> add-migration [update name] -c AIKernelSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ", "/ 2、运行:PM> add-migration [更新名称] -c AIKernelSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ": "/ 2. Run: PM> add-migration [update name] -c AIKernelSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ", "/ SenparcDbContextFactory 的公共配置": "/Public configuration for SenparcDbContextFactory", "/ 用于寻找 App_Data 文件夹,从而找到数据库连接字符串配置信息": "/ is used to find the App_Data folder to find the database connection string configuration information", "项目根目录": "Project root directory", "找到 Web目录,以获取统一的数据库连接字符串配置": "Locate the web directory for unified database connection string configuration", "优先使用Web统一配置": "Prefer using Web unified configuration", "/ 构造 SenparcAiSetting": "/ Construct SenparcAiSetting", "SK中实际上没有用ApiVersion": "ApiVersion is not actually used in SK", "/ 运行模型": "/run model", "TODO: 远程不提供,临时本地判断": "TODO: Not provided remotely, temporary local judgment", "/// 构造 SenparcAiSetting": "/// Construct SenparcAiSetting", "throw new Exception($\"尚未支持:{aiModel.ConfigModelType} 模型在 BuildSenparcAiSetting 中的处理\");": "throw new Exception($\"Not yet supported: {aiModel.ConfigModelType} model processing in BuildSenparcAiSetting\");", "/// 运行模型": "/// Run the model", "throw new SenparcAiException(\"SenparcAiSetting 不能为空\");": "throw new SenparcAiException(\"SenparcAiSetting cannot be empty\");", "return \"模型数据不存在,请检查是否已部署,或是否具备权限!\";": "return \"The model data does not exist, please check whether it has been deployed or whether you have permission!\";", "Note = $\"从 NeuChar AI 导入(DevId:{developerId})\",": "Note = $\"Import from NeuChar AI (DevId:{developerId})\",", "//TODO: 远程不提供,临时本地判断": "//TODO: Not provided remotely, temporary local judgment", "return $\"已成功添加 {addCount} 个模型,更新 {updateCount} 个模型信息。\";": "return $\"{addCount} models have been added successfully and {updateCount} model information has been updated.\";", "if (color == null)//如果是纯第一次安装,理论上不会有残留数据": "if (color == null)//If it is a pure first installation, theoretically there will be no residual data", "//创建默认颜色": "//Create default color", "//TODO:异步方法需要添加排序功能": "//TODO: The asynchronous method needs to add sorting function", "//TODO: 更多业务方法可以写到这里": "//TODO: More business methods can be written here", "TODO: 需要权限验证": "TODO: Permission verification required", "/ 分页获取AIModel": "/Paging to get AIModel", "/ 获取AIModel列表": "/ Get AIModel list", "/ 新建一个AIModel": "/ Create a new AIModel", "//response.ErrorMessage = \"AIModel已存在\";": "//response.ErrorMessage = \"AIModel already exists\";", "throw new NcfExceptionBase(\"AIModel已存在\");": "throw new NcfExceptionBase(\"AIModel already exists\");", "/ 修改AIModel": "/ Modify AIModel", "/ 删除AIModel": "/ Delete AIModel", "TODO: 使用日志下载提供详细教程": "TODO: Provide detailed tutorial on using log download", "//TODO: 集成到 ApiContainer": "//TODO: Integrate into ApiContainer", "TODO: 核验 AppKey 是否正确": "TODO: Verify whether the AppKey is correct", "TODO:立即使用一个模型做一个测试": "TODO: Use a model to do a test immediately", "[FunctionRender(\"测试 AI 模型\", \"测试已经设置的模型\", typeof(Register))]": "[FunctionRender(\"Test AI model\", \"Test the set model\", typeof(Register))]", "await request.LoadData(ServiceProvider);//加载数据": "await request.LoadData(ServiceProvider);//Load data", "throw new SenparcAiException(\"请至少选择一个模型!\");": "throw new SenparcAiException(\"Please select at least one model!\");", "msg.AppendLine($\"正在测试模型:{selectedItem.Value}\");": "msg.AppendLine($\"Testing model: {selectedItem.Value}\");", "msg.AppendLine($\"模型测试成功,返回信息:{aiResult.Output}\");": "msg.AppendLine($\"Model test successful, return information: {aiResult.Output}\");", "msg.AppendLine($\"模型测试失败:{ex.Message}\");": "msg.AppendLine($\"Model test failed: {ex.Message}\");", "logger.Append($\"测试完成,返回信息:\\r\\n{msg.ToString()}\");": "logger.Append($\"Test completed, return information:\\r\\n{msg.ToString()}\");", "/ 分页获取AIVector": "/ Get AIVector in paging", "/ 获取AIVector列表": "/ Get AIVector list", "/ 新建一个AIVector": "/ Create a new AIVector", "//response.ErrorMessage = \"AIVector已存在\";": "//response.ErrorMessage = \"AIVector already exists\";", "throw new NcfExceptionBase(\"AIVector已存在\");": "throw new NcfExceptionBase(\"AIVector already exists\");", "/ 修改AIVector": "/ Modify AIVector", "/// 获取或初始化一个 ColorDto 对象": "/// Get or initialize a ColorDto object", "var dt1 = SystemTime.Now;//开始计时": "var dt1 = SystemTime.Now;//Start timing", "var colorDto = await _colorService.GetOrInitColor();//获取或初始化颜色参数": "var colorDto = await _colorService.GetOrInitColor();//Get or initialize color parameters", "var costMs = SystemTime.DiffTotalMS(dt1);//记录耗时": "var costMs = SystemTime.DiffTotalMS(dt1);//Record time consumption", "下拉列表": "drop down list", "TODO: 更多 AI 参数": "TODO: More AI parameters", "从 appsettings.json 读取": "Read from appsettings.json", "从数据库中获取模型": "Get model from database", "集成模型参数": "Integrated model parameters", "/ 总数": "/Total", "/ 数据": "/ data", "/ 页码": "/page number", "/ 每页大小": "/ size per page", "/ 排序": "/ sort", "/ 端点": "/endpoint", "/ AI平台": "/ AI platform", "/ 组织ID": "/Organization ID", "/ 是否显示": "/ Whether to display", "/ API密钥": "/API key", "/ API版本": "/API version", "/ 最大令牌": "/max tokens", "/ 向量数据库类型": "/Vector database type", "/// 颜色码,0-255": "/// Color code, 0-255", "/// 花费时间": "/// Spend time", "多选框": "checkbox", "注册 XNCF 页面接口(按需选用)": "Register XNCF page interface (optional on demand)", "赋能 RazorPage 运行时编译": "Enable RazorPage runtime compilation", "new AreaPageMenuItem(GetAreaUrl($\"/Admin/AIKernel/DatabaseSample\"),\"数据库操作示例\",\"fa fa-bookmark-o\")": "new AreaPageMenuItem(GetAreaUrl($\"/Admin/AIKernel/DatabaseSample\"),\"Database Operation Example\",\"fa fa-bookmark-o\")", "此处可配置页面权限": "Page permissions can be configured here", "注册 XNCF 模块数据库(按需选用)": "Register the XNCF module database (optional)", "/ 数据库前缀": "/database prefix", "/ 动态获取数据库上下文": "/ Dynamically obtain database context", "实现 [XncfAutoConfigurationMapping] 特性之后,可以自动执行,无需手动添加": "After implementing the [XncfAutoConfigurationMapping] feature, it can be executed automatically without adding it manually.", "必须确保全局唯一,生成后必须固定,已自动生成,也可自行修改": "It must be globally unique and must be fixed after generation. It has been automatically generated and can also be modified by yourself.", "必须填写版本号": "Version number is required", "安装或升级版本时更新数据库": "Update database when installing or upgrading a version", "根据安装或更新不同条件执行逻辑": "Execute logic based on different conditions for installation or update", "新安装": "New installation", "TODO: 自动拉取 NeuChar 免费用量进行配置和载入 Seed 数据": "TODO: Automatically pull NeuChar free usage for configuration and load Seed data", "更新": "renew", "指定需要删除的数据实体": "Specify the data entity to be deleted", "注意:这里作为演示,在卸载模块的时候删除了所有本模块创建的表,实际操作过程中,请谨慎操作,并且按照删除顺序对实体进行排序!": "Note: As a demonstration, all tables created by this module are deleted when uninstalling the module. During actual operation, please operate with caution and sort the entities in the order of deletion!", "新增的对话框可见性": "New dialog visibility", "新增的表单数据": "New form data", "新增的验证规则": "New validation rules", "显示对话框": "show dialog", "把结果复制到剪切板": "Copy results to clipboard", "提示时展示'******'+key的后4位": "Display '******' + the last 4 digits of key when prompted.", "新增的清理表单方法": "New method to clean up the form", "添加动画控制变量": "Add animation control variables", "添加鼠标事件监听": "Add mouse event listener", "----------------------------------下为chart1--------------------------------------": "----------------------------------The following is chart1-------------------------------------------------", "添加区域填充颜色": "Add area fill color", "----------------------------------下为chart2--------------------------------------": "----------------------------------The following is chart2--------------------------------------------------", "----------------------------------下为chart3--------------------------------------": "----------------------------------The following is chart3--------------------------------------------------", "----------------------------------下为chart4--------------------------------------": "----------------------------------The following is chart4-------------------------------------------------", "----------------------------------下为chart5--------------------------------------": "----------------------------------The following is chart5--------------------------------------------------", "----------------------------------下为chart6--------------------------------------": "----------------------------------The following is chart6--------------------------------------------------", "XNCF 统计状态": "XNCF statistics status", "开放模块数据": "Open module data", "点击打开模块": "Click to open module", "添加新方法处理悬停效果": "Add new method to handle hover effects", "获取统计项元素 - 修正选择器": "Get statistic element - fix selector", "已安装模块统计项的鼠标事件": "Mouse events for installed module statistics", "待更新模块统计项的鼠标事件": "Mouse events for module statistical items to be updated", "触发抖动动画": "Trigger jitter animation", "添加随机延迟": "add random delay", "0-200ms的随机延迟": "0-200ms random delay", "动画结束后移除类": "Remove class after animation ends", "与动画持续时间匹配": "Match animation duration", "触发发光/淡化动画": "Trigger glow/fade animation", "为所有可更新的模块添加发光效果": "Added glow effect to all updatable modules", "为不可更新的模块添加淡化效果": "Add a fade effect to non-updatable modules", "记录到聊天记录": "Record to chat history", "TODO: serviceProvider 是 null": "TODO: serviceProvider is null", "检查 PromptCode 是否存在": "Check if PromptCode exists", "Prompt Code不存在的时候,会抛出异常": "When the Prompt Code does not exist, an exception will be thrown.", "/ 获取 AgentTemplate 的列表": "/ Get the list of AgentTemplate", "/ 获取 PromptRange 的树状结构": "/ Get the tree structure of PromptRange", "/ 创建或更新 AgentTemplate": "/Create or update AgentTemplate", "/ 获取 AgentTemplate 的详情": "/ Get details of AgentTemplate", "/ 获取带状态的 AgentTemplate 的详情": "/ Get the details of the stateful AgentTemplate", "/ 启用或者停用 AgentTemplate": "/ Enable or disable AgentTemplate", "/ 是否启用": "/ Whether to enable", "/ 获取所有已注册的 AI Plugin 类型": "/ Get all registered AI Plugin types", "/ 测试MCP连接": "/Test MCP connection", "/ Endpoint名称": "/ Endpoint name", "/ 包含工具列表和连接状态的响应": "/ Response containing tool list and connection status", "创建一个假工具以显示错误信息": "Create a fake tool to display error messages", "/ MCP连接测试结果": "/MCP connection test results", "/ 连接是否成功": "/ Whether the connection is successful", "/ HTTP状态码": "/HTTP status code", "/ 状态消息": "/status message", "/ 工具列表": "/ Tool list", "/ MCP工具信息": "/MCP tool information", "/ 工具名称": "/ tool name", "/ 工具描述": "/ tool description", "/ 工具参数列表": "/ Tool parameter list", "/ MCP工具参数": "/MCP tool parameters", "/ 参数描述": "/ Parameter description", "群主": "Group owner", "对接人": "docking person", "TODO:封装到 Service 中": "TODO: Encapsulated into Service", "新建": "New", "添加成员": "Add member", "合并“对接人”为成员": "Merge \"connectors\" as members", "/ 创建或设置 ChatGroup": "/ Create or set up a ChatGroup", "/ ChatGroup 信息>": "/ ChatGroup information>", "/ 成员 AgentTemplate ID": "/ Member AgentTemplate ID", "已存在的不添加": "Do not add existing ones", "删除不在范围内的成员": "Remove members that are not in scope", "增加模糊搜索组": "Add fuzzy search group", "/ 运行智能体": "/run agent", "TODO: 使用线程进行维护": "TODO: Use threads for maintenance", "/ 删除整个对话(包括所有消息、任务等)": "/ Delete the entire conversation (including all messages, tasks, etc.)", "检查是否选择了对话": "Check if the conversation is selected", "检查是否确认删除": "Check if deletion is confirmed", "获取对话信息": "Get conversation information", "1. 删除所有关联的消息和任务": "1. Delete all associated messages and tasks", "先获取所有相关的 ChatTask": "First get all related ChatTasks", "删除这些 ChatTask 下的所有 ChatGroupHistory": "Delete all ChatGroupHistory under these ChatTasks", "2. 删除所有 ChatTask": "2. Delete all ChatTasks", "3. 删除所有对话成员 (ChatGroupMember)": "3. Delete all chat members (ChatGroupMember)", "4. 最后删除对话本身 (ChatGroup)": "4. Finally delete the conversation itself (ChatGroup)", "生成删除摘要": "Generate deletion summary", "增加模糊搜索任务": "Add fuzzy search tasks", "/ Prompt 优化 AppService": "/Prompt Optimize AppService", "/ TODO: 需要权限验证": "/ TODO: Permission verification required", "/ 确保 PromptCatalyzer Agent 和相关资源已初始化": "/ Ensure that the PromptCatalyzer Agent and related resources are initialized", "/ 优化指定的 Prompt(包括内容和参数如 Temperature)": "/ Optimize the specified Prompt (including content and parameters such as Temperature)", "验证请求参数": "Verify request parameters", "调用优化服务": "Call optimization service", "/ Prompt 优化请求 DTO": "/Prompt optimization request DTO", "/ Prompt 的版本号(如 \"2024.1.1.1-T1-A1\")": "/ Prompt version number (such as \"2024.1.1.1-T1-A1\")", "/ Prompt 的内容": "/Prompt content", "/ 用户的优化需求描述": "/Description of user optimization needs", "/ 优化上下文(当前 Prompt 的参数)": "/ Optimization context (parameters of the current Prompt)", "TODO:可以选择多个通道": "TODO: Multiple channels can be selected", "HootRobotType 枚举": "HootRobotType enumeration", "new SelectionItem(\"Default\",\"系统默认\",\"通过系统默认配置的固定 AI 模型信息\",true)": "new SelectionItem(\"Default\",\"System Default\",\"Fixed AI model information configured by system default\",true)", "载入 AI 模型": "Load AI model", "/ 任务名称": "/ task name", "/ 如果是 0 ,则使用系统默认配置": "/ If 0, use system default configuration", "/ 发起对话的要求": "/ Request to initiate a conversation", "/ 使用个性化智能体": "/ Use personalized agents", "/ 消息平台": "/message platform", "/ 消息平台参数": "/Message platform parameters", "/ 最大对话轮数": "/Maximum number of dialogue rounds", "/ 可选:业务关联 ID(例如 Prompt 优化的 RequestId),用于在执行上下文中关联工具调用": "/ Optional: Business correlation ID (e.g. Prompt-optimized RequestId), used to correlate tool calls in the execution context", "/ 删除对话请求": "/ Delete conversation request", "加载所有可用的 ChatGroup": "Load all available ChatGroups", "/ Prompt 优化唯一执行路径:运行 ChatGroup 直至 Agent 对话结束,再由 Plugin 写入结果并发布响应事件": "/ Prompt optimizes the only execution path: run the ChatGroup until the Agent conversation ends, and then the Plugin writes the results and publishes the response event", "/ 监听 PromptOptimizationResponseEvent,更新 ChatTask 状态": "/ Listen to PromptOptimizationResponseEvent and update ChatTask status", "引用 PromptRange 的事件": "Events referencing PromptRange", "AgentsManager 现在知道 PromptRange 的存在,但只通过抽象层交互": "AgentsManager is now aware of the existence of PromptRange but only interacts through the abstraction layer", "处理逻辑...": "Processing logic...", "/ Agent模板信息": "/Agent template information", "/ 系统消息": "/ system message", "/ 是否启用": "/ Whether to enable", "/ PromptRange 的代号": "/Code name for PromptRange", "/ 第三方机器人平台类型": "/ Third-party robot platform type", "/ 第三方机器人平台参数": "/ Third-party robot platform parameters", "/ 描述": "/ describe", "/ Function Call 名称列表,多个用逗号分隔": "/ Function Call name list, multiple separated by commas", "/ McpEndpoints,多个用逗号分隔": "/ McpEndpoints, multiple separated by commas", "/ 获取McpEndpoints的JSON对象": "/ Get the JSON object of McpEndpoints", "/ 包含所有Endpoints的Dictionary对象": "/ Dictionary object containing all Endpoints", "/ 添加一个MCP Endpoint": "/ Add an MCP Endpoint", "/ Endpoint名称": "/ Endpoint name", "/ 是否添加成功": "/ Whether the addition was successful", "添加或更新Endpoint": "Add or update Endpoint", "序列化回字符串": "serialize back to string", "/ 移除一个MCP Endpoint": "/ Remove an MCP Endpoint", "/ 是否移除成功": "/ Whether the removal was successful", "移除Endpoint": "Remove Endpoint", "如果为空则设为null,否则序列化回字符串": "Set to null if empty, otherwise serialize back to string", "/ 微信公众号": "/ WeChat public account", "/ 企业微信机器人": "/ Enterprise WeChat robot", "/ ChatGroup 数据库实体": "/ChatGroup database entity", "/ 群名称": "/group name", "/ 状态": "/ state", "/ 管理员代理模板Id": "/ Administrator agent template ID", "/ 对接人代理模板Id": "/ Connector agent template ID", "/ 群状态": "/group status", "/ 未开始": "/ not started", "/ 运行中": "/ running", "/ 已结束": "/ ended", "/ ChatGroupHistory 数据库实体": "/ChatGroupHistory database entity", "/ ChatGroupMemer 数据库实体": "/ChatGroupMemer database entity", "/ AgentTemplate(类型同名)": "/AgentTemplate (type with the same name)", "/// ChatGroup(类型同名)": "/// ChatGroup (type with the same name)", "/ 重新设置 UID": "/reset UID", "/ 对于对话结果的评价": "/ Comment on the outcome of the conversation", "/ 进行 WebHook 的平台": "/ Platform for performing WebHook", "/ 进行 WebHook 的平台参数": "/ Platform parameters for WebHook", "/ 系统消息(PromptCode)": "/ System message (PromptCode)", "/ 可调用的函数名称列表,以逗号分隔": "/ List of callable function names, separated by commas", "/ ChatGroup 数据库实体 DTO": "/ ChatGroup Database Entity DTO", "/ ChatGroupHistory 数据库实体 DTO": "/ ChatGroupHistory Database Entity DTO", "/ ChatGroupMemer 数据库实体 DTO": "/ ChatGroupMemer Database Entity DTO", "/ 用于缓存的当前运行任务信息": "/ used to cache currently running task information", "throw new System.Exception(\"运行到这里了\");": "throw new System.Exception(\"Running here\");", "联合主键": "Union primary key", "/ 2、运行:PM> add-migration [更新名称] -c AgentsManagerSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ": "/ 2. Run: PM> add-migration [update name] -c AgentsManagerSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ", "/ 2、运行:PM> add-migration [更新名称] -c AgentsManagerSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ": "/ 2. Run: PM> add-migration [update name] -c AgentsManagerSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ", "/ 2、运行:PM> add-migration [更新名称] -c AgentsManagerSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ": "/ 2. Run: PM> add-migration [update name] -c AgentsManagerSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ", "/ 2、运行:PM> add-migration [更新名称] -c AgentsManagerSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ": "/ 2. Run: PM> add-migration [update name] -c AgentsManagerSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ", "/ 2、运行:PM> add-migration [更新名称] -c AgentsManagerSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ": "/ 2. Run: PM> add-migration [update name] -c AgentsManagerSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ", "/ 2、运行:PM> add-migration [更新名称] -c AgentsManagerSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ": "/ 2. Run: PM> add-migration [update name] -c AgentsManagerSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ", "var logStr = $\"下载网页内容{(requestSuccess ? \"成功\" : \"失败\")}。\" +": "var logStr = $\"Download web content {(requestSuccess ? \"Success\" : \"Failure\")}.\" +", "(requestSuccess ? $\"字符数:{urlData.MarkDownHtmlContent?.Length}\" : $\"错误代码:{item.Value.Result}\") +": "(requestSuccess ? $\"Number of characters: {urlData.MarkDownHtmlContent?.Length}\" : $\"Error code: {item.Value.Result}\") +", "logStr += $\" 来源:{urlData.ParentUrl} (链接:{urlData.LinkText})\";": "logStr += $\"Source: {urlData.ParentUrl} (Link: {urlData.LinkText})\";", "SenparcTrace.SendCustomLog(\"RAG日志\", logStr);": "SenparcTrace.SendCustomLog(\"RAG log\", logStr);", "Console.WriteLine(\"正在处理信息...\");": "Console.WriteLine(\"Processing information...\");", "#region Embedding 储存信息": "#region Embedding Save information", "//测试 TextEmbedding": "//Test TextEmbedding", "#pragma warning disable SKEXP0050 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。": "#pragma warning disable SKEXP0050 // Type is for evaluation only and may be changed or removed in a future update. Cancel this diagnostic to continue.", "#pragma warning restore SKEXP0050 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。": "#pragma warning restore SKEXP0050 // Type is for evaluation only and may be changed or removed in a future update. Cancel this diagnostic to continue.", "Console.WriteLine($\"等待冷却 {match.Value} 秒\");": "Console.WriteLine($\"Waiting for cooling {match.Value} seconds\");", "Console.WriteLine($\"处理完成(文件数:{contentMap.Count},段落数:{i})\");": "Console.WriteLine($\"Processing completed (number of files: {contentMap.Count}, number of paragraphs: {i})\");", "#region 提问": "#region Ask a question", "Console.WriteLine(\"提问:\" + question);": "Console.WriteLine(\"Question: \" + question);", "你是一位咨询机器人,你将根据我所提供的“提问”以及“备选信息”组织语言,生成一段给我的回复。": "You are a consulting robot. You will organize the language based on the \"questions\" and \"alternative information\" I provide to generate a reply to me.", "\"\"备选信息\"\"可能有多条,使用 ////// 表示每一条信息的开头, 表示每一条信息的结尾。在 ****** 后会有一个数字,表示这条信息的相关性。": "\"\"Alternative information\"\" may have multiple pieces, use ////// to indicate the beginning of each piece of information, and to indicate the end of each piece of information. There will be a number after ****** indicating the relevance of this message.", "你必须:": "You must:", "- 将回答内容严格限制在我所提供给你的备选信息中(开头和结尾标记中间的内容),其中越靠前的备选信息可信度越高,相关性不属于答案内容本身,因此在组织语言的过程中必须将其忽略。": "- Strictly limit the content of the answer to the alternative information I have provided to you (the content between the opening and closing marks). The earlier the alternative information, the higher the credibility. The relevance does not belong to the answer content itself, so it must be ignored in the process of organizing the language.", "- 请严格从“备选信息”中挑选和“提问”有关的信息,不要输出没有相关依据的信息。\";": "- Please strictly select information related to the \"question\" from the \"optional information\" and do not output information without relevant basis. \";", "SenparcTrace.SendCustomLog(\"RAG日志\", $@\"提问:{question},耗时:{(DateTime.Now - questionDt).TotalMilliseconds}ms": "SenparcTrace.SendCustomLog(\"RAG log\", $@\"Question: {question}, time consumption: {(DateTime.Now - questionDt).TotalMilliseconds}ms", "结果:": "result:", "Console.Write(\"回答:\");": "Console.Write(\"Answer:\");", "var input = @$\"提问:{question}": "var input = @$\"Question: {question}", "备选答案:": "Alternative answers:", "//使用流式输出": "//Use streaming output", "var originalColor = Console.ForegroundColor;//原始颜色": "var originalColor = Console.ForegroundColor;//original color", "//每个流式输出改变一次颜色": "//Change color once for each streaming output", "//输出结果": "//Output results", "//复原颜色": "//restore color", "也可以留空,将自动获取": "You can also leave it blank and it will be automatically obtained.", "尝试从 DI 容器获取服务,确保在调用时 ServiceProvider 已就绪": "Try to get the service from the DI container, make sure the ServiceProvider is ready when called", "注意:Constructor 注入在 Kernel.Plugins.AddFromType() 时可能需要 Kernel 自身配置了 DI": "Note: Constructor injection in Kernel.Plugins.AddFromType() may require Kernel itself to configure DI", "这里为了保险起见,使用 ServiceLocator 作为 backup": "To be on the safe side, use ServiceLocator as backup here.", "/ Prompt 优化 Plugin": "/Prompt Optimization Plugin", "/ Agent 通过调用这些 function 来完成 Prompt 优化任务": "/ Agent completes Prompt optimization tasks by calling these functions", "/ 获取 Prompt 信息": "/ Get Prompt information", "/ 分析 Range 中所有模型的历史评分": "/ Analyze historical ratings of all models in Range", "/ 创建优化后的新 Prompt 版本": "/Create a new, optimized version of Prompt", "/ 可选:一般由 PromptRange 页面打靶;仅当任务明确要求时在 Agent 内调用": "/ Optional: Generally used for target shooting on the PromptRange page; only called within the Agent when the task explicitly requires it", "获取 PromptItem": "GetPromptItem", "执行打靶": "Perform target practice", "/ 执行 AI 评分": "/ perform AI scoring", "获取最新的 PromptResult": "Get the latest PromptResult", "获取期望结果": "Get expected results", "执行 AI 评分": "Perform AI scoring", "更新 PromptItem 的平均分": "Update the average score of PromptItem", "重新获取更新后的 PromptItem": "Retrieve the updated PromptItem", "/ 使用 AgentTemplateDto 进行更新": "/ Update using AgentTemplateDto", "两者暂时等同": "The two are temporarily equivalent", "/ 获取干净的消息信息": "/ Get clean message information", "临时使用本机线程": "Temporarily use native threads", "/ 运行 ChatGroup(等待运行完成)": "/Run ChatGroup (wait for the run to complete)", "同一外围 Agent": "The same peripheral Agent", "作为唯一入口和汇报的关键人": "As the sole point of entry and reporting", "TODO:确认 Prompt 此时是否存在,如果不存在需要给出提示": "TODO: Confirm whether Prompt exists at this time. If it does not exist, you need to give a prompt.", "TODO:添加指定入口对接人员,参考群主": "TODO: Add designated entrance docking personnel, refer to the group owner", "var hearingMember = new UserProxyAgent(name: chatGroup.Name + \"群友\");": "var hearingMember = new UserProxyAgent(name: chatGroup.Name + \"Group Friends\");", "遍历所有 agents, 两两之间运行 graphConnector.ConnectFrom(agent1).TwoWay(agent2);": "Traverse all agents, running graphConnector.ConnectFrom(agent1).TwoWay(agent2);", "//Console.WriteLine(\"您已推出对话\");": "//Console.WriteLine(\"You have exited the conversation\");", "logger.Append(\"已完成运行:\" + chatGroup.Name);": "logger.Append(\"Completed running: \" + chatGroup.Name);", "/ 在独立进程中运行 ChatGroup(UI 界面中进行,不等待完成)": "/ Run ChatGroup in a separate process (in the UI interface, without waiting for completion)", "/ 运行 ChatGroup 直至本轮对话结束(用于 Prompt 优化等需同步等待的场景)": "/ Run ChatGroup until the end of this round of dialogue (used for Prompt optimization and other scenarios that require synchronous waiting)", "更新状态": "update status", "运行中进行缓存": "Caching on the fly", "全局默认模型": "Global default model", "确保已添加 Admin 和 Enter Agent": "Make sure Admin and Enter Agent are added", "同一 AgentTemplate 只保留一条(避免 Admin/Enter/成员重复出现两次相同模型,并行工具调用产生重复副作用)": "Only one of the same AgentTemplate is retained (to prevent the Admin/Enter/member from appearing twice in the same model and parallel tool calls to produce repeated side effects)", "Admin Agent 配置": "Admin Agent configuration", "Enter Agent 配置": "Enter Agent Configuration", "Enter Agent 中间件对象": "Enter Agent middleware object", "Function Call 全局对象集合": "Function Call global object collection", "遍历每一个成员": "Iterate through each member", "不启用的智能体不参与对话": "Agents that are not enabled do not participate in the conversation", "当前 Agent 配置": "Current Agent configuration", "判断是否需要个性化模型参数": "Determine whether personalized model parameters are needed", "使用个性化参数创建": "Created with personalization parameters", "获取当前 Agent 的 MCP Endpoints": "Get the MCP Endpoints of the current Agent", "遍历 endpointsDict 中的每个键值对": "Iterate over each key-value pair in endpointsDict", "获取键和值": "Get key and value", "使用 key 和 value 进行操作": "Operate with key and value", "类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。": "Types are for evaluation only and may be changed or removed in future updates. Cancel this diagnostic to continue.", "判断是否需要 Function Call": "Determine whether Function Call is needed", "添加 Plugins": "Add Plugins", "设置 Agent": "Setup Agent", "using (var scope = ServiceProvider.CreateScope())//已关闭": "using (var scope = ServiceProvider.CreateScope())//Close", "使用星型网络": "Use star network", "Console.WriteLine(\"您已推出对话\");": "Console.WriteLine(\"You have exited the conversation\");", "完成后移除缓存": "Remove cache when finished", "/ 获取缓存中记录正在运行的 ChatTask 的 Key": "/ Get the Key of the running ChatTask recorded in the cache", "TODO:需要缓存,以便快速读取": "TODO: Need to cache for fast reading", "TODO 检查是否所有任务已经完成,如果完成则设置 ChatGroup 状态为闲置状态": "TODO checks whether all tasks have been completed, and if completed, sets the ChatGroup status to idle.", "/ 关闭未完成的任务": "/Close unfinished tasks", "/ 只是筛选在此时间之前的未完成(Chatting 任务)": "/ Just filter unfinished (Chatting tasks) before this time", "/ 在 Agent 对话与 HTTP 优化请求之间传递结果(CreateOptimizedPrompt 写入,Handler 读取后发布响应事件)。": "/ Pass results between Agent conversations and HTTP optimization requests (CreateOptimizedPrompt writes, Handler reads and publishes response events).", "/ 每个优化 RequestId 只允许一次 (工具或 Kernel 回退谁先抢到谁写)。": "/ Each optimization RequestId is only allowed once (the tool or Kernel fallback will be written by whoever grabs it first).", "/ 解决:工具已插入 DB 但 未写入 State → TryTakeResult 失败 → 回退再插一条(内容相同)。": "/ Solution: The tool has been inserted into DB but is not written to State → TryTakeResult fails → fall back and insert another one (with the same content).", "/ 统一键格式,避免模型/客户端传入的 GUID 大小写与 不一致导致 _states 匹配失败(no registered session)。": "/ Unify the key format to avoid _states matching failure (no registered session) caused by inconsistency between the case of the GUID passed in by the model/client and .", "/ 在调用 AddPromptItemAsync 之前调用;仅第一次返回 true。": "/ Called before calling AddPromptItemAsync; only returns true the first time. ", "/ AddPromptItemAsync 失败时释放,允许 Kernel 回退重试写库。": "/ Released when AddPromptItemAsync fails, allowing Kernel to fall back and retry writing to the library. ", "/ 将模型传入的 optimizationRequestId 归一到 已注册的 key(禁止瞎填 Guid 导致孤儿 State)。": "/ Normalize the optimizationRequestId passed in by the model to the registered key (it is forbidden to blindly fill in the Guid and cause an orphan state).", "/ 统一清理指定 requestId 的所有 Bridge 状态(_states + _versionInsertClaimed)。": "/ Uniformly clear all Bridge states (_states + _versionInsertClaimed) of the specified requestId.", "/ 应在 ChatTaskHandler 的 finally 中调用,确保无论成功/失败都不留残余。": "/ should be called in the finally of ChatTaskHandler to ensure that no residue is left regardless of success/failure.", "成功路径不释放 claim:保持锁定以阻止后续重复插入。": "Success paths do not release claim: the lock is held to prevent subsequent repeated insertions.", "锁将由 ChatTaskHandler 的 CleanupRequest 统一清理。": "The lock will be cleaned uniformly by ChatTaskHandler's CleanupRequest.", "用于存储挂起的请求:RequestId -> TCS": "Used to store pending requests: RequestId -> TCS", "/ 同一靶道 + 需求 + 模型下禁止并发两次 HTTP 优化(双击会发两个 RequestId,各插一行且内容常相同)。": "/ Two concurrent HTTP optimizations are prohibited under the same target + demand + model (double-clicking will send two RequestIds, each with a row inserted and the content is always the same).", "/ 确保 PromptCatalyzer Agent 和相关资源已初始化(细粒度检查)": "/ Make sure the PromptCatalyzer Agent and related resources are initialized (fine-grained check)", "/ 可选:用户指定的 AI Model ID": "/ Optional: user-specified AI Model ID", "=== 步骤1:检查 Agent 是否已存在 ===": "=== Step 1: Check if the Agent already exists ===", "Agent 的 SystemMessage 存储了 PromptCode": "Agent's SystemMessage stores PromptCode", "Agent 不存在,需要创建": "Agent does not exist and needs to be created", "通过 EventBus 请求创建 PromptItem": "Create PromptItem via EventBus request", "创建 Agent": "Create Agent", "🔥 关键:设置 function-calling": "🔥 Key: Set function-calling", "=== 步骤3:检查 ChatGroup 是否已存在(独立检查)===": "=== Step 3: Check if the ChatGroup already exists (independent check) ===", "验证 ChatGroup 的 Agent 引用是否正确": "Verify that the Agent reference for the ChatGroup is correct", "TODO: 这里可以选择更新 ChatGroup 的 Agent 引用,或者提示用户手动处理": "TODO: Here you can choose to update the Agent reference of ChatGroup, or prompt the user to handle it manually.", "/ 完成初始化请求(由 PromptInitResponseHandler 调用)": "/ Complete the initialization request (called by PromptInitResponseHandler)", "/ 优化指定的 Prompt(包括内容和参数)": "/ Optimize the specified Prompt (including content and parameters)", "1. 确保 Agent 已初始化": "1. Make sure the Agent is initialized", "2. 发布优化请求事件": "2. Publish optimization request events", "3. 等待响应(Agent 多轮对话可能较慢)": "3. Waiting for response (Agent multi-round dialogue may be slower)", "/ 完成优化请求(由 PromptOptimizationResponseHandler 调用)": "/ Complete optimization request (called by PromptOptimizationResponseHandler)", "/ API 权限验证特性": "/ API permission verification feature", "/ 支持多种认证方案:Cookie (AdminAuthorize) 和 JWT (Bearer)": "/ Supports multiple authentication schemes: Cookie (AdminAuthorize) and JWT (Bearer)", "/ 默认构造函数,支持 Cookie 和 JWT 两种认证方式": "/Default constructor, supports two authentication methods: Cookie and JWT", "支持多种认证方案:Admin Cookie 认证 和 JWT Bearer 认证": "Supports multiple authentication schemes: Admin Cookie authentication and JWT Bearer authentication", "只要任一方案通过,即可访问": "As long as any plan passes, you can access", "/ 带策略的构造函数": "/ Constructor with strategy", "/ 授权策略名称(如 \"AdminOnly\")": "/ Authorization policy name (such as \"AdminOnly\")", "/ PromptCatalyzer 初始化 API Controller": "/PromptCatalyzer initializes API Controller", "/ 检查 PromptCatalyzer 是否已初始化": "/ Check if PromptCatalyzer has been initialized", "/ 获取所有可用的 Chat 类型 AI Model": "/ Get all available Chat type AI Models", "先获取所有模型用于调试": "First obtain all models for debugging", "记录每个模型的详情": "Record the details of each model", "查询 Chat 类型的模型(只检查类型,不检查 Show 字段)": "Query the model of Chat type (only the type is checked, the Show field is not checked)", "/ 初始化 PromptCatalyzer Agent 和相关 Prompt 资源": "/ Initialize PromptCatalyzer Agent and related Prompt resources", "验证 Model 是否存在": "Verify that Model exists", "调用初始化服务": "Call initialization service", "/ PromptCatalyzer 状态 DTO": "/PromptCatalyzer Status DTO", "/ 可用模型列表 DTO": "/ List of available models DTO", "/ AI Model 信息 DTO": "/AI Model Information DTO", "/ 初始化请求 DTO": "/Initialization request DTO", "/ 初始化响应 DTO": "/Initialize response DTO", "/ Prompt 优化 API Controller": "/Prompt Optimize API Controller", "🔥 关键修复:确保 Agent 和 ChatGroup 已初始化": "🔥 Critical fix: Make sure Agent and ChatGroup are initialized", "new AreaPageMenuItem(GetAreaUrl($\"/Admin/AgentsManager/DatabaseSample\"),\"数据库操作示例\",\"fa fa-bookmark-o\")": "new AreaPageMenuItem(GetAreaUrl($\"/Admin/AgentsManager/DatabaseSample\"),\"Database Operation Example\",\"fa fa-bookmark-o\")", "TOOD: 按照不同租户,需要区分": "TOOD: Different tenants need to be distinguished", "TODO: 调试多租户,暂时禁用": "TODO: Debugging multi-tenancy, temporarily disabled", "name: \"Agents 定时清理未完成任务\",": "name: \"Agents regularly clean up unfinished tasks\",", "//SenparcTrace.SendCustomLog(\"执行调试\", \"DatabaseToolkit.Register.ThreadConfig\");": "//SenparcTrace.SendCustomLog(\"Execute debugging\", \"DatabaseToolkit.Register.ThreadConfig\");", "threadInfo.RecordStory(\"Agents 任务开始检测并\");": "threadInfo.RecordStory(\"Agents task started to detect and complete\");", "threadInfo.RecordStory(\"检测并备份结束\");": "threadInfo.RecordStory(\"Detection and backup completed\");", "AutoMap映射": "AutoMap mapping", "注册 PromptOptimizationService": "Register PromptOptimizationService", "🔥 新增:Prompt 优化 Plugin(含 GetPromptInfo, CreateOptimizedPrompt, ExecuteShootTest, ExecuteAIGrade 等方法)": "🔥 Newly added: Prompt optimization Plugin (including GetPromptInfo, CreateOptimizedPrompt, ExecuteShootTest, ExecuteAIGrade and other methods)", "测试": "test", "🔥 新增:Prompt 优化 Plugin": "🔥 New: Prompt Optimization Plugin", "页面整体容器 也是tabs整体样式": "The overall container of the page is also the overall style of tabs", "tabPane容器 也是tabs单项整体样式": "The tabPane container is also the overall style of tabs.", "侧边整体容器": "Side integrated container", "侧边头部容器 (标题和搜索)": "Side header container (title and search)", "侧边主体容器 155 114 60": "Side main container 155 114 60", "标题 行": "title row", "搜索区域": "search area", "筛选条件 popover": "filter popover", "智能体 侧边列表 item": "Agent side list item", "智能体列表": "Agent list", "滚动区域 215 40 20 28 = 303": "Scroll area 215 40 20 28 = 303", "//颜色、x轴偏移量、y轴偏移量,": "//Color, x-axis offset, y-axis offset,", "智能体详情": "Agent details", "组侧边 tree node": "Group side tree node", "组 主体内容": "Group body content", "抽屉 表单容器": "Drawer form container", "组成员": "group member", "抽屉 右侧自定义": "Drawer right side customized", "el 组件样式修改": "el component style modification", "以下 自定义公用样式": "The following custom public styles", "单行 溢出隐藏": "Single line overflow hidden", "多行 溢出隐藏": "Multi-line overflow hidden", "任务描述": "Task description", "全局滚动条样式": "Global scroll bar style", "自定义滑动按钮": "Custom sliding button", "上箭头": "up arrow", "下箭头": "down arrow", "左箭头": "left arrow", "右箭头": "right arrow", "全局滚动条样式结束": "Global scroll bar style ends", "Function Calls 相关样式": "Function Calls related styles", "MCP Endpoints 相关样式": "MCP Endpoints related styles", "* axios封装": "* axios package", "* 请求拦截、响应拦截、错误统一处理": "* Request interception, response interception, and unified error handling", "创建一个axios实例": "Create an axios instance", "请求拦截": "request interception", "响应拦截器": "response interceptor", "console.log('文件流:', response)": "console.log('File stream:', response)", "请求已发出,其他状态": "The request has been sent, other status", "切换隐藏时不给错误提示,直接刷新": "No error message is given when switching to hide, refresh directly", "el 组件尺寸大小 默认为空 medium、small、mini": "el component size defaults to empty medium, small, mini", "first(智能体) second(组) third(任务)": "first(agent) second(group) third(task)", "显隐 visible": "visible visible", "智能体 新增|编辑": "Agent New|Edit", "智能体 新增dialog": "Intelligent agent adds dialog", "组 新增|编辑": "Group New|Edit", "组 启动": "group start", "智能体参数 列表": "Agent parameter list", "任务评价页面": "Task evaluation page", "MCP工具列表对话框": "MCP Tool List Dialog", "等待 Waiting stand #3376cd": "Waiting stand #3376cd", "聊天 Chatting loading #409EFF": "Chat Chatting loading #409EFF", "停顿 Paused loading #409EFF": "Paused loading #409EFF", "完成 Finished success #67C23A": "Finished Finished success #67C23A", "取消 Cancelled error #666": "Cancel Canceled error #666", "取消 fail error #F56C6C": "Cancel fail error #F56C6C", "动画": "animation", "旋转": "rotate", "智能体 ---start": "Agent ---start", "筛选文本": "Filter text", "默认降序": "Default descending order", "进行中": "in progress", "停用": "deactivate", "待命": "Standby", "筛选条件 popover 显隐": "Filter condition popover show and hide", "为了保持最后一行的样式 填充的card数量": "In order to maintain the style of the last row, the number of cards filled in", "侧边智能体index 默认全部": "Side agent index defaults to all", "智能体详情数据 查看": "View agent details data", "智能体详情 tabs": "Agent details tabs", "first(组) second(任务)": "first(group) second(task)", "智能体详情 组": "Agent Details Group", "1:组详情 2:任务详情": "1:Group details 2:Task details", "侧边组index 默认全部": "Side group index defaults to all", "选中的任务列表": "Selected task list", "组 任务列表": "Group task list", "智能体详情 任务": "Agent Details Task", "侧边任务index 默认全部": "Side task index defaults to all", "智能体 ---end": "Agent ---end", "组 ---start": "Group ---start", "1:组列表 2:组详情 3:任务详情": "1: Group list 2: Group details 3: Task details", "组 新增|编辑 智能体": "Group New|Edit Agent", "组新增时的智能体列表": "Agent list when the group is added", "组 ---end": "Group ---end", "任务 task ---start": "Task task ---start", "任务模块 筛选条件 popover 显隐": "Task module filter condition popover show and hide", "任务详情数据 查看": "View task details data", "任务 task ---end": "Task task ---end", "0 是新增": "0 is new", "名称": "name", "是否启用": "Whether to enable", "说明": "illustrate", "外接平台": "External platform", "外接参数": "External parameters", "头像": "avatar", "Function Call 名称,逗号分隔": "Function Call names, comma separated", "{ required: true, message: '请填写', trigger: 'blur' },": "{ required: true, message: 'Please fill in', trigger: 'blur' },", "成员列表": "Member list", "群主即agent": "The group owner is the agent", "对接人即agent": "The docking person is the agent", "组名称": "Group name", "组id": "group id", "标题": "title", "模型 id": "model id", "是否采用个性化": "Whether to use personalization", "任务评价": "task evaluation", "对话记录 轮询": "Conversation Record Polling", "智能体参数列表": "Agent parameter list", "tabs选中": "tabs selected", "描述内容": "Description content", "用于编辑时临时存储标签": "Used to temporarily store tags while editing", "存储所有可用的插件类型": "Stores all available plugin types", "MCP Endpoints相关": "MCP Endpoints related", "当前查看的MCP工具列表": "List of currently viewed MCP tools", "计算未被选择的插件类型": "Count unselected plugin types", "将逗号分隔的字符串转换为数组进行比较": "Convert comma separated string to array for comparison", "解析 McpEndpoints JSON 字符串": "Parse McpEndpoints JSON string", "在组件创建时获取插件类型列表": "Get list of plugin types when component is created", "调试对话框相关变量": "Debug dialog related variables", "测试对话框显示": "Test dialog box displays", "测试创建假工具列表": "Test creating fake tool list", "智能体": "agent", "组": "Group", "任务": "Task", "寻找目标字符串": "Find target string", "待判断的字符串": "The string to be judged", "const str = '2025.05.07.1-T1-A1-草稿';": "const str = '2025.05.07.1-T1-A1-Draft';", "正则表达式:匹配 XXXX.XX.XX.X 的结构(X为数字)": "Regular expression: Match the structure of XXXX.XX.XX.X (X is a number)", "判断字符串是否符合规则": "Determine whether a string conforms to the rules", "计算 agent列表 需要填充的元素数量": "Calculate the number of elements that need to be filled in the agent list", "单个元素 最小宽 315": "Single element minimum width 315", "获取 状态文本": "Get status text", "获取 状态颜色": "Get status color", "获取 智能体 数据": "Get agent data", "接口对接": "Interface docking", "获取详情": "Get details", "确保更新数据时 不会清空选中": "Make sure the selection is not cleared when updating data", "组成员table 初始选中": "Group members table initial selection", "获取 智能体详情": "Get agent details", "获取组列表": "Get list of groups", "获取任务列表": "Get task list", "获取 组 数据": "Get group data", "获取agent列表": "Get agent list", "设置最新的任务信息": "Set the latest task information", "name: '全部组',": "name: 'All groups',", "清空选中": "Clear selection", "获取 组详情": "Get group details", "获取 任务 数据": "Get task data", "智能体 任务": "agent task", "智能体 组 任务": "Agent group task", "组 任务": "group task", "获取模型列表": "Get model list", "默认展示第一个任务详情": "Display the first task details by default", "获取 任务详情": "Get task details", "不是仅详情时 清除轮询": "Not Details Only Clear Polling", "获取任务成员列表": "Get task member list", "轮询 获取对话数据": "Polling to get conversation data", "获取 任务历史记录": "Get task history", "使用 MarkDown 格式,对输出结果进行展示": "Use MarkDown format to display the output results", "滚动区域 吸附底部": "Scroll area adsorption bottom", "获取 任务 成员列表": "Get task member list", "保存 submitForm 数据": "Save submitForm data", "agent 新增|编辑": "agent new|edit", "确保 serviceForm 是正确的对象": "Make sure serviceForm is the correct object", "直接将 functionCallTags 数组转换为字符串并赋值": "Directly convert the functionCallTags array to a string and assign it", "打印日志以便调试": "Print logs for debugging", "组启动(运行任务) ['drawerGroupStart', 'drawerTaskStart'].includes(btnType)": "Group start (run task) ['drawerGroupStart', 'drawerTaskStart'].includes(btnType)", "重置 组获取智能体query": "Reset group acquisition agent query", "重新获取数据": "Retrieve data", "重新获取任务详情": "Retrieve task details", "轮询获取 task 历史对话记录": "Polling to obtain task history conversation records", "执行代码块": "Execute code block", "清除 获取历史对话记录 的轮询": "Clear polling for historical conversation records", "编辑 Dailog|抽屉 按钮": "Edit Dailog|Drawer Button", "任务 评价": "Task Evaluation", "创建一个新的对象来存储表单数据": "Create a new object to store form data", "确保 functionCallNames 被正确初始化": "Make sure functionCallNames is initialized correctly", "将数据赋值给表单": "Assign data to the form", "// 获取 全部智能体数据": "// Get all agent data", "回显 表单值": "echo form values", "打开 抽屉": "open drawer", "Dailog|抽屉 打开 按钮": "Dailog|Drawer Open button", "console.log('通用新增按钮:', btnType);": "console.log('Universal new button:', btnType);", "详情: formData.chatGroupDto 列表: formData": "Details: formData.chatGroupDto List: formData", "Dailog|抽屉 关闭 按钮": "Dailog|Drawer Close Button", "清空数据": "Clear data", "清理 Function Calls 数据": "Clean up Function Calls data", "Dailog|抽屉 提交 按钮": "Dailog|Drawer Submit Button", "提交数据给后端": "Submit data to the backend", "只有执行分配任务启动的时候,保存后,才跳入到任务详情": "Only when the assigned task is started, and after saving, will it jump to the task details.", "切换到对应的tab": "Switch to the corresponding tab", "跳转到任务详情": "Jump to task details", "表单 单条校验": "Form single check", "识别事件": "Identify events", "自动选出PromptRange(不做处理)": "Automatically select PromptRange (no processing)", "TODO:默认成为新的提示词,zai": "TODO: becomes the new prompt word by default, zai", "切换 tabs 页面": "Switch tabs page", "筛选输入变化": "Filter input changes", "筛选条件事件 agent group task": "Filter event agent group task", "to do 调用接口": "to do call interface", "查看全部智能体 列表": "View all agents list", "清空索引": "Clear index", "清空详情数据": "Clear detailed data", "查看 智能体": "View Agent", "重置 数据": "Reset data", "获取智能体 详情": "Get agent details", "重置 智能体详情下的组和任务数据": "Reset group and task data under Agent Details", "切换 智能体详情 tabs 页面": "Switch the agent details tabs page", "智能体 状态 切换": "Agent state switching", "message 当作 HTML片段处理": "message is treated as an HTML fragment", "侧边 组tree 组件 节点 筛选": "side group tree component node filter", "侧边 组tree 组件 节点 点击": "Side group tree component node click", "清空组详情": "Clear group details", "组 查看详情": "Group View details", "组 查看全部 列表": "Group View All List", "组 查看列表 详情": "Group View List Details", "智能体下时 查看组详情": "View group details when the agent is down", "切换展示类型": "Switch display type", "组大类时 查看组详情": "View group details when grouping into major categories", "组 新增|编辑 智能体table 切换table 选中": "Group Add | Edit Agent table Switch table Select", "组 新增|编辑 智能体table 选中变化": "Group New|Edit Agent table Select changes", "组 新增|编辑 智能体 成员取消选中": "Group New|Edit Agent Members Unchecked", "组列表选中变化 (批量删除)": "Group list selection changes (batch deletion)", "组 删除": "group delete", "操作确认 提示": "Operation confirmation prompt", "查看全部组": "View all groups", "组批量删除": "Group batch deletion", "任务 查看全部 列表": "Tasks View All List", "查看 任务详情": "View task details", "返回组详情页面": "Return to group details page", "组件详情": "Component details", "智能体-组 任务列表table选中变化 (批量启动和删除)": "Agent-group task list table selection change (batch startup and deletion)", "组 任务列表table选中变化 (批量启动和删除)": "Group task list table selection change (batch startup and deletion)", "任务列表选择": "Task list selection", "查看智能体参数 列表": "View the list of agent parameters", "对接接口获取数据 this.agentParameterList": "The docking interface obtains data this.agentParameterList", "获取 任务成员列表": "Get task member list", "再次执行 (即再次启动)": "Execute again (i.e. start again)", "任务删除": "Task deletion", "组-任务批量启动(任务) agentGroupTaskBatch groupTaskBatch": "Group-task batch start (task) agentGroupTaskBatch groupTaskBatch", "组-任务批量删除(任务) agentGroupTaskBatch groupTaskBatch": "Group-task batch deletion (task) agentGroupTaskBatch groupTaskBatch", "查看任务描述": "View task description", "任务描述复制": "Task description copy", "复制文本": "copy text", "input 数值类型处理": "input numerical type processing", "任务成员列表筛选": "Filter task member list", "el-scrollbar 触底滚动 到底部": "el-scrollbar bottom scroll to the bottom", "当前滚动的顶部": "The top of the current scroll", "内容总高度": "total content height", "可视区域高度": "Viewing area height", "滚动到底部": "scroll to bottom", "获取发送人名称": "Get sender name", "靶场:targetrangeId 靶道:targetlaneId": "Target range: targetrangeId Target lane: targetlaneId", "展示详情数据": "Display detailed data", "处理靶道 和 靶场展示名称": "Processing Lanes and Range Display Names", "靶道": "target lane", "靶场": "range", "复制 task 任务描述": "Copy task task description", "task 对话 原始内容": "task conversation original content", "task 对话 HTML内容": "task conversation HTML content", "task 对话 promptCommand(任务描述)内容": "task dialogue promptCommand (task description) content", "task 任务描述": "task task description", "组成员头像堆叠 数量处理": "Group member avatar stacking quantity processing", "组成员头像堆叠 数量": "Group member avatar stack quantity", "显示新增 Function Call 输入框": "Display the new Function Call input box", "处理 Function Call 输入确认": "Handling Function Call input confirmation", "删除 Function Call 标签": "Delete Function Call tag", "测试MCP Endpoint连接": "Test MCP Endpoint connection", "设置加载状态": "Set loading status", "详细日志": "Detailed log", "根据实际API返回的数据结构进行判断": "Determine based on the data structure returned by the actual API", "尝试从不同位置获取工具列表": "Try getting a list of tools from a different location", "调试完整响应": "Debug full response", "检查可能的数据结构": "Check possible data structures", "结构1: response.data.data.tools": "Structure 1: response.data.data.tools", "结构2: response.data.data直接是工具列表": "Structure 2: response.data.data is directly the tool list", "确保工具列表是数组": "Make sure the tools list is an array", "确保每个工具对象都有必要的属性": "Make sure each tool object has the necessary properties", "初始化testResult对象": "Initialize testResult object", "设置结果属性": "Set result properties", "如果有工具列表,直接显示弹窗": "If there is a tool list, directly display the pop-up window", "清除加载状态": "Clear loading status", "获取插件类型列表": "Get a list of plugin types", "添加插件类型到 functionCallNames": "Add plugin type to functionCallNames", "将现有值分割为数组": "Split existing values ​​into array", "添加新值并用逗号连接": "Add new value and concatenate with comma", "McpEndpoints 相关方法": "McpEndpoints related methods", "显示添加 Endpoint 输入框": "Display the Add Endpoint input box", "编辑 Endpoint": "Edit Endpoint", "取消添加 Endpoint": "Cancel adding Endpoint", "确认添加或更新 Endpoint": "Confirm to add or update Endpoint", "编辑模式:如果名称变了,需要删除旧的再添加新的": "Edit mode: If the name changes, you need to delete the old one and add the new one.", "添加/更新 Endpoint": "Add/Update Endpoint", "清空输入框": "Clear input box", "删除 Endpoint": "Delete Endpoint", "删除指定 Endpoint": "Delete the specified Endpoint", "显示MCP工具列表对话框": "Display the MCP Tool List dialog box", "检查endpoint对象及其属性": "Check the endpoint object and its properties", "创建一个工具列表的副本": "Create a copy of the tool list", "设置当前MCP工具列表,并显示对话框": "Set the current MCP tool list and display the dialog box", "如果对话框不显示,则尝试使用备用弹窗": "If the dialog does not appear, try using an alternate popup", "备用的MCP工具列表弹窗 (使用alert)": "Alternate MCP tool list pop-up window (use alert)", "* 节流 防抖": "*Throttling and anti-shake", "据上一次触发时间间隔": "According to the last trigger time interval", "上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait": "The time interval between the last time the wrapped function was called last is less than the set time interval wait", "如果设定为immediate===true,因为开始边界已经调用过了此处无需调用": "If set to immediate===true, there is no need to call here because the starting boundary has already been called.", "如果延时不存在,重新设定延时": "If the delay does not exist, reset the delay", "* 克隆": "* clone", "* 判断值是否 数字": "* Determine whether the value is a number", "* @param {*} val 需要判断的变量": "* @param {*} val variable to be judged", "* 判断值是否是 空对象": "* Determine whether the value is an empty object", "* 打开 window窗口": "* Open window window", "* 模拟 a 标签": "* Simulate a tag", "* @param {string} url // 原地址": "* @param {string} url // original address", "* 处理接口 query 参数 转换为 string": "* Process the interface query parameter and convert it to string", "* @param {Object} queryObj // 原地址": "* @param {Object} queryObj //Original address", "将对象转换为 URL 参数字符串": "Convert object to URL parameter string", "过滤掉空值": "Filter out null values", "* 日期格式化为 yyyy-MM-dd HH:mm:ss": "* The date is formatted as yyyy-MM-dd HH:mm:ss", "* @returns {string} - 格式化后的时间": "* @returns {string} - formatted time", "月份从0开始": "Month starts from 0", "替换格式中的标识符": "Replace identifiers in format", "* 计算持续时间": "* Calculate duration", "* @param {string} startTime - 开始时间字符串(ISO格式)": "* @param {string} startTime - start time string (ISO format)", "* @param {string} [endTime] - 结束时间字符串(ISO格式),可选": "* @param {string} [endTime] - end time string (ISO format), optional", "* @returns {string} - 持续时间字符串,根据差值级别动态显示": "* @returns {string} - duration string, dynamically displayed according to the difference level", "将开始时间和结束时间转换为 Date 对象": "Convert start time and end time to Date objects", "如果没有结束时间,则使用当前时间": "If there is no end time, the current time is used", "计算时间差(以毫秒为单位)": "Calculate time difference in milliseconds", "各个时间单位的毫秒值": "millisecond value for each time unit", "假设一年365天": "Assume there are 365 days in a year", "计算各个时间单位": "Calculate individual time units", "动态构建输出字符串": "Dynamically build output string", "简单对比 数组是否相等": "Simple comparison to see if arrays are equal", "prompt 分数处理": "prompt score processing", "* 加载载 模拟json 数据": "* Load simulated json data", "task-html-renderer 渲染任务对话记录的内容": "task-html-renderer renders the content of the task conversation record", "使用 CSS 类": "Use CSS classes", "直接插入 HTML": "Insert HTML directly", "注册一个全局自定义指令 v-el-select-loadmore": "Register a global custom directive v-el-select-loadmore", "获取element-ui定义好的scroll盒子": "Get the scroll box defined by element-ui", "* scrollHeight 获取元素内容高度(只读)": "* scrollHeight gets the content height of the element (read-only)", "* scrollTop 获取或者设置元素的偏移值,常用于, 计算滚动条的位置, 当一个元素的容器没有产生垂直方向的滚动条, 那它的scrollTop的值默认为0.": "* scrollTop gets or sets the offset value of the element. It is often used to calculate the position of the scroll bar. When an element's container does not produce a vertical scroll bar, its scrollTop value defaults to 0.", "* clientHeight 读取元素的可见高度(只读)": "* clientHeight reads the visible height of the element (read-only)", "* 如果元素滚动到底, 下面等式返回true, 没有则返回false:": "* If the element is scrolled to the end, the following equation returns true, otherwise it returns false:", "load-more-select 组件": "load-more-select component", "默认使用公共": "Use public by default", "横向/竖向 horizontal/vertical": "horizontal/vertical horizontal/vertical", "接口返回数据": "Interface returns data", "找到dom": "find dom", "// 对dom新增class": "//Add class to dom", "对dom新增class": "Add new class to dom", "远程搜索": "remote search", "请求接口": "Request interface", "刷新接口": "Refresh interface", "调用接口": "Call interface", "本地搜索 调用": "local search call", "/ 备份间隔时间": "/ backup interval", "/ 是否已经启用备份": "/ Whether backup is enabled", "/ 备份物理路径": "/Backup physical path", "/ 上次备份时间": "/Last backup time", "/ 用于生成 SQLServer 数据库 Migration 信息的类,请勿修改": "/ Class used to generate SQLServer database migration information, please do not modify it", "/ 2、运行:PM> add-migration [更新名称] -C DatabaseToolkitSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ": "/ 2. Run: PM> add-migration [update name] -C DatabaseToolkitSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ", "Debug模式下项目根目录": "Project root directory in Debug mode", "/* 用于寻找 App_Data 文件夹,从而找到数据库连接字符串配置信息": "/* Used to find the App_Data folder to find the database connection string configuration information", "/ 用于生成 MySQL 数据库 Migration 信息的类,请勿修改": "/ Class used to generate MySQL database Migration information, please do not modify it", "/ 2、运行:PM> add-migration [更新名称] -C DatabaseToolkitSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ": "/ 2. Run: PM> add-migration [update name] -C DatabaseToolkitSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ", "/ 用于生成 Oracle 数据库 Migration 信息的类,请勿修改": "/ Class used to generate Oracle database migration information, please do not modify it", "/ 2、运行:PM> add-migration [更新名称] -C DatabaseToolkitSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ": "/ 2. Run: PM> add-migration [update name] -C DatabaseToolkitSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ", "/ 用于生成 PostgreSQL 数据库 Migration 信息的类,请勿修改": "/ Class used to generate PostgreSQL database migration information, please do not modify it", "/ 2、运行:PM> add-migration [更新名称] -C DatabaseToolkitSenparcEntities_PostgreSQL -o Domain/Migrations/Migrations.PostgreSQL ": "/ 2. Run: PM> add-migration [update name] -C DatabaseToolkitSenparcEntities_PostgreSQL -o Domain/Migrations/Migrations.PostgreSQL ", "/ 用于生成 Sqlite 数据库 Migration 信息的类,请勿修改": "/ Class used to generate Sqlite database Migration information, please do not modify it", "/ AI Agent 集成 AppService": "/ AI Agent integrated AppService", "/ 定义用于 AI Agent 与 Database Toolkit 交互的 Prompt 和系统": "/ Define prompts and systems for AI Agent to interact with Database Toolkit", "/ 获取 AI Agent 系统 Prompt": "/ Get AI Agent system prompt", "/ 返回配置给 AI Agent 的系统提示,定义其在与数据库交互时的行为": "/ Returns the system prompt configured for the AI ​​Agent, defining its behavior when interacting with the database", "/ 获取常见查询模板": "/ Get common query templates", "/ 返回预定义的常见查询模板,帮助 AI Agent 快速生成正确的查询": "/ Returns predefined common query templates to help AI Agent quickly generate correct queries", "5秒钟内创建的": "Created in 5 seconds", "/ 数据库操作 AppService": "/ Database operation AppService", "/ 提供执行数据库查询、更新、删除等操作的 Function": "/ Provide functions to perform database query, update, delete and other operations", "/ 查询数据库记录": "/ Query database records", "/ 根据指定条件和字段查询表中的数据": "/ Query data in the table based on specified conditions and fields", "验证模块和表": "Validate modules and tables", "模糊解析模块名": "Fuzzy parsing module name", "模糊解析实体名": "Fuzzy parsing of entity names", "执行查询": "Execute query", "/ 获取数据库统计信息": "/ Get database statistics", "/ 获取指定表的行数、最小/最大值等统计信息": "/ Get statistical information such as the number of rows and minimum/maximum values ​​of the specified table", "/ 查询数据库记录请求": "/Query database record request", "/ 获取统计信息请求": "/ Get statistics request", "/ 数据库架构元数据查询 AppService": "/ Database schema metadata query AppService", "/ 提供查询数据库结构信息的 Function": "/ Provides functions for querying database structure information", "/ 查询可用的模块和表结构": "/ Query available modules and table structures", "/ 返回所有可查询的模块及其表的结构信息": "/ Returns the structure information of all queryable modules and their tables", "如果指定了模块名和表名,获取详细信息": "If module name and table name are specified, get detailed information", "先尝试精确匹配,再尝试模糊匹配": "Try exact match first, then fuzzy match", "如果只指定了模块名,获取该模块的所有表": "If only the module name is specified, get all tables of the module", "获取所有模块和表的概览": "Get an overview of all modules and tables", "/ 查询数据库架构请求": "/Query database schema request", "可选:在此处加载默认数据": "Optional: Load default data here", "/ 显示当前的数据库配置类型": "/ Display the current database configuration type", "/ 数据库结构元数据": "/ Database structure metadata", "/ 用于存储模块-表-字段的映射关系": "/ Used to store module-table-field mapping relationship", "/ 模块名称": "/ module name", "/ 例如: Senparc.Xncf.AgentsManager": "/Example: Senparc.Xncf.AgentsManager", "/ 表名称": "/ table name", "/ 表的完全限定名(C# 类型全名)": "/ Fully qualified name of the table (full C# type name)", "/ 例如: Senparc.Xncf.AgentsManager.Models.DatabaseModel.AgentTemplate": "/ For example: Senparc.Xncf.AgentsManager.Models.DatabaseModel.AgentTemplate", "/ 表的中文描述": "/ Chinese description of the table", "/ 表的详描述": "/ Detailed description of the table", "/ 字段列表": "/ field list", "/ 创建时间": "/Creation time", "/ 是否可见/可查询": "/ Whether it is visible/queryable", "/ 用于权限控制": "/ used for permission control", "/ 数据库字段元数据": "/ Database field metadata", "/ 字段名称": "/Field name", "/ 字段类型": "/Field type", "/ 例如: string, int, DateTime": "/Example: string, int, DateTime", "/ 字段中文名称/描述": "/Field Chinese name/description", "/ 字段描述": "/Field description", "/ 是否为主键": "/ Whether it is the primary key", "/ 是否为必需字段": "/ Is it a required field?", "/ 最大长度(字符串字段)": "/ Maximum length (string field)", "/ 是否可用于查询过滤": "/ Whether it can be used for query filtering", "/ 用于确定是否在查询时返回此字段": "/ Used to determine whether to return this field when querying", "/ 模型-表映射信息的 DTO": "/ DTO for model-table mapping information", "/ 用于通过 API 传输": "/ for transfer via API", "/ 数据库字段信息 DTO": "/ Database field information DTO", "/ 查询条件元数据": "/ Query condition metadata", "/ 用于指定如何查询某个表": "/ is used to specify how to query a table", "/ 过滤条件 JSON": "/ filter condition JSON", "/ 用于构建 Where 条件": "/ is used to build Where conditions", "/ 例如: { \"Id\": 1 } 或 { \"Name\": \"test\" }": "/ For example: { \"Id\": 1 } or { \"Name\": \"test\" }", "/ 排序字段": "/ sort field", "/ 是否降序": "/ Whether to descend", "/ 页码(从 0 开始)": "/Page number (starting from 0)", "/ 每页数量": "/number per page", "/ 返回的字段列表": "/ Returned field list", "/ 如果为空,则返回所有可见字段": "/ If empty, returns all visible fields", "/ 通用数据库执行器": "/Universal Database Executor", "/ 提供模块/表级别的数据查询与统计能力": "/ Provide module/table level data query and statistical capabilities", "构造 Expression> 类型(而非 Func),与 GetFullListAsync 签名一致": "Constructs type Expression> (instead of Func), consistent with GetFullListAsync signature", "必须提供全部参数类型(包括可选参数),否则 GetMethod 无法定位到正确重载": "All parameter types (including optional parameters) must be provided, otherwise GetMethod cannot locate the correct overload", "调用时须传入全部参数(可选参数也必须明确提供)": "All parameters must be passed in when calling (optional parameters must also be provided explicitly)", "/ 数据库架构元数据提供者": "/ Database schema metadata provider", "/ 用于获取和管理数据库结构信息": "/ Used to obtain and manage database structure information", "/ 初始化元数据缓存": "/ Initialize metadata cache", "/ 扫描所有已注册的 XNCF 模块中的实体类": "/ Scan all registered XNCF modules for entity classes", "获取模块中的所有实体类": "Get all entity classes in the module", "查找 DatabaseModel 或 Models 命名空间下的实体": "Find entities under the DatabaseModel or Models namespace", "记录错误但继续,避免启动失败": "Log error but continue to avoid startup failure", "EntityBase 的完全限定类型名,避免强制引用 Senparc.Ncf.Core 程序集": "Fully qualified type name of EntityBase to avoid forcing a reference to the Senparc.Ncf.Core assembly", "/ 从模块程序集中获取实体类(继承自 EntityBase 的非抽象类)": "/ Get the entity class (non-abstract class inherited from EntityBase) from the module assembly", "忽略错误(如无法加载依赖项的程序集)": "Ignore errors (such as the dependency's assembly cannot be loaded)", "/ 判断类型是否继承自 Senparc.Ncf.Core.Models.EntityBase": "/ Determine whether the type inherits from Senparc.Ncf.Core.Models.EntityBase", "/ 从实体类型创建架构元数据": "/ Create schema metadata from entity types", "获取类的 XML 注释(如果存在)": "Get the XML annotation for a class (if present)", "获取所有公共属性": "Get all public properties", "跳过某些不应该在查询中暴露的属性": "Skip certain properties that should not be exposed in queries", "/ 判断是否应该跳过某个属性": "/ Determine whether an attribute should be skipped", "/ 获取简化的类型名称": "/ Get the simplified type name", "/ 判断属性是否为必需字段": "/ Determine whether the attribute is a required field", "/ 获取属性的最大长度": "/ Get the maximum length of the attribute", "/ 判断类型是否可以用于过滤": "/ Determine whether the type can be used for filtering", "/ 将用户输入的模块名解析为缓存中的精确 key。": "/ Resolve the module name entered by the user into the exact key in the cache.", "/ 匹配顺序:精确 → 大小写不敏感 → 后缀(\"AIKernel\"→\"Senparc.Xncf.AIKernel\")": "/ Matching order: Exact → Case-insensitive → Suffix (\"AIKernel\" → \"Senparc.Xncf.AIKernel\")", "/ → XncfRegisterManager.Name/MenuName → 包含(唯一时)": "/ → XncfRegisterManager.Name/MenuName → Contains (when unique)", "/ 返回 null 表示未找到。": "/ Returns null if not found.", "1. 精确匹配": "1. Exact match", "2. 大小写不敏感精确匹配": "2. Case-insensitive exact matching", "3. 后缀匹配(如 \"AIKernel\" 匹配 \"Senparc.Xncf.AIKernel\")": "3. Suffix matching (such as \"AIKernel\" matches \"Senparc.Xncf.AIKernel\")", "4. 通过 XncfRegisterManager.RegisterList 匹配 Name / MenuName": "4. Match Name / MenuName through XncfRegisterManager.RegisterList", "优先使用已注册模块的官方 Name 及人性化 MenuName 进行匹配": "Priority is given to using the official Name and humanized MenuName of the registered module for matching.", "5. 包含匹配(最宽松,仅在唯一匹配时使用)": "5. Inclusive match (the loosest, only used when there is a unique match)", "/ 在已解析模块中模糊匹配实体名(表名)。": "/ Fuzzy matches entity names (table names) in parsed modules.", "/ 匹配优先级(高→低):精确 > 大小写不敏感 > 前缀/被包含 > 包含": "/ Match priority (high → low): exact > case insensitive > prefix/included > contains", "/ 唯一最高分匹配时返回精确 TableName,否则返回 null。": "/ Returns the exact TableName if the unique highest score matches, otherwise returns null.", "唯一最高分 → 返回;多个相同最高分 → 歧义,返回 null": "Unique highest score → return; multiple identical highest scores → ambiguous, return null", "/ 对单个 schema 与输入实体名的匹配程度打分(0 = 不匹配)": "/ Score how well a single schema matches the input entity name (0 = no match)", "精确匹配(短名)": "Exact match (short name)", "精确匹配(全限定名)": "Exact match (fully qualified name)", "前缀:schema名 以 input 开头,或 input 以 schema名 开头": "Prefix: schema name starts with input, or input starts with schema name", "包含:schema名包含 input": "Contains: schema name contains input", "包含:input 包含 schema名": "Contains: input contains schema name", "/ 获取所有模块的架构信息": "/ Get the architecture information of all modules", "/ 获取指定模块的架构信息": "/ Get the architecture information of the specified module", "/ 获取所有已知模块名称(缓存 key 列表),用于错误提示。": "/ Get all known module names (cache key list) for error prompts.", "/ 获取指定模块下所有可见实体的短名称,用于错误提示。": "/ Get the short names of all visible entities under the specified module for error prompts.", "/ 获取指定表的架构信息": "/ Get the schema information of the specified table", "/ 获取实体的完全限定名": "/ Get the fully qualified name of the entity", "/ 转换为 DTO": "/ Convert to DTO", "TODO: 专门做数据库,负责定时项目的注册": "TODO: Specialize in database and responsible for registration of scheduled projects", "SenparcTrace.SendCustomLog(\"执行调试\", \"DatabaseToolkit.Register.ThreadConfig\");": "SenparcTrace.SendCustomLog(\"Execute debugging\", \"DatabaseToolkit.Register.ThreadConfig\");", "检测当前模块是否可用": "Check whether the current module is available", "同一时间内只提示一次": "Only prompt once at the same time", "初始化数据库备份方法": "Initialize database backup method", "初始化参数": "Initialization parameters", "threadInfo.RecordStory(\"完成备份设置数据载入\");": "threadInfo.RecordStory(\"Complete loading of backup setting data\");", "不需要备份,或没有设置,返回": "No backup required, or no settings, return", "可能没有配置数据库,返回": "There may be no configuration database, return", "执行备份方法": "Execute backup method", "必须确保全局唯一,生成后必须固定": "Must ensure global uniqueness and must be fixed after generation", "此处也可以加 SideMenu": "You can also add SideMenu here", "/ Color 实体类": "/ Color entity class", "/ 颜色码,0-255": "/ Color code, 0-255", "/ 附加列,测试多次数据库 Migrate": "/ Additional columns, test the database multiple times Migrate", "随机": "random", "随机产生颜色代码": "Randomly generate color codes", "/ ColumnMetadata 实体类,用于存储列的基本信息。": "/ ColumnMetadata entity class, used to store basic information of columns.", "/ 关联的表格ID。": "/The associated table ID.", "/ 列名称。": "/ column name.", "/ 列的数据类型。": "/ The data type of the column.", "/ 是否允许NULL值。": "/ Whether to allow NULL values.", "/ 列的默认值。": "/ The default value for the column.", "/ 关联的表格元数据。": "/ Associated table metadata.", "/ 关联的列ID。": "/ Associated column ID.", "/ 单元格的值。": "/The value of the cell.", "/ 关联的列元数据。": "/ Associated column metadata.", "/ 表格名称。": "/ table name.", "/ 表格描述。": "/Table description.", "/ 关联的列元数据集合。": "/ Associated column metadata collection.", "/ 关联的数据集合。": "/ Associated data collection.", "配置索引": "Configure index", "/ TableData 实体类,用于存储单元格的数据。": "/ TableData entity class, used to store cell data.", "/ TableMetadata 实体类,用于存储表格的基本信息。": "/ TableMetadata entity class, used to store basic information of the table.", "TODO: 缓存所有 Table 和 Column 信息": "TODO: Cache all Table and Column information", "/ 2、运行:PM> add-migration [更新名称] -c DynamicDataSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ": "/ 2. Run: PM> add-migration [update name] -c DynamicDataSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ", "/ 2、运行:PM> add-migration [更新名称] -c DynamicDataSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ": "/ 2. Run: PM> add-migration [update name] -c DynamicDataSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ", "/ 2、运行:PM> add-migration [更新名称] -c DynamicDataSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ": "/ 2. Run: PM> add-migration [update name] -c DynamicDataSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ", "/ 2、运行:PM> add-migration [更新名称] -c DynamicDataSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ": "/ 2. Run: PM> add-migration [update name] -c DynamicDataSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ", "/ 2、运行:PM> add-migration [更新名称] -c DynamicDataSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ": "/ 2. Run: PM> add-migration [update name] -c DynamicDataSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ", "/ 2、运行:PM> add-migration [更新名称] -c DynamicDataSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ": "/ 2. Run: PM> add-migration [update name] -c DynamicDataSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ", "如果是纯第一次安装,理论上不会有残留数据": "If this is a purely first-time installation, theoretically there will be no residual data.", "创建默认颜色": "Create default colors", "TODO:异步方法需要添加排序功能": "TODO: The asynchronous method needs to add sorting function", "TODO: 更多业务方法可以写到这里": "TODO: More business methods can be written here", "/ 尝试根据实体创建表和列元数据": "/ Attempts to create table and column metadata based on entities", "查看是否已经存在表": "Check if the table already exists", "创建对象": "Create object", "创建列": "Create column", "TODO:是否可为null,需要[Required]特性进行判断": "TODO: Whether it can be null requires the [Required] attribute to judge.", "TODO: 添加 .ToDto扩展方法": "TODO: Add .ToDto extension method", "/ 根据模板创建 TableDataDto 列表": "/ Create a TableDataDto list based on the template", "从ColumnMetadataDto中获取TableDataDto": "Get TableDataDto from ColumnMetadataDto", "/ 设置数据": "/set data", "* 使用 [ApiBind] 可将任意方法或类快速创建动态 WebApi。": "* Use [ApiBind] to quickly create dynamic WebApi from any method or class.", "* 在 DDD 系统中,出于安全和防腐考虑,建议只在 AppService 上使用。": "* In DDD systems, for security and anti-corrosion considerations, it is recommended to only use AppService.", "* 当 AppService 上添加 [ApiBind] 标签满足不了需求时,仍然可以手动创建 ApiController。": "* When adding the [ApiBind] tag to the AppService cannot meet the needs, you can still create an ApiController manually.", "/ 将 AppService 暴露为 WebApi": "/ Expose AppService as WebApi", "/ 自定义 Post 类型和复杂参数,同时测试异常抛出和自定义状态码": "/ Customize Post type and complex parameters, while testing exception throwing and custom status codes", "StringAppResponse 是 AppResponseBase 的快捷写法": "StringAppResponse is a shortcut for AppResponseBase", "/ 获取或初始化一个 ColorDto 对象": "/ Get or initialize a ColorDto object", "开始计时": "Start timing", "获取或初始化颜色参数": "Get or initialize color parameters", "记录耗时": "Recording time", "页面上点击“执行”后,将调用这里的方法": "After clicking \"Execute\" on the page, the method here will be called", "* 参数说明:": "* Parameter description:", "* response:已经初始化后的返回结果": "* response: the return result after initialization", "* logger:日志": "* logger: log", "* 如果直接对 response 的属性修改,则最终 return null,": "* If the properties of response are modified directly, null will eventually be returned.", "* 否则可以返回一个新的 response 对象,系统将自动覆盖原有对象": "* Otherwise, a new response object can be returned, and the system will automatically overwrite the original object.", "/ 花费时间": "/ spend time", "new AreaPageMenuItem(GetAreaHomeUrl(),\"首页\",\"fa fa-laptop\"),": "new AreaPageMenuItem(GetAreaHomeUrl(),\"Home\",\"fa fa-laptop\"),", "new AreaPageMenuItem(GetAreaUrl($\"/Admin/DynamicData/DatabaseSample\"),\"数据库操作示例\",\"fa fa-bookmark-o\")": "new AreaPageMenuItem(GetAreaUrl($\"/Admin/DynamicData/DatabaseSample\"),\"Database Operation Example\",\"fa fa-bookmark-o\")", "侧边栏": "sidebar", "主要内容区域": "main content area", "以下 修改el组件样式": "Modify the el component style below", "文本": "text", "限制最大高度,以适应两行文本": "Limit the maximum height to fit two lines of text", "标题区域": "title area", "菜单区域": "menu area", "组件内容区域": "component content area", "组件提示": "Component Tips", "组件占位区域": "Component footprint area", "提示": "hint", "操作区域": "operating area", "分段器": "segmenter", "表单区域": "form area", "table区域": "table area", "筛选盒子": "filter box", "添加排序": "Add sort", "排序展示列表": "Sort display list", "筛选条件设置": "Filter settings", "二进制数据处理": "Binary data processing", "el 组件尺寸大小 默认为空 medium、small、mini\\": "el component size defaults to empty medium, small, mini\\", "数据表 列表 list": "data table list list", "数据表 选中的item Id": "Data table selected item ID", "创建数据表的 表单": "Create data table form", "创建 表字段": "Create table fields", "{ required: true, message: '请选择事件', trigger: 'change' },": "{ required: true, message: 'Please select an event', trigger: 'change' },", "{ required: true, message: '请选择颜色', trigger: 'change' },": "{ required: true, message: 'Please select a color', trigger: 'change' },", "{ required: true, message: '请填写描述', trigger: 'blur' }": "{ required: true, message: 'Please fill in the description', trigger: 'blur' }", "表字段 列表 list": "table field list list", "表相关按钮 disabled": "Table related buttons disabled", "布局相关按钮 disabled": "Layout related buttons disabled", "获取 数据表 列表": "Get data table list", "模拟 数据": "simulated data", "todo 调用接口": "todo calling interface", "获取 数据表字段 列表": "Get the list of data table fields", "查看表详情": "View table details", "调用接口 获取字段列表信息": "Call the interface to obtain field list information", "新增数据表": "Add data table", "清空 字段数据": "Clear field data", "重置表单": "Reset form", "数据表 基础信息编辑": "Data table basic information editing", "取消编辑数据表": "Cancel editing data table", "确认创建数据表": "Confirm creation of data table", "校验 sheetForm 表单": "Validate sheetForm form", "// todo 调用接口": "// todo calling interface", "模拟调用接口": "Simulation call interface", "todo 调用接口 创建 this.sheetSelectId": "todo calls the interface to create this.sheetSelectId", "todo 调用接口 重新获取 dataSheetList": "todo calls the interface to re-obtain dataSheetList", "todo 调用接口 重新获取 tableFieldList: [], tableFieldTotal: 0,": "todo calls the interface to re-obtain tableFieldList: [], tableFieldTotal: 0,", "重置 sheetForm 表单": "Reset sheetForm form", "关闭 字段表单抽屉": "Close Field Form Drawer", "新增字段": "Add new field", "编辑字段": "Edit field", "删除字段": "Delete field", "todo 调用删除接口": "todo calls the delete interface", "标识ID 自动生成": "Identification ID automatically generated", "提交 新增字段表单抽屉": "Submit New Field Form Drawer", "清空表单 关闭抽屉": "Clear form Close drawer", "重置 新增字段表单抽屉": "Reset the new field form drawer", "创建布局": "Create layout", "todo 跳转 ?uid=796D12D8-580B-40F3-A6E8-A5D9D2EABB69": "todo jump ?uid=796D12D8-580B-40F3-A6E8-A5D9D2EABB69", "导入": "import", "导出": "Export", "测试 地址": "Test address", "布局 数据 列表": "layout data list", "获取 layoutList 数据": "Get layoutList data", "获取 状态样式名称": "Get status style name", "复制布局": "Copy layout", "这是 HTML 片段": "This is a HTML fragment", "message: '取消输入'": "message: 'Cancel input'", "查看布局": "View layout", "删除布局": "Delete layout", "布局名称": "layout name", "布局名称是否 编辑": "Whether the layout name is Edit", "布局菜单": "layout menu", "新增菜单loading": "Added menu loading", "默认选中的菜单": "Default selected menu", "默认选中的组件": "Components selected by default", "组件 选中类型 table normalColumn customColumn columnEditBtn form formItem formSubmitBtn": "Component selected type table normalColumn customColumn columnEditBtn form formItem formSubmitBtn", "列选中 index": "Column selected index", "拖拽 开始 index": "Drag and drop to start index", "拖拽 结束 index": "Drag to end index", "布局组件区域列表": "Layout component area list", "侧边菜单 切换": "Side menu toggle", "tabs 类别 添加:first 设置:second": "tabs category add:first set:second", "设置 tabs 类别 属性:first 样式:second": "Set tabs category attribute: first style: second", "数据表 的 列管理": "Data table column management", "是否选择checked 字段:field 类型:dataType 名称:name": "Whether to select checked field: field type: dataType name: name", "添加列表单": "Add list", "数据设置": "Data settings", "筛选条件个数": "Number of filter conditions", "筛选条件field symbol condition": "filter conditionfield symbol condition", "排序规则": "Sorting rules", "筛选字段 数量": "Filter field quantity", "筛选字段": "Filter fields", "字段查询范围": "Field query range", "表格样式设置": "Table style settings", "是否 斑马纹": "Whether zebra print", "是否 边框": "Whether border", "是否显示表头": "Whether to display header", "表格高度 固定高度": "Table height fixed height", "Table 的尺寸 medium / small / mini": "Table size medium / small / mini", "列数据": "column data", "分页设置": "Pagination settings", "是否启用分页": "Whether to enable paging", "是否使用小型分页样式": "Whether to use small pagination style", "是否为分页按钮添加背景色": "Whether to add a background color to the paging button", "只有一页时是否隐藏": "Whether to hide when there is only one page", "设置最大页码按钮数 默认7": "Set the maximum number of page number buttons, default 7", "每页显示个数": "Display number per page", "位置": "Location", "自定义列按钮": "Custom column buttons", "lable 狂顶": "lable Crazy support", "表单 组件": "form component", "label: '固定时间点选择器',": "label: 'Fixed time point selector',", "表单 组件 类型": "form component type", "// 自定义列设置": "// Custom column settings", "columnAttribute: '', //自定义列属性": "columnAttribute: '', //custom column attributes", "columnTitle: '', //自定义列标题": "columnTitle: '', //custom column title", "btnList: [], //按钮列表": "btnList: [], //Button list", "displayColumn: false,//显示列": "displayColumn: false,//display column", "fixedColumn: true,//固定列": "fixedColumn: true,//Fixed column", "// 列属性设置": "//Column attribute settings", "columnTitle: '',//列标题": "columnTitle: '',//column title", "boundField: '',//绑定字段": "boundField: '',//Binding field", "displayContent: '',//展示内容": "displayContent: '',//display content", "dataFormat: '',//数据格式": "dataFormat: '',//data format", "colWidth: '',//列宽": "colWidth: '',//column width", "alignment: '',//对齐方式": "alignment: '',//alignment", "sortEnable: false,//允许排序": "sortEnable: false,//Allow sorting", "exportEnable: false,//允许导出": "exportEnable: false,//Allow export", "editEnable: false,//允许编辑": "editEnable: false,//Allow editing", "deleteEnable: false,//允许删除": "deleteEnable: false,//Allow deletion", "// 按钮设置": "// Button settings", "按钮名称": "Button name", "按钮属性": "Button properties", "按钮类型": "button type", "禁用": "Disable", "带入id": "Bring in id", "自定义id名称": "Custom id name", "事件设置": "Event settings", "按钮事件": "Button event", "组件名称": "Component name", "执行方法": "Execution method", "选择交互": "Select interaction", "选择页面": "Select page", "选择数据源": "Select data source", "//表单属性设置": "//Form attribute settings", "selectComponent: '',//选择组件": "selectComponent: '',//Select component", "fieldName: '',//字段名称": "fieldName: '',//Field name", "tipText: '',//提示文字": "tipText: '',//prompt text", "defaultValue: '',//默认值": "defaultValue: '',//Default value", "formType: '1',//类型": "formType: '1',//type", "mustFillin: '',//必填": "mustFillin: '',//required", "notReuse: '',//不允许重复输入": "notReuse: '',//Do not allow repeated input", "finiteWord: '',//限制字数": "finiteWord: '',//Limit the number of words", "maxWord: '',//最大字数": "maxWord: '',//Maximum number of words", "miniWord: '',//最小字数": "miniWord: '',//minimum number of words", "finiteFormat: '',//限定输入格式": "finiteFormat: '',//limited input format", "size: '1/4',//表单尺寸": "size: '1/4',//form size", "displayUsage: '',//显示方式": "displayUsage: '',//Display mode", "dateType: '',//日期类型": "dateType: '',//Date type", "timeType: '',//时间类型": "timeType: '',//time type", "dateFormat: '',//日期格式": "dateFormat: '',//Date format", "readOnly: '',//只读": "readOnly: '',//read only", "conCeal: '',//隐藏": "conCeal: '',//Hide", "optionList: [], //选项列表": "optionList: [], //option list", "customData: '',//自定义数据集": "customData: '',//custom data set", "table | form 的数据表和列 配置": "table | form data table and column configuration", "选择组件 后变化后修改对应的": "Select the component and modify the corresponding", "当前组件": "current component", "处理数据": "Process data", "table 组件 增加模拟数据": "table component adds simulation data", "table列|form列 选择侧边 配置": "table column|form column select side configuration", "选择组件 和table列 后变化后修改对应的": "Select the component and table column and modify the corresponding ones after changing them.", "table 组件 )": "table component)", "formStyleForm 表单样式": "formStyleForm form style", "添加页面 点击事件监听": "Add page click event monitoring", "移除页面 点击事件监听": "Remove page click event listener", "编辑区域 组件占位显隐判断": "Edit area component occupancy display and concealment judgment", "侧边 添加组件按钮 禁用判断": "Side Add component button Disable judgment", "设置菜单和组件列表的值": "Set values ​​for menus and component lists", "修改对应组件": "Modify the corresponding component", "当前菜单": "Current menu", "修改对应菜单": "Modify the corresponding menu", "处理 Subsection 数据更新": "Handling Subsection data updates", "目前 column选中时 subsection 不切换": "Currently, subsection does not switch when column is selected.", "拖拽修改排序 开始": "Drag and drop to modify sorting start", "拖拽修改排序 结束": "Drag and drop to modify sorting End", "切换位置": "Switch location", "排序变化后目标对象的索引变成源对象的索引": "After the sorting change, the index of the target object becomes the index of the source object.", "拖拽修改排序 移动": "Drag and drop to modify sorting and move", "处理 选中组件事件": "Handle selected component events", "重置 中间|右侧 配置": "Reset center|right configuration", "数据回显": "Data echo", "列管理": "column management", "提交按钮": "submit button", "提交按钮事件": "submit button event", "属性:first 样式:second": "Attribute: first Style: second", "添加 组件": "Add component", "tabs 类别切换": "tabs category switching", "添加:first 设置:second": "Add:first Set:second", "添加 组件 数据类型": "Add component data type", "修改对应菜单 数据": "Modify corresponding menu data", "重置各项 配置": "Reset various configurations", "组件区域 拖拽 开始 index": "Component area drag start index", "组件区域 拖拽 结束 index": "Component area drag end index", "重置 table this.$options.data.call(this)": "Reset table this.$options.data.call(this)", "添加列": "Add column", "自定义列添加按钮": "Custom column add button", "重置 form": "reset form", "代码查看": "code view", "预览": "Preview", "发布": "release", "布局名称 编辑": "Layout name edit", "布局名称 输入框 聚焦": "Layout name input box focus", "布局名称 失去焦点": "Layout name loses focus", "新增菜单": "New menu", "切换菜单": "Toggle menu", "选中的菜单": "selected menu", "选中的组件": "selected component", "组件 选中类型": "Component selected type", "切换 tabs 类别": "Switch tabs category", "修改菜单名称": "Modify menu name", "菜单名称 输入框 聚焦": "Menu name input box focus", "修改菜单名称 失去焦点": "Modify menu name and lose focus", "table 组件 renderHeader": "table component renderHeader", "添加 用于显示表头 label": "Add to display header label", "添加一个空标签用于显示拖动动画": "Add an empty label to display the drag animation", "table 组件 列 鼠标按下": "table component column mouse click", "数据 回显": "Data echo", "tabs 切换": "tabs switch", "table 组件 选中 heade列样式": "table component selects header column style", "表单 item 列 鼠标按下": "Form item column mouse click", "重置 配置": "reset configuration", "选中列": "Select column", "选中组件类型": "Select component type", "切换 tabs": "switch tabs", "没有选中的菜单则禁用": "Unselected menus are disabled", "右侧Tabs 整体组件 添加 设置": "Tabs overall component on the right Add settings", "确认添加 列": "Confirm to add column", "自定义列 增加按钮列表": "Custom column Add button list", "重置添加列": "Reset added columns", "选择组件类型为 table 时": "When the component type is selected as table", "列属性": "Column properties", "列标题": "Column header", "绑定字段": "Bind field", "展示内容": "Show content", "数据格式": "Data format", "列宽": "column width", "对齐方式": "Alignment", "允许排序": "Allow sorting", "允许导出": "Allow export", "允许编辑": "Allow editing", "允许删除": "Allow deletion", "选择组件类型为 form 时 设置基础信息": "When selecting the component type as form, set basic information", "表单 组件类别 默认是input switch select time date checkbox radio": "Form component category default is input switch select time date checkbox radio", "组件类型": "Component type", "值 可设置默认值": "Value can set default value", "添加列 表单 单个组件类型切换处理": "Add list form Single component type switching processing", "form 选择": "form selection", "列 table 全选": "Column table select all", "列 table 选择": "Column table selection", "添加 筛选条件": "Add filter", "删除 筛选条件": "Delete filter", "添加 排序规则": "Add sorting rules", "删除 排序规则": "Delete sorting rule", "筛选器 table 全选": "filter table select all", "筛选器 table 选择": "filter table selection", "分页 全选": "Pagination Select all", "分页 选择": "Pagination Select", "table 自定义列 添加按钮": "table custom column add button", "table 自定义列 删除按钮": "table custom column delete button", "table 自定义列 按钮(选中) 侧边属性编辑": "table custom column button (selected) side attribute editing", "form 列 添加 optionData": "form column add optionData", "form 列 删除 optionData": "form column delete optionData", "form 提交 按钮": "form submit button", "//切换宽度占比": "//Switch width ratio", "// 添加 按钮列表": "//Add button list", "// 删除 按钮列表": "//Delete button list", "当前选中的菜单": "Currently selected menu", "用于前端显示的属性": "Properties used for front-end display", "[Table(Register.DATABASE_PREFIX + nameof(NcfFile))]//必须添加前缀,防止全系统中发生冲突": "[Table(Register.DATABASE_PREFIX + nameof(NcfFile))]//A prefix must be added to prevent conflicts in the entire system", "Guid 格式的文件名": "File name in Guid format", "相对路径,例如:App_Data/NcfFiles/yyyy/MM/": "Relative path, for example: App_Data/NcfFiles/yyyy/MM/", "文件大小(字节)": "File size (bytes)", "文件扩展名": "file extension", "文件类型枚举": "file type enum", "文件描述": "File description", "上传时间": "Upload time", "所属文件夹,可为空表示根目录": "The folder to which it belongs. It can be empty to indicate the root directory.", "新增文件夹实体": "Add new folder entity", "父级文件夹,null 为根": "Parent folder, null is the root", "/ 2、运行:PM> add-migration [更新名称] -c FileManagerSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ": "/ 2. Run: PM> add-migration [update name] -c FileManagerSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ", "/ 2、运行:PM> add-migration [更新名称] -c FileManagerSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ": "/ 2. Run: PM> add-migration [update name] -c FileManagerSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ", "/ 2、运行:PM> add-migration [更新名称] -c FileManagerSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ": "/ 2. Run: PM> add-migration [update name] -c FileManagerSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ", "/ 2、运行:PM> add-migration [更新名称] -c FileManagerSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ": "/ 2. Run: PM> add-migration [update name] -c FileManagerSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ", "/ 2、运行:PM> add-migration [更新名称] -c FileManagerSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ": "/ 2. Run: PM> add-migration [update name] -c FileManagerSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ", "/ 2、运行:PM> add-migration [更新名称] -c FileManagerSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ": "/ 2. Run: PM> add-migration [update name] -c FileManagerSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ", "/ 文件存储的基础路径": "/ base path for file storage", "列表(支持按文件夹过滤)": "List (supports filtering by folder)", "/ 删除文件(同时删除数据库记录与物理文件)": "/ Delete files (delete database records and physical files at the same time)", "获取CSRF Token": "Get CSRF Token", "获取文件列表": "Get file list", "格式化文件大小": "Format file size", "日期格式化": "date formatting", "分页处理": "Pagination", "上传文件": "Upload files", "上传成功处理": "Upload successfully processed", "上传失败处理": "Upload failure handling", "编辑备注": "Editor's Notes", "提交编辑备注": "Submit Editor's Note", "删除文件": "Delete files", "筛选按钮": "filter button", "编辑、新增": "Edit, add", "/ 允许扩展名类": "/ Allow extension classes", "/ 图片扩展名": "/image extension", "/ 音视频扩展名": "/ audio and video extension", "/ 文件扩展名": "/ file extension", "/ 上传根目录,可用软链接分离": "/ Upload root directory, can be separated by soft link", "/ 请求根目录": "/request root directory", "/ 上传文件大小,单位:MB": "/ Upload file size, unit: MB", "/ 访问的域名": "/Domain name visited", "/ 允许上传的文件": "/ Allow uploaded files", "/ 训练模型Id": "/Training model ID", "/ 向量数据库Id": "/Vector databaseId", "/ 对话模型Id": "/ Dialog model ID", "/ 内容": "/ content", "/ 知识库Id": "/Knowledge BaseId", "/ 内容类型": "/ content type", "/ 源文件名称": "/ source file name", "/ 文本切片索引": "/text slice index", "/ KnowledgeBase 实体类": "/ KnowledgeBase entity class", "/ KnowledgeBasesDetail 实体类": "/ KnowledgeBasesDetail entity class", "/ 文本切片索引总数": "/Total number of text slice indexes", "/ 是否已向量化": "/ Whether it has been vectorized", "/ 向量化时间": "/vectorization time", "/ 向量化成功后调用,更新状态和时间戳": "/ Called after successful vectorization, update status and timestamp", "/ 2、运行:PM> add-migration [更新名称] -c KnowledgeBaseSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ": "/ 2. Run: PM> add-migration [update name] -c KnowledgeBaseSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ", "/ 2、运行:PM> add-migration [更新名称] -c KnowledgeBaseSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ": "/ 2. Run: PM> add-migration [update name] -c KnowledgeBaseSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ", "/ 2、运行:PM> add-migration [更新名称] -c KnowledgeBaseSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ": "/ 2. Run: PM> add-migration [update name] -c KnowledgeBaseSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ", "/ 2、运行:PM> add-migration [更新名称] -c KnowledgeBaseSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ": "/ 2. Run: PM> add-migration [update name] -c KnowledgeBaseSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ", "/ 2、运行:PM> add-migration [更新名称] -c KnowledgeBaseSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ": "/ 2. Run: PM> add-migration [update name] -c KnowledgeBaseSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ", "/ 2、运行:PM> add-migration [更新名称] -c KnowledgeBaseSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ": "/ 2. Run: PM> add-migration [update name] -c KnowledgeBaseSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ", "新增": "New", "增加文件": "add file", "编辑": "edit", "/ 批量将文件添加到知识库(读取、切片、保存详情)": "/ Add files to the knowledge base in batches (read, slice, save details)", "/ 总切片数": "/ Total number of slices", "/ 将文件添加到知识库(读取、切片、保存详情)": "/ Add file to knowledge base (read, slice, save details)", "/ 切片数": "/ Number of slices", "1. 获取文件信息": "1. Get file information", "2. 读取文件内容": "2. Read the file content", "构造物理路径: App_Data/NcfFiles/{Year}/{Month}/{StorageName}{Ext}": "Construct the physical path: App_Data/NcfFiles/{Year}/{Month}/{StorageName}{Ext}", "简单处理:目前只支持文本文件读取": "Simple processing: currently only supports text file reading", "TODO: 后续支持 PDF, Word 等格式解析": "TODO: Support PDF, Word and other format parsing in the future", "3. 文本切片": "3. Text slicing", "默认 chunk size 500, overlap 100": "Default chunk size 500, overlap 100", "4. 保存切片到 KnowledgeBasesDetail": "4. Save slices to KnowledgeBasesDetail", "记录源文件名": "Record source file name", "记录切片索引": "Record slice index", "/ 对知识库进行向量化(Embedding)": "/ Vectorize the knowledge base (Embedding)", "/ 当前 Embedding 记录的 Tag": "/ Tag of the current Embedding record", "1. 检查配置": "1. Check configuration", "2. 获取AI Model配置": "2. Get AI Model configuration", "3. 获取待向量化的文本切片(未向量化的数据)": "3. Get the text slice to be vectorized (unvectorized data)", "TODO:改成动态": "TODO: Change to dynamic", "4. 初始化 SemanticAiHandler": "4. Initialize SemanticAiHandler", "throw new NcfExceptionBase(\"SemanticAiHandler 服务未注册。\");": "throw new NcfExceptionBase(\"SemanticAiHandler service is not registered.\");", "5. 构建 IWantToRun (Embedding 模式)": "5. Build IWantToRun (Embedding mode)", "6. 批量生成 Embeddings 并存储": "6. Generate Embeddings in batches and store them", "进行切片": "slice", "SenparcTrace.SendCustomLog(\"知识库\", $\"知识库 '{knowledgeBase.Name}' 没有待向量化的文本切片。现在开始切片\");": "SenparcTrace.SendCustomLog(\"Knowledge Base\", $\"Knowledge base '{knowledgeBase.Name}' has no text slices to vectorize. Start slicing now\");", "从关联的文件中获取内容进行切片 //TODO: 判断 ContentType 来决定如何处理不同类型的内容": "Get the content from the associated file for slicing //TODO: Judge the ContentType to decide how to process different types of content", "ReadOnlyMemory searchVector = await iWantToRunEmbedding.SemanticKernelHelper.GetEmbeddingAsync(embeddingModelName, \"什么是NCF?\");": "ReadOnlyMemory searchVector = await iWantToRunEmbedding.SemanticKernelHelper.GetEmbeddingAsync(embeddingModelName, \"What is NCF?\");", "Console.WriteLine($\"得到结果:{item.Record.ToJson(true)}\");": "Console.WriteLine($\"Get the result: {item.Record.ToJson(true)}\");", "错误计数自增": "Error count increments", "/ 召回测试(Embedding)": "/ Recall Test (Embedding)", "/ 简单的文本切片算法": "/ Simple text slicing algorithm", "简单按字符数切分,后续可以优化为按 Token 或段落切分": "Simply split by the number of characters, and can be optimized to split by token or paragraph later.", "防止死循环(如果 overlap >= chunkSize)": "Prevent infinite loops (if overlap >= chunkSize)", "根据模型调整,例如 text-embedding-ada-002 为 1536,Large 为 3072": "Adjust according to the model, for example text-embedding-ada-002 is 1536, Large is 3072", "/ 根据知识库ID获取关联的 KnowledgeBaseItem 列表(用于配置页回显)": "/ Get the associated KnowledgeBaseItem list based on the knowledge base ID (for configuration page echo)", "/ 上传": "/upload", "/ 创建及修改": "/Create and modify", "/ 请求记录Dto模型": "/ Request to record Dto model", "/ 创建或设置 KnowledgeBaseDetail": "/ Create or set KnowledgeBaseDetail", "/ 对KnowledgeBase进行Embedding,返回向量化结果描述(供前端展示)": "/ Embedding KnowledgeBase and returning vectorized result description (for front-end display)", "/ 批量导入文件到知识库": "/ Batch import files into the knowledge base", "/ 根据知识库ID获取关联的条目列表(用于配置页回显:已关联文件、内容等)": "/ Get the associated entry list based on the knowledge base ID (used for configuration page echo: associated files, content, etc.)", "/ 召回测试": "/recall test", "/ 召回片段数量 Top K,默认 5,范围 1-20": "/ Number of recalled fragments Top K, default 5, range 1-20", "//静态资源允许跨域": "//Static resources allow cross-domain", "OnPrepareResponse = (x) =>//验证静态资源授权": "OnPrepareResponse = (x) =>//Verify static resource authorization", "//x.Context.Response.Headers.Add(\"Access-Control-Allow-Origin\", \"*\");//允许跨域,Core已做处理": "//x.Context.Response.Headers.Add(\"Access-Control-Allow-Origin\", \"*\");//Cross-domain allowed, Core has processed it", "考虑浏览器兼容性:兼容 Chrome, Safari, Opera": "Consider browser compatibility: Compatible with Chrome, Safari, Opera", "文件列表": "file list", "向量化进度弹窗": "Vectorized progress pop-up window", "向量化结果展示": "Display of vectorization results", "检索设置抽屉": "Retrieve settings drawer", "段落详情弹框": "Paragraph details pop-up box", "配置抽屉内「新建文件」上传弹框": "Configure the \"New File\" upload pop-up box in the drawer", "向量化结果展示弹窗": "Vectorization result display pop-up window", "新建文件弹框内的上传列表(仅展示用,关闭时清空)": "Upload list in the new file pop-up box (for display only, cleared when closed)", "分页参数": "Paging parameters", "分页接口传参": "Paging interface parameter passing", "表格数据": "tabular data", "是否严格的遵守父子节点不互相关联": "Whether the parent and child nodes are strictly observed and not related to each other", "组 新增|编辑(内容类型:1=输入 2=文件 3=采集外部数据,默认文件)": "Group New|Edit (Content type: 1=Input 2=File 3=Collect external data, default file)", "内容类型,默认「文件」": "Content type, default \"File\"", "内容": "content", "知识库ID": "Knowledge base ID", "配置页文件列表查询与分页": "Configuration page file list query and paging", "文件名称筛选": "File name filter", "文件列表总条数,用于分页": "The total number of files in the list, used for paging", "配置页文件列表": "Configuration page file list", "打开配置时待选中的文件名(从 KnowledgeBaseItem 回显用)": "The file name to be selected when opening the configuration (echoed from KnowledgeBaseItem)", "获取文件数据": "Get file data", "TODO:初始化设置选中的字段": "TODO: Initialize the selected fields", "关闭dialog,清空": "Close the dialog and clear it", "上传成功": "Upload successful", "清空文件列表(关键)": "Clear file list (key)", "配置抽屉「新建文件」弹框:上传成功后将文件写入 FileManage,并加入当前已选列表、刷新文件列表并勾选新文件": "Configure the drawer \"New File\" pop-up box: After the upload is successful, write the file to FileManage, add it to the currently selected list, refresh the file list, and check the new file", "获取 文件 数据(支持分页与筛选)": "Get file data (supports paging and filtering)", "配置页文件列表:页码变化(首页/上一页/下一页/尾页/跳转)": "Configuration page file list: page number changes (first page/previous page/next page/last page/jump)", "配置页文件列表:每页条数变化": "Configuration page file list: changes in the number of items per page", "打开配置时:根据当前知识库拉取 KnowledgeBaseItem 关联项,回填文件选中或内容": "When opening the configuration: Pull KnowledgeBaseItem related items based on the current knowledge base, and backfill the file selection or content", "获取列表": "Get list", "使用 map 转换为目标格式的对象数组": "Convert to an array of objects in the target format using map", "获取分类列表数据": "Get category list data", "编辑 // 新增知识库管理(文件在配置中上传,此处不再使用文件列表)": "Edit // Added knowledge base management (files are uploaded in the configuration, the file list is no longer used here)", "新增 - 初始化空数据": "New - Initialize empty data", "编辑 - 使用现有数据": "Edit - use existing data", "设置下拉框默认值": "Set the default value of the drop-down box", "设置父级菜单默认显示 递归": "Set the default display of the parent menu recursively", "保存 submitForm 数据(配置抽屉确认:内容类型为文件时导入选中文件,否则不校验文件)": "Save submitForm data (Configure drawer confirmation: import the selected file when the content type is a file, otherwise the file will not be verified)", "2=文件": "2=File", "内容类型为「输入」或「采集外部数据」时,不校验文件,直接关闭": "When the content type is \"Input\" or \"Collect external data\", the file will not be verified and will be closed directly.", "更新新增、编辑(文件改为在「配置」中上传并关联,此处不再传文件)": "Update, add, edit (files are uploaded and associated in \"Configuration\" instead, files are no longer uploaded here)", "res.data 是后端返回的对象:{success: true, data: true, msg: \"保存成功\"}": "res.data is the object returned by the backend: {success: true, data: true, msg: \"Save successfully\"}", "删除": "delete", "配置抽屉内「文件列表」表格勾选变化:同步到 groupForm.files,保存时用于关联知识库": "Changes in the checkbox of the \"File List\" table in the configuration drawer: synchronized to groupForm.files, used to associate with the knowledge base when saving", "配置页文件列表:按文件名称搜索(调用接口过滤,并回到第一页)": "Configuration page file list: search by file name (call the interface to filter and return to the first page)", "配置页文件列表:删除文件(删除数据库与物理文件,并刷新列表与已选)": "Configuration page file list: delete files (delete database and physical files, and refresh the list and selected)", "配置页文件列表:批量删除(删除当前选中的多行,并同步数据库与物理文件)": "Configuration page file list: batch deletion (delete the currently selected multiple rows and synchronize the database and physical files)", "获取选中数据": "Get selected data", "// 重置 组获取智能体query": "//Reset group acquisition agent query", "配置抽屉「已选择」文件列表中移除某一项,并同步表格勾选": "Remove an item from the \"Selected\" file list of the configuration drawer and synchronize the table check", "编辑 // 新增知识库管理详情 // 增加下一级": "Edit // Add new knowledge base management details // Add next level", "dialog中父级菜单 做递归显示": "Recursive display of parent menu in dialog", "更新新增、编辑": "Update, add, edit", "表单校验": "form validation", "{ score: '0.95', content: (that.recallContent || '') + ' [召回示例1]', recallTime: timeStr },": "{ score: '0.95', content: (that.recallContent || '') + ' [Recall Example 1]', recallTime: timeStr },", "{ score: '0.88', content: (that.recallContent || '') + ' [召回示例2]', recallTime: timeStr },": "{ score: '0.88', content: (that.recallContent || '') + ' [Recall Example 2]', recallTime: timeStr },", "{ score: '0.72', content: (that.recallContent || '') + ' [召回示例3]', recallTime: timeStr }": "{ score: '0.72', content: (that.recallContent || '') + ' [Recall Example 3]', recallTime: timeStr }", "/ MCP Endpoint 配置": "/MCP Endpoint Configuration", "/ 存储可用的 MCP 端点信息,供 Agent 使用": "/ Stores available MCP endpoint information for use by Agent", "/ 端点名称": "/ endpoint name", "/ 端点 URI/路径": "/endpoint_uri/path", "/ 例如: \"http://localhost:3001/mcp/endpoint\"": "/ For example: \"http://localhost:3001/mcp/endpoint\"", "/ 或 \"sse://...\" 或其他 MCP 协议支持的格式": "/ or \"sse://...\" or other format supported by MCP protocol", "/ 端点类型": "/ endpoint type", "/ 例如: \"http\", \"sse\", \"stdio\", \"websocket\"": "/ Example: \"http\", \"sse\", \"stdio\", \"websocket\"", "/ 协议版本": "/ protocol version", "/ 例如: \"1.0\", \"2.0\"": "/Example: \"1.0\", \"2.0\"", "/ 描述信息": "/description information", "/ 认证信息 JSON": "/Authentication information JSON", "/ 用于存储认证相关的配置,如 API Key、Token 等": "/ Used to store authentication-related configurations, such as API Key, Token, etc.", "/ 额外参数 JSON": "/Extra parameters JSON", "/ 用于存储其他自定义参数": "/ is used to store other custom parameters", "/ 最后测试时间": "/last test time", "/ 最后测试结果": "/Last test results", "/ true: 成功, false: 失败": "/ true: success, false: failure", "/ 2、运行:PM> add-migration [更新名称] -c MCPSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ": "/ 2. Run: PM> add-migration [update name] -c MCPSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ", "/ 2、运行:PM> add-migration [更新名称] -c MCPSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ": "/ 2. Run: PM> add-migration [update name] -c MCPSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ", "/ 2、运行:PM> add-migration [更新名称] -c MCPSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ": "/ 2. Run: PM> add-migration [update name] -c MCPSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ", "/ 2、运行:PM> add-migration [更新名称] -c MCPSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ": "/ 2. Run: PM> add-migration [update name] -c MCPSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ", "/ 2、运行:PM> add-migration [更新名称] -c MCPSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ": "/ 2. Run: PM> add-migration [update name] -c MCPSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ", "/ 2、运行:PM> add-migration [更新名称] -c MCPSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ": "/ 2. Run: PM> add-migration [update name] -c MCPSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ", "/ MCP Endpoint 管理服务": "/MCP Endpoint Management Service", "/ 获取所有已启用的 MCP Endpoints": "/ Get all enabled MCP Endpoints", "/ 根据名称获取端点": "/ Get the endpoint by name", "/ 根据 Endpoint 地址获取端点": "/ Get the endpoint based on the Endpoint address", "/ 测试端点连接": "/Test endpoint connection", "TODO: 实现实际的端点连接测试逻辑": "TODO: Implement actual endpoint connection test logic", "这里应该根据 EndpointType 调用相应的测试方法": "The corresponding test method should be called based on EndpointType.", "/ NCF 内置启动的 MCP 服务(Server)": "/NCF built-in started MCP service (Server)", "TODO: 找一个没有被用过的端口": "TODO: Find an unused port", "防止重复启动": "Prevent repeated starts", "启用路由支持": "Enable routing support", "配置端点,包括 \"/\"": "Configure the endpoint, including \"/\"", "检查 Token": "Check Token", "/ MCP Endpoint 管理 AppService": "/MCP Endpoint Management AppService", "/ 提供 MCP 端点的增删改查以及测试功能": "/ Provides the addition, deletion, modification, checking and testing functions of MCP endpoints", "/ 获取所有 MCP Endpoints": "/ Get all MCP Endpoints", "/ 创建/编辑 MCP Endpoint": "/Create/Edit MCP Endpoint", "验证输入": "Validate input", "检查名称是否已存在(编辑时除外)": "Check if the name already exists (except when editing)", "编辑现有端点": "Edit existing endpoint", "创建新端点": "Create new endpoint", "更新属性": "Update properties", "/ 删除 MCP Endpoint": "/ Delete MCP Endpoint", "/ 测试 MCP Endpoint": "/Test MCP Endpoint", "/ 创建或编辑 MCP Endpoint 请求": "/Create or edit MCP Endpoint requests", "/ 删除 MCP Endpoint 请求": "/ Delete MCP Endpoint request", "/ 测试 MCP Endpoint 请求": "/Test MCP Endpoint request", "[McpServerTool, Description(\"获取当前时间\")]": "[McpServerTool, Description(\"Get the current time\")]", "Console.WriteLine(\"Now tool 收到请求:\" + reqeust.ToJson());": "Console.WriteLine(\"Now tool received request: \" + reqeust.ToJson());", "自动增加小时数": "Automatically add hours", "根据 MCP 服务器选择来确定端点": "Determine endpoints based on MCP server selection", "如果选中了非\"手动输入\"的 MCP 服务器,从注册列表中获取真实地址": "If an MCP server other than \"Manual Entry\" is selected, get the real address from the registration list", "从 XncfRegisterManager 中查找对应的服务器信息": "Find the corresponding server information from XncfRegisterManager", "构建完整的服务器地址": "Build the complete server address", "如果找不到对应的服务器信息,回退到默认地址": "If the corresponding server information cannot be found, fall back to the default address.", "如果解析失败,回退到默认地址": "If parsing fails, fall back to the default address", "如果选中了\"手动输入\"或没有选择,使用手动输入的端点": "If \"Manual input\" is checked or not selected, the manually entered endpoint is used", "Console.WriteLine(\"MCP 收到结果:\" + response.ToJson(true));": "Console.WriteLine(\"MCP received the result: \" + response.ToJson(true));", "页面上点击\"执行\"后,将调用这里的方法": "After clicking \"Execute\" on the page, the method here will be called", "response.ErrorMessage = \"被除数不能为0!\";": "response.ErrorMessage = \"The dividend cannot be 0!\";", "response.ErrorMessage = $\"未知的运算符:{theOperator}\";": "response.ErrorMessage = $\"Unknown operator: {theOperator}\";", "logger.Append($\"进行运算:{number1} {theOperator} {number2} = {calcResult}\");": "logger.Append($\"Perform operation: {number1} {theOperator} {number2} = {calcResult}\");", "logger.Append($\"进行{power}次方运算:{oldValue}{(power == 2 ? \"²\" : \"³\")} = {calcResult}\");": "logger.Append($\"Perform {power} power operation: {oldValue}{(power == 2 ? \"²\" : \"³\")} = {calcResult}\");", "response.Data = $\"【{theOperator}】计算结果:{calcResult}。计算过程请看日志\";": "response.Data = $\"[{theOperator}] calculation result: {calcResult}. Please see the log for the calculation process\";", "添加手动输入选项": "Add manual input option", "从 XncfRegisterManager 获取已注册的 MCP 服务器": "Get registered MCP servers from XncfRegisterManager", "使用服务器的唯一标识作为 Value,而不是路由": "Use the server's unique identifier as Value, not the route", "Console.WriteLine(\"开始启用 MCP 服务(全局)\");": "Console.WriteLine(\"Start enabling MCP service (global)\");", "//恢复首页": "//Restore home page", "// 从请求头获取 Token": "// Get Token from request header", "//放置 NCF-MCP-Server SSE": "//Place NCF-MCP-Server SSE", "// 获取配置的 Token": "// Get the configured Token", "/ LLM 模型管理 AppService": "/ LLM Model Management AppService", "/ 添加模型": "/Add model", "/ 编辑模型": "/edit model", "/ 获取model": "/ Get model", "/ 模型删除": "/Model delete", "/ PromptItem 管理 AppService": "/PromptItem Management AppService", "/ Add方法用于添加一个新的PromptItem,并根据请求中的IsDraft字段决定是否立即生成结果": "The /Add method is used to add a new PromptItem and decide whether to generate the result immediately based on the IsDraft field in the request", "新增promptItem": "Add promptItem", "?? throw new NcfExceptionBase(\"新增失败\");": "?? throw new NcfExceptionBase(\"Add failed\");", "是否立即生成结果": "Whether to generate results immediately", "如果立即生成,就根据numsOfResults立即生成": "If it is generated immediately, it will be generated immediately according to numsOfResults", "转换历史记录格式": "Convert history format", "连发时,如果第一个结果是 Chat 模式,后续结果也需要保持 Chat 模式": "When sending continuously, if the first result is in Chat mode, subsequent results also need to remain in Chat mode.", "获取第一个结果的模式,用于后续结果保持一致": "Get the pattern of the first result to keep subsequent results consistent", "如果是第一次生成,使用传入的参数": "If it is generated for the first time, use the parameters passed in", "如果是后续生成,且第一个结果是 Chat 模式,则保持 Chat 模式": "If it is a subsequent build and the first result is in Chat mode, stay in Chat mode", "第一次生成,使用传入的参数": "The first generation, using the parameters passed in", "后续生成,且第一个结果是 Chat 模式,保持 Chat 模式": "Subsequent generation, and the first result is in Chat mode, remains in Chat mode", "使用相同的 userMessage,但不传递历史记录(每次都是独立的对话)": "Use the same userMessage but don't pass the history (a separate conversation each time)", "连发时,每次都是独立的对话,不传递历史记录": "When sending continuously, each session is an independent conversation, and no history records are transferred.", "如果第一个结果是 Single 模式,后续也使用 Single 模式(currentUserMessage 为 null)": "If the first result is Single mode, subsequent Single modes are also used (currentUserMessage is null)", "分别生成结果": "Generate results separately", "记录第一个结果的模式": "Pattern to record the first result", "/ 列出 靶场名称 下所有的promptItem的id和name": "/ List the ids and names of all promptItems under the shooting range name", "/ 靶场名称(必须)": "/ Range name (required)", "/ 根据靶场 ID, 查询其中所有的promptItem的id和name": "/ Based on the shooting range ID, query the id and name of all promptItems in it", "/ 靶场 ID(必须)": "/ Range ID (required)", "/ 列出所有的promptItem的RangeName": "/ List the RangeName of all promptItems", "/// 根据ID,找到对应的promptItem的所有父级的信息": "/// Based on the ID, find the information of all parents of the corresponding promptItem", "// 根据promptItemId找到promptItem, 然后获取version": "// Find promptItem based on promptItemId, and then get the version", "/ 根据主键 ID 获取 PromptItem 所有信息": "/ Get all information of PromptItem based on primary key ID", "/ 主键 ID ": "/ Primary key ID ", "获取promptItem": "Get promptItem", "转换为 response": "convert to response", "获取所有对应的结果": "Get all corresponding results", "/// 根据 完整版号 获取 PromptItem 所有信息": "/// Get all the information of PromptItem based on the complete version number", "/// 完整版号": "/// Full version number", "/ 获取版本树": "/ Get version tree", "/ 靶场名称": "/ Range name", "根据 request 中的字段,对应修改": "According to the fields in the request, modify the corresponding", "删除其他同名的 PromptItem": "Delete other PromptItems with the same name", "修改当前名称(如果是 \"\",则清空)": "Modify the current name (if it is \"\", clear it)", "/ 根据主键 ID 删除 PromptItem 所有信息": "/ Delete all information of PromptItem based on primary key ID", "关联删除所有子战术": "Delete all sub-tactics associated with them", "/ 设置 AI 自动打分评分标准接口": "/Set AI automatic scoring standard interface", "/// 根据靶场名(自动生成)获取靶场里最好的promptItem": "/// Get the best promptItem in the shooting range based on the shooting range name (automatically generated)", "/ 上传plugin接口": "/Upload plugin interface", "/ 导出靶场为 plugin": "/ Export range as plugin", "/// 导出指定版本的靶道为 plugin": "/// Export the specified version of the target as plugin", "验证目录是否存在": "Verify directory exists", "验证目录中是否有文件": "Verify that there is a file in the directory", "创建压缩文件": "Create compressed file", "清理临时文件": "Clean temporary files", "删除 zip 文件": "Delete zip file", "清理临时文件夹": "Clean temporary folder", "清理失败不影响返回结果,只记录日志": "Failure to clean up does not affect the returned results, only logs are recorded.", "确保在出错时也清理临时文件": "Make sure temporary files are also cleaned up on errors", "忽略清理错误": "Ignore cleanup errors", "/ PromptRange 管理 AppService": "/PromptRange Management AppService", "/// 设置 AI 自动打分评分标准接口": "///Set the AI ​​automatic scoring standard interface", "/ 获取靶场列表详情(添加时间倒序)": "/ Get shooting range list details (add reverse chronological order)", "/ 修改自定义靶场代号": "/ Modify custom shooting range code", "/ 根据 ID 删除靶场": "/ Delete range based on ID", "/ PromptResult 管理 AppService": "/PromptResult Management AppService", "/ 手动打分": "/ Manual scoring", "更新绑定的 item 的分数": "Update the score of the bound item", "/ 自动打分": "/ automatic scoring", "/ 接受一个promptItemId,然后找到所有的promptResult,然后进行评分": "/ Accept a promptItemId, then find all promptResults, and then score", "/ 根据靶道ID获取对应的结果列表": "/ Get the corresponding result list based on the target channel ID", "/ 生成结果": "/ generate results", "/ 靶道ID": "/ Target ID", "/ 连发次数": "/ Number of bursts", "/ 用户消息(可选,如果为空且第一个结果是 Chat 模式,则从第一个结果的对话记录中获取)": "/ User message (optional, if empty and the first result is Chat mode, obtained from the conversation record of the first result)", "#region 删除之前的结果": "#region Delete previous results", "throw new NcfExceptionBase(\"删除失败\");": "throw new NcfExceptionBase(\"Deletion failed\");", "如果没有传入 userMessage,先检查该 PromptItem 的第一个 PromptResult 是否是 Chat 模式": "If no userMessage is passed in, first check whether the first PromptResult of the PromptItem is in Chat mode.", "获取该 PromptItem 的第一个 PromptResult(按 ID 升序)": "Get the first PromptResult of this PromptItem (in ascending order by ID)", "第一个结果是 Chat 模式,从对话记录中获取第一条用户消息": "The first result is Chat mode, getting the first user message from the conversation record", "第一次生成时,chatHistory 应该为空": "The first time it is generated, chatHistory should be empty", "/ 根据 PromptResultId 获取对话记录": "/ Get the conversation record based on PromptResultId", "/ PromptResult 的 ID": "/ ID of PromptResult", "/ 根据 PromptResultId 获取对话历史和 Prompt 内容": "/ Get the conversation history and Prompt content based on PromptResultId", "获取对话历史": "Get conversation history", "获取 PromptResult": "GetPromptResult", "优先使用保存的 SystemMessage,如果没有则使用当前的 Prompt 内容": "The saved SystemMessage is used first, if not, the current Prompt content is used.", "使用保存的 SystemMessage(已完成参数替换)": "Use saved SystemMessage (parameter substitution done)", "降级方案:如果没有保存的 SystemMessage,使用当前的 Prompt 内容": "Downgrade scenario: If there is no saved SystemMessage, use the current Prompt content", "/ 继续聊天:在现有 PromptResult 中追加对话记录": "/ Continue chatting: Append the conversation record to the existing PromptResult", "/ 继续聊天请求": "/ Continue chat request", "/ 更新对话记录的用户反馈(Like/Unlike)": "/ Update user feedback of conversation records (Like/Unlike)", "/ 更新反馈请求": "/ Update feedback request", "/ 用于传送统计数据的接口服务": "/ Interface service for transmitting statistical data", "载入 PromptRange": "Load PromptRange", "/ 模型的类型(必须), 例如:OpenAI,Azure OpenAI,HuggingFace": "/ Model type (required), for example: OpenAI, Azure OpenAI, HuggingFace", "/ 温度": "/ temperature", "/ 最大 Token 数": "/Maximum number of Tokens", "/ 频率惩罚": "/ frequency penalty", "/ 连发次数": "/number of bursts", "/ 是否启用“ai评分标准”": "/ Whether to enable \"ai scoring criteria\"", "/ 对话模式下的用户消息(可选)": "/ User messages in conversation mode (optional)", "/ 继续聊天模式下的历史对话记录(可选)": "/ Continue historical conversation in chat mode (optional)", "/ 对话历史记录项": "/conversation history items", "/ 角色:'user' 或 'assistant'": "/ Role: 'user' or 'assistant'", "/ 消息内容": "/ message content", "/ 靶场代号(用户自定义)": "/ Shooting range code (user-defined)", "/ 靶场名称(来自版号生成)": "/ Shooting range name (from version number generation)", "/ 期望结果Json": "/Expected result Json", "/ 继续聊天请求": "/ continue chat request", "/ PromptResult 的 ID": "/PromptResult ID", "/ 用户消息": "/user messages", "/ 更新对话反馈请求": "/ Update conversation feedback request", "/ 对话记录 ID": "/Conversation record ID", "/ 反馈:Like(true)、Unlike(false)、取消反馈(null)": "/ Feedback: Like (true), Unlike (false), Cancel feedback (null)", "/ 用户名": "/ username", "/ 靶场 ID": "/shooting range ID", "/ 昵称": "/ Nick name", "/ 完整版本号": "/Full version number", "/ 停止序列(JSON 数组)": "/ stop sequence (JSON array)", "/ 评估参数, 平均分": "/ evaluation parameters, average score", "/ 评估参数": "/evaluation parameters", "/ 靶场名称": "/shooting range name", "/ 完整版号": "/Full version number", "/ 是否是草稿": "/ Is it a draft?", "/ 是否分享": "/ Whether to share", "/ 前缀": "/ prefix", "/ 后缀": "/ suffix", "/ 参数字典(JSON)": "/parameter dictionary (JSON)", "/ 对话历史和 Prompt 内容响应": "/Conversation history and prompt content response", "/ 对话历史记录": "/conversation history", "/ Prompt 内容(SystemMessage)": "/Prompt content(SystemMessage)", "=== 步骤1:确保 PromptRange \"PromptCatalyzer\" 存在 ===": "=== Step 1: Make sure PromptRange \"PromptCatalyzer\" exists ===", "=== 步骤2:确定使用哪个 AI Model ===": "=== Step 2: Determine which AI Model to use ===", "=== 步骤3:确保 PromptItem 存在(容错处理)===": "=== Step 3: Ensure PromptItem exists (fault tolerance) ===", "限制在 20 字符以内(数据库字段限制)": "Limited to 20 characters (database field limit)", "确保所有字符串字段都有值,避免 null 导致数据库错误": "Ensure all string fields have values ​​to avoid nulls causing database errors", "尝试再次查询,看看是否已经创建(可能是并发问题)": "Try querying again to see if it has been created (maybe a concurrency issue)", "真的失败了,重新抛出异常": "If it really fails, rethrow the exception", "=== 步骤4:返回成功响应 ===": "=== Step 4: Return successful response ===", "使用 PublishDerivedAsync 继承事件链信息(防止循环引用)": "Use PublishDerivedAsync to inherit event chain information (prevent circular references)", "捕获完整的异常信息,包括 inner exception": "Capture complete exception information, including inner exception", "如果还有更深层的 inner exception(例如 EF Core 的数据库错误)": "If there is a deeper inner exception (such as a database error in EF Core)", "即使是错误响应,也需要继承事件链": "Even error responses need to inherit the event chain", "/ 全局常量配置": "/ Global constant configuration", "Azure OpenAI API版本列表": "Azure OpenAI API version list", "/ ID 主键": "/ID primary key", "/ 名称(必须)": "/name (required)", "/ 模型的类型": "/Type of model", "/// TextCompletionModelName(可选)": "/// TextCompletionModelName (optional)", "/// TextEmbeddingModelName(可选)": "/// TextEmbeddingModelName (optional)", "/// OtherModelName(可选)": "/// OtherModelName (optional)", "/ Prompt内容": "/Prompt content", "/ 为打靶次数,int": "/ is the number of target shooting, int", "/ 父Tactic, 可以是空串": "/ Parent Tactic, can be an empty string", "/ 最后一次运行时间": "/Last run time", "/ 是否公开": "/ Is it public?", "/// 期望结果Json": "/// Expected result Json", "/ PromptResult 的 ID(外键)": "/ID of PromptResult (foreign key)", "/ 对话角色类型:User 或 Assistant": "/ Dialogue role type: User or Assistant", "/ 对话内容": "/Conversation content", "/ 对话顺序(在同一 PromptResult 中的顺序,从 1 开始)": "/ Conversation order (order within the same PromptResult, starting from 1)", "/ 用户反馈:Like(true)、Unlike(false)、未反馈(null)": "/ User feedback: Like (true), Unlike (false), No feedback (null)", "/ 用户评分(0-10分,可选)": "/ User rating (0-10 points, optional)", "/ 从实体创建 DTO": "/Create DTO from entity", "/ LlmModel 的 Id": "/Id of LlmModel", "/ LlmModel 类型的 LlmModel": "/ LlmModel type LlmModel", "/ PromptItem类型的PromptItem": "/PromptItem of type PromptItem", "/ string类型的PromptItemVersion": "/PromptItemVersion of type string", "/ 结果字符串": "/result string", "/ 花费时间,单位:毫秒": "/ Time spent, unit: milliseconds", "/ 机器人打分,0-100分": "/ Robot scoring, 0-100 points", "/ 人类打分,0-100分": "/ Human scoring, 0-100 points", "/ 最终得分": "/ final score", "/ 机器人测试期望结果": "/ Robot test expected results", "/ 是否机器人测试结果完全相等": "/ Whether the robot test results are exactly equal", "/ 测试类型,枚举中包含:文字、图形、声音": "/ Test type, the enumeration includes: text, graphics, sound", "/ 提示花费的 Token 数量": "/ Prompt the number of Tokens spent", "/ 结果花费的 Token 数量": "/ The number of Tokens spent as a result", "/ 总共花费的 Token 数量": "/Total number of Tokens spent", "/ 打靶模式:Chat(聊天模式)或 Single(单次测试模式),可为空(兼容旧数据)": "/ Target practice mode: Chat (chat mode) or Single (single test mode), can be empty (compatible with old data)", "/ SystemMessage(Prompt 内容,完成参数替换后的最终内容)": "/ SystemMessage (Prompt content, final content after completing parameter replacement)", "/ 用于对话模式,确保即使 Prompt 内容或参数变化,也能追溯历史使用的 SystemMessage": "/ Used in conversation mode to ensure that even if the Prompt content or parameters change, the historically used SystemMessage can be traced", "/ PromptResultChat 实体配置映射": "/PromptResultChat entity configuration mapping", "配置外键关系": "Configure foreign key relationships", "配置字段长度和约束": "Configure field lengths and constraints", "Content 字段不设置 MaxLength,使用数据库的 TEXT 类型以支持长文本": "The Content field does not set MaxLength and uses the TEXT type of the database to support long text.", "将枚举转换为 int 存储": "Convert enum to int storage", "UserFeedback 和 UserScore 可以为 null,不需要额外配置": "UserFeedback and UserScore can be null and no additional configuration is required", "/ PromptItem:每个不同版本的 Prompt 配置": "/PromptItem: Prompt configuration for each different version", "/ 靶场 ID": "/rangeID", "/ AI 模型的 AiModel.Id": "/ AiModel.Id of the AI ​​model", "/ 版本号,格式为 Name-Tactic-Aiming": "/ Version number, in the format Name-Tactic-Aiming", "/ 为 Tx 这里的x为分支号,str,允许1.1.1。。。": "/ is Tx where x is the branch number, str, 1.1.1 is allowed. . .", "/ Aiming 为 Ax 这里的x为打靶次数,int": "/ Aiming is Ax where x is the number of shooting times, int", "/ 靶场名 -> 应该采用 PromptRange 中的名字": "/ Range name -> should use the name in PromptRange", "/ 格式为 yyyy.MM.dd.x ,这里的x为当天生成的序号,int": "/ The format is yyyy.MM.dd.x, where x is the serial number generated on the day, int", "/ 示例一": "/ Example 1", "/ 示例二": "/ Example 2", "/ 靶道名": "/ Target lane name", "/ 格式为 x.x.x... ,这里的x为分支号": "/ The format is x.x.x..., where x is the branch number", "/ 在完整的版本号中,应该用-T连接Name": "/ In the complete version number, -T should be used to connect Name", "/ 请求参数键值对 JSON": "/ Request parameter key-value pair JSON", "/ 判断是否为 Version 格式": "/ Determine whether it is Version format", "判断是否为 Prompt 版本(从左到右,必须包含 -T 之前的部分,-Txxx 为可选,当,出现 -T 时,-A 为可选,但 -A 不能单独出现)。": "Determine whether it is a Prompt version (from left to right, the part before -T must be included, -Txxx is optional, when -T appears, -A is optional, but -A cannot appear alone).", "* 可能的格式为:": "* Possible formats are:", "* ...(T 后面可以有多个小数点)": "* ...(T can have multiple decimal points after it)", "/ 判断 Version 是否能匹配前缀": "/ Determine whether Version can match the prefix", "完整正则表达式": "Complete regular expression", "检查输入字符串和对比字符串是否完全匹配": "Check if input string and comparison string match exactly", "检查输入字符串是否部分匹配对比字符串": "Check if the input string partially matches the comparison string", "分割对比字符串和输入字符串": "Split comparison string and input string", "输入部分段数不能超过对比字符串段数": "The number of input part segments cannot exceed the number of comparison string segments.", "检查每个段是否匹配,并处理-T部分的继承规则": "Checks each segment for a match and handles inheritance rules for the -T part", "比较日期段": "Compare date range", "处理-T部分的继承规则": "Inheritance rules for handling -T parts", "输入的T部分不能比对比的T部分更长": "The T part of the input cannot be longer than the T part of the comparison", "比较每一级T部分": "Compare T-sections at each level", "其他部分必须完全匹配": "Other parts must match exactly", "/ 导入时使用": "/ used when importing", "初始化 NickName(避免 null 导致数据库保存失败)": "Initialize NickName (to avoid null causing database saving failure)", "/ 更新使用的模型参数": "/ Update the model parameters used", "/ 从 获取 对象": "/ Get the object from ", "/ 当存在 时返回此属性,否则使用 ": "/ Return this property when is present, otherwise use ", "/ 根据 FullVersion 字符串获取 RangeName、Tactic、Aim参数": "/ Get RangeName, Tactic, Aim parameters based on FullVersion string", "/ 从 Tastic 字符串判断上一级 Tastic,如果已经是顶级,则返回 \"\"": "/ Determine the previous level Tastic from the Tastic string. If it is already the top level, return \"\"", "/ PromptRange 靶场": "/PromptRange shooting range", "/ 当存在 时返回此属性,否则使用 ": "/ Return this property when is present, otherwise use ", "/ 数据库中的 PromptItems 实体": "/ PromptItems entity in database", "/ 数据库中的 PromptResult 实体": "/ PromptResult entity in database", "/ 数据库中的 LlmModel 实体": "/LlmModel entity in database", "/ 数据库中的 PromptResultChat 实体": "/ PromptResultChat entity in database", "/ PromptResult:PromptItem 的打靶结果": "/ PromptResult: PromptItem’s target shooting result", "/ LlmModelId,并添加LlmModel类作为属性": "/LlmModelId and add the LlmModel class as a property", "/ 机器人打分,0-10分": "/ Robot scoring, 0-10 points", "/ 人类打分,0-10分": "/ Human rating, 0-10 points", "/ PromptItem,并添加PromptItem类作为属性": "/PromptItem and add PromptItem class as attribute", "分数": "Fraction", "/ 更新手动评分": "/update manual rating", "/ 更新自动机器评分": "/Update automatic machine ratings", "/ 更新最终得分": "/update final score", "/ 测试类型枚举": "/Test type enum", "/ 文字": "/ Word", "/ 图形": "/graphics", "/ 声音": "/ sound", "/ 打靶模式枚举": "/ Targeting mode enumeration", "/ 单次测试模式": "/Single test mode", "/ 聊天模式": "/chat mode", "/ PromptResultChat:PromptResult 的对话历史记录": "/PromptResultChat: Conversation history for PromptResult", "/ 对话角色类型": "/ Dialogue role type", "/ 对话内容": "/ Conversation content", "/ 对话顺序": "/ Conversation sequence", "/ 从 DTO 创建实体": "/Create entities from DTO", "/ 更新用户反馈": "/Update user feedback", "/ Like(true)、Unlike(false)、取消反馈(null)": "/ Like (true), Unlike (false), Cancel feedback (null)", "/ 更新用户评分": "/update user ratings", "/ 评分(0-10分),null 表示取消评分": "/ Score (0-10 points), null means cancel the score", "/ 对话角色类型枚举": "/ Dialogue character type enumeration", "/ 用户": "/user", "/ 助手(AI)": "/ Assistant (AI)", "/ 2、运行:PM> add-migration [更新名称] -c PromptRangeSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ": "/ 2. Run: PM> add-migration [update name] -c PromptRangeSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ", "/ 2、运行:PM> add-migration [更新名称] -c PromptRangeSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ": "/ 2. Run: PM> add-migration [update name] -c PromptRangeSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ", "/ 2、运行:PM> add-migration [更新名称] -c PromptRangeSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ": "/ 2. Run: PM> add-migration [update name] -c PromptRangeSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ", "/ 2、运行:PM> add-migration [更新名称] -c PromptRangeSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ": "/ 2. Run: PM> add-migration [update name] -c PromptRangeSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ", "/ 2、运行:PM> add-migration [更新名称] -c PromptRangeSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ": "/ 2. Run: PM> add-migration [update name] -c PromptRangeSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ", "/ 2、运行:PM> add-migration [更新名称] -c PromptRangeSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ": "/ 2. Run: PM> add-migration [update name] -c PromptRangeSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ", "如果是Azure OpenAI": "If it is Azure OpenAI", "强制要求ApiVersion和Endpoint不为空": "It is mandatory that ApiVersion and Endpoint are not empty", "ApiVersion不为空且不在ApiVersionList中": "ApiVersion is not empty and not in ApiVersionList", "TODO: Image 需要不一样的触发机制": "TODO: Image requires a different triggering mechanism", "根据 rangeFilePaths, 找出他们公共父文件夹的路径": "According to rangeFilePaths, find the path of their common parent folder", "只有一个路径,返回其父目录": "There is only one path, returning to its parent directory", "标准化路径分隔符(兼容Windows和Unix)": "Standardized path separators (Windows and Unix compatible)", "minLen - 1 确保不包含最后的文件夹名": "minLen - 1 ensures that the last folder name is not included", "大小写不敏感比较(兼容Windows和Unix)": "Case-insensitive comparison (compatible with Windows and Unix)", "Unix系统需要保留根路径的 /": "Unix systems need to retain the / of the root path", "/ 根据靶场 ID, 导出该靶场下所有的靶道,返回文件夹路径": "/ Based on the shooting range ID, export all the shooting ranges under the shooting range and return to the folder path", "根据靶场名,获取靶场": "Get the shooting range based on the shooting range name", "获取输出的靶场的文件夹路径": "Get the folder path of the output range", "根据靶场名,获取靶道": "Get the target lane based on the shooting range name", "//用版号作为key, 映射字典": "//Use version number as key, mapping dictionary", "// 提取出 T 的第一位,并分组": "//Extract the first bit of T and group it", "每个靶道都需要导出": "Each target lane needs to be exported", "// 找出最佳item": "// Find the best item", "/ 导出指定的单个靶道,返回文件夹路径": "/Export the specified single target lane and return the folder path", "添加输入对象": "Add input object", "当前 plugin 文件夹目录,靶道名/别名": "Current plugin folder directory, target channel name/alias", "如果别名已经存在,就增加一个尾缀": "If the alias already exists, add a suffix", "完整的JSON文件路径": "Full JSON file path", "清空文件内容": "Clear file contents", "写入并且保持格式": "Write and keep format", "同理,构造 skprompt.txt 文件,内容为content": "In the same way, construct the skprompt.txt file with content", "/ 根据靶场,生成文件夹,并返回文件夹路径": "/ Based on the shooting range, generate a folder and return the folder path", "有别名就用别名,没有就用靶场名": "If there is an alias, use the alias; if not, use the shooting range name.", "先获取根目录": "Get the root directory first", "生成文件夹": "Generate folder", "如果存在,就先清理指定文件夹": "If it exists, clean the specified folder first", "TODO: 添加更多": "TODO: Add more", "限制文件上传的大小为 50M": "Limit file upload size to 50M", "文件保存路径": "File save path", "读取 zip 文件": "Read zip file", "解压": "Unzip", "判断文件结构": "Determine file structure", "假设为完整路径": "Assume full path", "先创建靶场": "Create a shooting range first", "TODO:给出失败提示": "TODO: Give a failure message", "读取所有的文件为一个 string": "Read all files into a string", "提取 prompt 请求参数": "Extract prompt request parameters", "TODO: 支持更多格式": "TODO: Support more formats", "没有参数": "no parameters", "// #region 可以选择先解压": "// #region can choose to decompress first", "// 解压文件": "// Unzip the file", "// 开始读取": "// Start reading", "throw new NcfExceptionBase($\"{curFile.FullName}文件格式错误\");": "throw new NcfExceptionBase($\"{curFile.FullName} file format error\");", "if (curFile.Name == \"\") // 是目录": "if (curFile.Name == \"\") // is a directory", "// throw new NcfExceptionBase($\"{curFile.FullName}文件格式错误\");": "// throw new NcfExceptionBase($\"{curFile.FullName} file format error\");", "// 从缓存中读取": "//Read from cache", "// 根据不同文件名,更新不同的字段": "//Update different fields based on different file names", "if (curFile.Name == \"config.json\") // 更新配置文件": "if (curFile.Name == \"config.json\") // Update configuration file", "// 读取所有的文件为一个 string": "// Read all files into a string", "// 提取 prompt 请求参数": "//Extract prompt request parameters", "// 没有参数": "// no parameters", "throw new NcfExceptionBase($\"{curFile.FullName}不符合上传要求\");": "throw new NcfExceptionBase($\"{curFile.FullName} does not meet the upload requirements\");", "保存": "save", "/ 新增, 打靶时": "/ Newly added, during target practice", "IsNewTactic IsNewSubTactic不能同时为True": "IsNewTactic IsNewSubTactic cannot be True at the same time", "默认值为2000": "The default value is 2000", "// 更新版本号": "//Update version number", "新建 PromptItem": "New PromptItem", "如果有id,就先找到对应的promptItem, 再根据Item.RangeId获取promptRange,再根据参数新建一个靶道": "If there is an id, first find the corresponding promptItem, then get the promptRange based on Item.RangeId, and then create a new target channel based on the parameters.", "目标版号的父 T 是空串": "The parent T of the target version number is an empty string", "关联复制预期结果过来": "Copy the expected results by association", "目标版号的父 T 应该是当前版本的父 T": "The parent T of the target version number should be the parent T of the current version", "目标版号的父 T 应该是当前版本的 T": "The parent T of the target version number should be the current version T", "不改变分支": "Do not change branches", "// 连发": "// burst", "保存之前验证一下版号是否已经存在,确保版号唯一性": "Before saving, verify whether the version number already exists to ensure the uniqueness of the version number.", "/ 分数趋势图(依据时间)": "/ Score trend chart (based on time)", "/ TODO 改为显示靶场下所有有平均分的promptItem的趋势图": "/ TODO Change to display the trend chart of all promptItems with average scores in the shooting range", "获取同一个靶道下的所有打过分的item": "Get all scored items under the same target lane", "构造返回值": "Construct return value", "根据 Tactic 的第一级(第一个点号之前的部分,如 \"1\"、\"11\"、\"1.1\" 分别提取为 \"1\"、\"11\"、\"1\")": "According to the first level of Tactic (the part before the first period number, such as \"1\", \"11\", and \"1.1\" are extracted as \"1\", \"11\", and \"1\" respectively)", "[t1, 版号, 平均分]": "[t1, version number, average score]", "[t2, 版号, 平均分]": "[t2, version number, average score]", "/ 获取某个版本的 PromptItem 和模型信息,支持:": "/ Get a certain version of PromptItem and model information, support:", "/ 精准搜索,如:2024.01.06.3-T1-A2": "/ Precise search, such as: 2024.01.06.3-T1-A2", "/ 靶道模糊搜索:输入到靶场和靶道信息,如:2024.01.06.3-T1": "/ Target fuzzy search: Enter the shooting range and target information, such as: 2024.01.06.3-T1", "/ 靶场模糊搜索:只输入靶场编号,如:2024.01.06.3": "/ Fuzzy search of shooting range: enter only the shooting range number, such as: 2024.01.06.3", "/ 当模糊搜索时,是否采用平均分最高分,如果为 false,则直接取最高分": "/ When fuzzy search, whether to use the average highest score, if it is false, then directly take the highest score", "throw new NcfExceptionBase($\"找不到{dto.ModelId}对应的AIModel\");": "throw new NcfExceptionBase($\"Cannot find the AIModel corresponding to {dto.ModelId}\");", "精准查询经过测试的 PromptItem,如:2024.01.06.3-T1-A2": "Accurately query the tested PromptItem, such as: 2024.01.06.3-T1-A2", "模糊查询,如:2024.01.06.3-T1,或者 2024.01.06.3": "Fuzzy query, such as: 2024.01.06.3-T1, or 2024.01.06.3", "定位靶场": "Positioning range", "平均分": "average score", "最高分": "highest score", "按照靶道进行模糊搜索": "Fuzzy search based on target lane", "定位靶道": "target lane", "按照靶场进行模糊搜索": "Fuzzy search by shooting range", "不需要再增加条件": "No need to add any more conditions", "生成最终的查询条件表达式": "Generate the final query condition expression", "从某个靶道进行模糊搜索": "Fuzzy search from a target lane", "?? throw new NcfExceptionBase($\"找不到{promptItem.ModelId}对应的AIModel\");": "?? throw new NcfExceptionBase($\"Cannot find the AIModel corresponding to {promptItem.ModelId}\");", "/ 根据配置获取模型参数": "/ Get model parameters according to configuration", "定义 AI 接口调用参数和 Token 限制等": "Define AI interface calling parameters and Token restrictions, etc.", "/ 输入靶场名,构建该靶场内所有的版本树": "/ Enter the shooting range name to build all version trees in the shooting range", "/ 靶场名": "/ Range name", "/ 版本树": "/ Version tree", "获取同一个靶道下的所有的 PromptItem": "Get all PromptItems under the same target lane", "设置顶部节点(Tx)": "Set top node (Tx)", "查找结点方法": "Find node method", "由于 PromptItem 的产生时间顺序特征,反向查找能够更快找到": "Due to the temporal ordering characteristics of PromptItem, reverse search can find it faster", "寻找上级节点 TODO:为了提高效率,可以只向上查找": "Find superior nodes TODO: In order to improve efficiency, you can only search upwards", "创造当前新节点信息": "Create current new node information", "顶部节点": "top node", "子节点": "child node", "/ 返回带树形结构的 PromptRange": "/ Returns a PromptRange with a tree structure", "由于 Tastic 层次是根据时间(ID)顺序向下发展,所以只要根据 ID 排序,即可实现所有节点自顶向下的排列顺序": "Since the Tastic hierarchy develops downward according to time (ID) order, all nodes can be sorted from top to bottom as long as they are sorted according to ID.", "加全角空格": "Add full-width spaces", "加半角空格": "Add half-width space", "获取柱状结构前缀": "Get columnar structure prefix", "读取评分": "Read ratings", "获取树状结构": "Get tree structure", "正在开始一个新的 PromptRange,插入这个 Prompt的整体引导 TODO:判断是否需要添加额外描述性节点": "Starting a new PromptRange, inserting the overall guidance of this Prompt TODO: Determine whether additional descriptive nodes need to be added", "迭代插入子节点": "Iteratively insert child nodes", "显示 Tactic 虚拟节点": "Show Tactic virtual nodes", "顶层,如:T1,加上下拉标记": "Top level, such as: T1, plus drop-down mark", "创建当前节点": "Create current node", "判断是否还有下级": "Determine whether there are subordinates", "throw new Exception($\"未找到{promptRangeId}对应的靶场\");": "throw new Exception($\"The shooting range corresponding to {promptRangeId} was not found\");", "todo 关联删除": "todo association deletion", "/ 根据 PromptResultId 获取所有对话记录": "/ Get all conversation records based on PromptResultId", "/ 批量添加对话记录": "/ Add conversation records in batches", "/ 对话消息列表,格式:[{role: 'user'|'assistant', content: string}]": "/ Conversation message list, format: [{role: 'user'|'assistant', content: string}]", "/ 起始序号,如果为 null 则从现有最大序号+1开始,如果为 1 则从头开始": "/ Start sequence number, if it is null, start from the existing maximum sequence number + 1, if it is 1, start from the beginning", "如果指定了起始序号,使用它;否则从现有最大序号+1开始": "If a starting sequence number is specified, use it; otherwise start from the highest existing sequence number + 1", "获取现有的最大序号": "Get the largest existing serial number", "强制保存更改,确保 ID 被正确更新": "Force changes to be saved to ensure IDs are updated correctly", "保存后,Entity Framework 会自动更新实体的 ID": "After saving, Entity Framework will automatically update the entity's ID", "但为了确保 ID 正确,我们强制重新从数据库读取所有刚保存的实体": "But to make sure the IDs are correct, we force all newly saved entities to be re-read from the database", "这样可以避免 ID 为 0 的问题": "This avoids the problem of ID 0", "获取所有刚保存的实体的 Sequence 和 RoleType 组合(用于精确匹配)": "Get the Sequence and RoleType combination of all the entities just saved (for exact matching)", "重新从数据库读取这些实体(通过 PromptResultId、Sequence 和 RoleType)": "Re-read these entities from the database (via PromptResultId, Sequence and RoleType)", "注意:同一个 Sequence 可能有 User 和 Assistant 两条记录,所以我们需要精确匹配": "Note: The same Sequence may have two records, User and Assistant, so we need exact matching", "按照原始顺序排序并创建 DTO": "Sort and create DTO in original order", "需要按照 chatEntities 的原始顺序来匹配,确保返回顺序正确": "Need to match the original order of chatEntities to ensure the correct return order", "通过 Sequence 和 RoleType 精确匹配找到对应的已保存实体": "Find the corresponding saved entity through exact matching of Sequence and RoleType", "如果找不到或 ID 仍然为 0,抛出异常而不是返回无效的 DTO": "If not found or ID is still 0, throw an exception instead of returning an invalid DTO", "这样可以确保问题被及时发现和修复": "This ensures that problems are discovered and fixed promptly", "/ 对话记录 ID": "/ Conversation record ID", "/ 删除指定 PromptResult 的所有对话记录": "/ Delete all conversation records of the specified PromptResult", "/ 对话消息 DTO(用于批量添加)": "/ Conversation message DTO (for batch addition)", "?? throw new NcfExceptionBase($\"未找到{promptItemId}对应的提示词\");": "?? throw new NcfExceptionBase($\"The prompt word corresponding to {promptItemId} was not found\");", "需要在变量前添加$": "You need to add $ before the variable", "string completionPrompt = $@\"请根据提示输出对应内容:": "string completionPrompt = $@\"Please output the corresponding content according to the prompts:", "生成替换参数后的 SystemMessage(用于保存到数据库)": "Generate SystemMessage after replacing parameters (for saving to database)", "如果 Prompt 内容包含参数占位符(如 {{$variableName}}),进行参数替换": "If the Prompt content contains parameter placeholders (such as {{$variableName}}), perform parameter substitution", "读取参数并替换 Prompt 内容中的占位符": "Read parameters and replace placeholders in Prompt content", "替换格式:{Prefix}{key}{Suffix} -> value": "Replacement format: {Prefix}{key}{Suffix} -> value", "例如:{{$variableName}} -> actualValue": "For example: {{$variableName}} -> actualValue", "从数据库中获取模型信息": "Get model information from database", "构建生成AI设置": "Build to generate AI settings", "TODO: model 加上模型的类型:Chat/TextCompletion/TextToImage 等": "TODO: model plus model type: Chat/TextCompletion/TextToImage, etc.", "创建 AI Handler 处理器(也可以通过工厂依赖注入)": "Create AI Handler processor (can also be injected through factory dependency)", "todo 替换为真实用户名,可能需要从NeuChar获取?": "Replace todo with your real username, which may need to be obtained from NeuChar?", "如果有参数,前后缀不能为空": "If there are parameters, the suffix and suffix cannot be empty.", "读取参数并填充": "Read parameters and fill in", "todo 计算token消耗": "todo calculates token consumption", "简单计算": "Simple calculation", "判断是否为聊天模式": "Determine whether it is chat mode", "如果是聊天模式,保存 SystemMessage;否则为 null": "If in chat mode, save SystemMessage; otherwise null", "只在聊天模式时保存 SystemMessage": "Save SystemMessage only in chat mode", "如果是聊天模式,保存对话记录": "If in chat mode, save the conversation record", "如果有历史记录,先添加历史记录": "If there is a history record, add the history record first", "添加当前对话(用户消息和 AI 响应)": "Add current conversation (user messages and AI responses)", "有期望结果, 进行自动打分": "Have expected results and perform automatic scoring", "/ 继续聊天:在现有 PromptResult 中追加对话记录,不创建新的 PromptResult": "/ Continue chatting: Append the conversation record to the existing PromptResult without creating a new PromptResult", "/ 现有的 PromptResult ID": "/ Existing PromptResult ID", "/ 用户消息": "/ User message", "/ 返回新追加的对话记录(用户消息和 AI 回复)": "/ Returns newly added conversation records (user messages and AI replies)", "获取现有的 PromptResult": "Get existing PromptResult", "验证是否为聊天模式": "Verify whether it is in chat mode", "获取历史对话记录": "Get historical conversation records", "定义 AI 接口调用参数": "Define AI interface call parameters", "这样可以确保即使 Prompt 内容或参数变化,继续对话时也使用最初保存的 SystemMessage": "This ensures that even if the Prompt content or parameters change, the originally saved SystemMessage is used when continuing the conversation.", "这种情况可能发生在旧数据或 Single 模式的数据中": "This may happen with old data or data in Single mode", "如果 Prompt 内容包含参数占位符,进行参数替换": "If the Prompt content contains parameter placeholders, perform parameter substitution", "创建 AI Handler 处理器": "Create an AI Handler processor", "使用 ChatConfig,使用基于 promptResultId 的唯一 userId": "Using ChatConfig, use a unique userId based on promptResultId", "获取历史记录并添加到 KernelArguments": "Get history and add to KernelArguments", "调用 AI 接口": "Call AI interface", "追加新的对话记录到 PromptResultChat": "Append new conversation records to PromptResultChat", "添加新的对话记录(会自动从现有最大序号+1开始)": "Add a new conversation record (it will automatically start from the existing maximum sequence number + 1)", "更新 PromptResult 的 ResultString(追加新的回复)": "Update ResultString of PromptResult (append new reply)", "注意:由于 ResultString 是 private set,我们需要通过反射或者添加一个更新方法": "Note: Since ResultString is a private set, we need to use reflection or add an update method", "这里我们暂时不更新 ResultString,因为对话记录已经保存在 PromptResultChat 中了": "We will not update ResultString here because the conversation record has been saved in PromptResultChat.", "如果需要更新,可以添加一个 UpdateResultString 方法": "If you need to update, you can add an UpdateResultString method", "验证 score >= 0": "Verify score >= 0", "根据id搜索数据库": "Search database based on id", "/ 构造 SenparcAiSetting, 在两个地方使用": "/ Construct SenparcAiSetting, used in two places", "/ AI 打分": "/AI scoring", "保存期望结果列表": "Save a list of expected results", "TODO:添加不等号规则": "TODO: Add inequality sign rule", "获取模型": "Get model", "TODO:可以设置一个默认 PromptRange 的值,或者使用配置来指定打分的 AI,而不是使用同一个模型。": "TODO: You can set a default PromptRange value, or use configuration to specify the scoring AI instead of using the same model.", "正则匹配出result.Output中的数字": "Regularly match the numbers in result.Output", "匹配 MAX_SCORE,后面可以跟 0-2 位的小数": "Matches MAX_SCORE, which can be followed by 0-2 decimal places", "/ 更新promptItem的平均分和最高分": "/ Update the average score and maximum score of promptItem", "没有结果": "no results", "区分用户": "Distinguish between users", "/ 根据输入的 Version、NickName 等规则,找到关联 PromptItem 中质量最好的一个": "/ Based on the input Version, NickName and other rules, find the best quality among the associated PromptItems", "/ 当明确指定时,会精确命中; ": "/ When explicitly specified, an exact hit will occur; ", "/ 当 Version 是模糊查询时,在下级 PromptItem 中查找最好的一个": "/ When Version is a fuzzy query, find the best one among the lower-level PromptItems", "/ 当 NickName 存在多个的时候,寻找同名的最好的一个": "/ When there are multiple NickNames, find the best one with the same name", "准备运行": "ready to run", "var userId = \"XncfBuilder\";//区分用户": "var userId = \"XncfBuilder\";//Differentiate users", "var modelName = \"text-davinci-003\";//默认使用模型": "var modelName = \"text-davinci-003\"; //Use model by default", "//TODO:外部传入配置": "//TODO: External incoming configuration", "优先从数据库找": "Find it first from the database", "不提供文件地址时,优先从数据库找": "When the file address is not provided, priority is given to finding it from the database.", "第一层:靶场,对应 Plugin": "The first level: shooting range, corresponding to Plugin", "Plugin 名字": "Plugin name", "导入 Plugin 后的 KernelPlugin 对象": "KernelPlugin object after importing Plugin", "从数据库读取失败,需要尝试从本地文件读取": "Failed to read from database, need to try reading from local file", "从数据库读取": "Read from database", "转到尝试用文件读取": "Go to try reading with file", "查找下属所有的靶道": "Find all target lanes for subordinates", "&& (functionNames.Any(f => f == z.FullVersion) //完整版本信息匹配": "&& (functionNames.Any(f => f == z.FullVersion) //Full version information matching", "|| functionNames.Any(f => f == z.NickName)) //别称匹配": "|| functionNames.Any(f => f == z.NickName)) //alias matching", "PromptItem 的命中有几种可能:": "There are several possibilities for PromptItem hits:", "* 1、FullVersion 达到匹配 functionName 要求": "* 1. FullVersion meets the requirement of matching functionName", "* 2、NickName 达到匹配 functionName 要求(常见)": "* 2. NickName meets the requirement of matching functionName (common)", "* 3、满足上述任意条件后(都可能多个),在相关记录内,找到评分最高的一个": "* 3. After meeting any of the above conditions (there may be multiple), find the one with the highest score in the relevant records", "第二层:在某个靶场下的具体 PromptItem 筛选": "Second level: specific PromptItem filtering under a certain shooting range", "符合版本格式": "Comply with version format", "使用别名查找": "Find using alias", "选择最佳结果": "Select best result", "加入当前 functionName 的最佳结果": "Add the best results for the current functionName", "采用默认配置覆盖": "Override with default configuration", "TODO: 专门提供注释": "TODO: Specially provide comments", "设置模型参数": "Set model parameters", "设置输入参数": "Set input parameters", "需要从 Plugin 文件读取": "Need to read from Plugin file", "从文件读取": "read from file", "统一将文件或数据库读取的 function 进行注册": "Unifiedly register functions for reading files or databases", "TODO:给出警告": "TODO: give a warning", "构建请求对象": "Build request object", "请求": "ask", "/ 提供可供 functionName 使用的名称": "/ Provides a name that functionName can use", "/ 如果出现特殊字符,可能出现错误:A function name can contain only ASCII letters, digits, and underscores: '2024.05.17.1-T1.1-A3' is not a valid name. (Parameter 'value')": "/ If special characters appear, an error may occur: A function name can contain only ASCII letters, digits, and underscores: '2024.05.17.1-T1.1-A3' is not a valid name. (Parameter 'value')", "new AreaPageMenuItem(GetAreaUrl(HomeUrl+\"Model\"),\"模型\",\"fa fa-laptop\"),": "new AreaPageMenuItem(GetAreaUrl(HomeUrl+\"Model\"),\"model\",\"fa fa-laptop\"),", "new AreaPageMenuItem(GetAreaUrl($\"/Admin/PromptRange/DatabaseSample\"),\"数据库操作示例\",\"fa fa-bookmark-o\")": "new AreaPageMenuItem(GetAreaUrl($\"/Admin/PromptRange/DatabaseSample\"),\"Database Operation Example\",\"fa fa-bookmark-o\")", "导出plugin操作按钮区域": "Export plugin action button area", "导出plugin树形选择器": "Export plugin tree selector", "树节点悬停效果": "Tree node hover effect", "树节点选中效果": "Tree node selection effect", "移除gap,因为已经有8px的分隔条": "Remove the gap because there is already an 8px divider", "使用视口高度,减去头部导航高度": "Use viewport height, minus head navigation height", "确保有最小宽度,小于此宽度会出现横向滚动条": "Make sure there is a minimum width, below which a horizontal scroll bar will appear", "允许横向滚动,不隐藏内容": "Allow horizontal scrolling without hiding content", "promptPage 横向滚动条样式": "promptPage horizontal scroll bar style", "卡片": "card", "标题行": "title line", "左侧模块": "left module", "默认宽度,会被Vue的style覆盖": "The default width will be overridden by Vue's style", "允许用户拖动到更宽": "Allow user to drag to wider", "拖动时的平滑过渡": "Smooth transition when dragging", "左侧模块 配置区域\\输入prompt区域": "Left module configuration area\\input prompt area", "左侧模块 配置 行 区域": "Left module configuration row area", "左侧按钮组紧凑布局": "Compact layout of left button group", "左侧模块 配置 下拉选择 区域": "Module configuration drop-down selection area on the left", "左侧模块 配置 参数 内容 区域": "Left module configuration parameter content area", "模型参数 展开折叠 设置": "Model Parameters Expand Collapse Settings", "输入prompt盒子": "Enter prompt box", "改为纵向布局": "Change to portrait layout", "请求参数区域 - 在Prompt输入框下方": "Request parameter area - below the Prompt input box", "输入prompt输入框盒子 内容": "Enter prompt input box box content", "移除 display: flex 和 flex-direction: column,因为它们会导致 contenteditable 内部的 inline 元素(如 var-highlight span)垂直排列": "Remove display: flex and flex-direction: column as they cause inline elements (such as var-highlight spans) inside contenteditable to align vertically", "contenteditable富文本编辑器": "contenteditable rich text editor", "placeholder效果": "placeholder effect", "高亮变量样式 - 强制内联显示": "Highlight variable style - force display inline", "使用 normal 而不是 nowrap,这样可以完全覆盖父元素的 pre-wrap,忽略空白字符": "Use normal instead of nowrap, which completely overrides the parent element's pre-wrap, ignoring whitespace characters", "确保 span 标签前后没有空白导致的换行": "Make sure there are no line breaks caused by whitespace before and after the span tag", "防止因为空白字符导致的换行": "Prevent line breaks caused by whitespace characters", "确保内容不会换行": "Make sure content doesn't wrap", "确保 var-highlight span 前后的空白文本节点不会导致换行": "Ensure that whitespace text nodes before and after the var-highlight span do not cause line breaks", "确保编辑器内的br正常工作": "Make sure br within the editor is working properly", "变量提示卡片": "Variable reminder card", "输入Prompt请求参数盒子 promptPmroparaBox": "Enter the Prompt request parameter box promptPmroparaBox", "请求参数区域的标题行": "The header line of the request parameters area", "请求参数盒子的滚动条样式": "Scroll bar style of request parameter box", "中间模块 - 输入Prompt": "Intermediate module - input prompt", "中间区域最大化状态": "Maximum state of the middle area", "占据所有可用空间": "Take up all available space", "取消最大宽度限制": "Remove maximum width limit", "右侧模块 - 输出和分析": "Right module - output and analysis", "优先扩展,占据剩余空间": "Prioritize expansion and occupy the remaining space", "右侧区域最大化状态": "Right area maximized state", "右侧区域隐藏状态(中间区域最大化时)": "The right area is hidden (when the middle area is maximized)", "不占用任何空间": "Does not take up any space", "右侧模块 输出区域": "Right module output area", "重要:允许flex收缩": "Important: Allow flex to shrink", "为自定义滚动条定位": "Positioning a custom scroll bar", "定制滚动条样式": "Customize scroll bar style", "滚动条的宽度": "scroll bar width", "滚动条轨道的背景色": "The background color of the scroll bar track", "滚动条滑块的背景色": "The background color of the scrollbar slider", "滚动条滑块的圆角": "Rounded corners of scroll bar slider", "鼠标悬停时滚动条滑块的背景色": "Background color of scrollbar slider on mouseover", "VSCode风格的自定义滚动条缩略图": "VSCode style custom scroll bar thumbnail", "与滚动条对齐": "Align with scrollbar", "从内容区域开始,避免覆盖标题栏和最大化按钮 (boxCard padding 10px + promptVersion ~40px + gap 15px + titleRow 35px + gap 15px)": "Start with the content area and avoid covering the title bar and maximize button (boxCard padding 10px + promptVersion ~40px + gap 15px + titleRow 35px + gap 15px)", "增加宽度以容纳评分信息": "Increase width to accommodate rating information", "增加透明度": "increase transparency", "增强毛玻璃效果": "Enhance frosted glass effect", "缩略图内容容器": "Thumbnail content container", "时间显示": "time display", "评分显示": "Rating display", "Excel风格数据条背景": "Excel style data bar background", "根据评分等级调整颜色": "Adjust colors based on rating scale", "绿色 - 优秀 8-10分": "Green - Excellent 8-10 points", "蓝色 - 良好 6-8分": "Blue - Good 6-8 points", "黄色 - 中等 4-6分": "Yellow - medium 4-6 points", "橙色 - 较低 2-4分": "Orange - lower 2-4 points", "红色 - 差 0-2分": "Red - 0-2 points worse", "激活状态下的数据条颜色更深": "Data bars in active state are darker", "可视区域指示器": "Visible area indicator", "不需要调整内容宽度,滚动条悬浮显示": "No need to adjust the content width, the scroll bar is displayed in suspension", "改为 normal,让 HTML 标签自然控制换行,避免多余空行": "Change to normal to allow HTML tags to naturally control line breaks and avoid redundant blank lines.", "Markdown 列表样式优化": "Markdown list style optimization", "减少上下间距,避免过多空行": "Reduce the upper and lower spacing to avoid too many blank lines", "保持左侧缩进": "Keep left indented", "减少列表项之间的间距": "Reduce the spacing between list items", "确保无额外 padding": "Make sure there is no additional padding", "Markdown table 样式": "Markdown table style", "Markdown 代码块样式": "Markdown code block style", "为复制按钮留出空间": "Leave space for copy button", "代码块复制按钮": "Code block copy button", "输出区域的复制按钮需要避开右侧滚动条缩略图 (140px + 8px margin)": "The copy button in the output area needs to avoid the right scroll bar thumbnail (140px + 8px margin)", "行内代码样式": "Inline code style", "代码块滚动条样式": "Code block scroll bar style", "Markdown 标题样式": "Markdown title style", "Markdown 引用块样式": "Markdown quote block style", "Markdown 段落间距": "Markdown paragraph spacing", "Markdown 水平分割线": "Markdown horizontal dividing line", "Markdown 链接样式": "Markdown link style", "SystemMessage 折叠面板样式": "SystemMessage accordion style", "SystemMessage 内容区域样式": "SystemMessage content area style", "为右侧滚动条预留空间": "Reserve space for right scrollbar", "允许换行,避免空间不足": "Allow line breaks to avoid running out of space", "左对齐": "left aligned", "AI评分加载状态": "AI rating loading status", "改为左侧显示": "Change to display on the left", "添加阴影": "add shadow", "右侧模块 分析区域": "Right module analysis area", "不伸缩": "Not scalable", "减小高度,留更多空间给输出区域": "Reduce the height and leave more space for the output area", "调整图表高度": "Adjust chart height", "版本记录 btn": "version record btn", "版本记录 抽屉": "version record drawer", "tree 更多提示操作区域": "tree More prompt operation area", "可拖动分隔条样式": "Draggable divider style", "防止双击选中文本": "Prevent double-clicking selected text", "使用伪元素扩展可点击区域,不影响布局": "Use pseudo elements to expand the clickable area without affecting the layout", "分隔条中间的指示线": "The indicator line in the middle of the divider", "双击还原时的闪烁动画": "Flashing animation when double-clicking to restore", "推理过程样式": "reasoning process style", "========== Prompt 对比对话框样式(Git Diff 风格) ==========": "========== Prompt comparison dialog style (Git Diff style) ==========", "对话框主体": "Dialog body", "选择器区域 - 紧凑横向布局": "Selector area - compact horizontal layout", "评分统计对比卡片": "Rating statistics comparison card", "对比内容区域": "Compare content areas", "紧凑型信息表格": "Compact information form", "Diff 区块样式": "Diff block style", "Diff 容器(支持同步滚动)": "Diff container (supports synchronous scrolling)", "Diff 列头部": "Diff column header", "Diff 代码块": "Diff code block", "Git风格差异高亮": "Git style difference highlighting", "新增的内容(绿色)": "New content (green)", "删除的内容(红色)": "Deleted content (red)", "修改的内容(黄色边框,用于行内差异)": "Modified content (yellow border, for inline differences)", "未修改的内容(灰色)": "Unmodified content (gray)", "行内单词级别差异高亮": "Inline word level difference highlighting", "空内容提示": "Empty content prompt", "Diff容器滚动条": "Diff container scroll bar", "请求参数表格特殊样式": "Request parameter form special style", "空状态": "Empty state", "空参数提示": "Empty parameter prompt", "对话框底部按钮": "Button at bottom of dialog box", "响应式调整": "Responsive adjustments", "========== 模型选择器自定义显示 ==========": "========== Model selector custom display ==========", "选中状态": "selected state", "悬停状态": "hover state", "========== 对比窗口版本号复制按钮 ==========": "========== Compare window version number copy button ==========", "继续聊天 - 对话历史容器": "Continue Chat - Conversation History Container", "对话历史容器滚动条样式": "Conversation history container scroll bar style", "System Message 在对话历史容器内的样式": "System Message style within conversation history container", "空状态提示": "Empty status prompt", "对话消息项": "Conversation message item", "用户消息(右侧对齐)": "User message (right aligned)", "助手消息(左侧对齐)": "Assistant message (left aligned)", "消息头像": "Message avatar", "消息内容包装器": "Message content wrapper", "消息头部(角色、序号、时间)": "Message header (role, serial number, time)", "消息内容": "Message content", "移除 white-space: pre-wrap; 因为 Markdown 已解析为 HTML,HTML 标签会自然控制换行": "Remove white-space: pre-wrap; because Markdown is parsed into HTML, and HTML tags will naturally control line wrapping", "保留此属性会导致 HTML 标签之间的换行符被保留,造成多余空行": "Preserving this attribute will cause line breaks between HTML tags to be preserved, resulting in extra blank lines", "代码块样式": "code block style", "用户消息中的复制按钮特殊样式": "Copy button special style in user messages", "优化:减少上下间距,避免过多空行": "Optimization: Reduce the upper and lower spacing to avoid too many blank lines", "优化:减少列表项之间的间距": "Optimization: reduce the spacing between list items", "消息反馈区域(只有助手消息显示)": "Message feedback area (only assistant messages are displayed)", "优化对话消息的响应式设计": "Optimize the responsive design of conversation messages", "对话消息的hover效果": "Hover effect of conversation messages", "优化markdown内容在对话中的显示": "Optimize the display of markdown content in conversations", "修复对话框滚动条问题": "Fix dialog scroll bar issue", "确保表单内容不会导致对话框超出视口": "Ensure form content does not cause the dialog box to extend beyond the viewport", "确保对话框主体不会超出视口": "Make sure the dialog body does not extend beyond the viewport", "移除可能导致滚动条的 margin-bottom": "Remove margin-bottom that may cause scrollbars", "继续聊天对话框的特殊样式": "Special style for continue chat dialog", "))return e;for(var r=e.split(\"/\"),i=t.split(\"/\"),n=r[0];\".\"===n||\"..\"===n;)\"..\"===n&&i.pop(),r.shift(),n=r[0];return i.join(\"/\")+\"/\"+r.join(\"/\")},extend:function(e,t){if(t)for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r]);return e},defaults:function(e,t){if(t)for(var r in t)void 0===e[r]&&(e[r]=t[r]);return e},extendWithPropList:function(e,t,r){if(t)for(var i=0;i=400?e.onerror&&e.onerror():e.onload&&e.onload(t.response)},e.onerror&&(t.onerror=e.onerror),t.send(null)}};var F,z={supportWebGL:function(){if(null==F)try{var e=document.createElement(\"canvas\");if(!e.getContext(\"webgl\")&&!e.getContext(\"experimental-webgl\"))throw new Error}catch(e){F=!1}return F}};z.Int8Array=\"undefined\"==typeof Int8Array?Array:Int8Array,z.Uint8Array=\"undefined\"==typeof Uint8Array?Array:Uint8Array,z.Uint16Array=\"undefined\"==typeof Uint16Array?Array:Uint16Array,z.Uint32Array=\"undefined\"==typeof Uint32Array?Array:Uint32Array,z.Int16Array=\"undefined\"==typeof Int16Array?Array:Int16Array,z.Float32Array=\"undefined\"==typeof Float32Array?Array:Float32Array,z.Float64Array=\"undefined\"==typeof Float64Array?Array:Float64Array;var G={};\"undefined\"!=typeof window?G=window:void 0!==r.g&&(G=r.g),z.requestAnimationFrame=G.requestAnimationFrame||G.msRequestAnimationFrame||G.mozRequestAnimationFrame||G.webkitRequestAnimationFrame||function(e){setTimeout(e,16)},z.createCanvas=function(){return document.createElement(\"canvas\")},z.createImage=function(){return new G.Image},z.request={get:B.get},z.addEventListener=function(e,t,r,i){e.addEventListener(t,r,i)},z.removeEventListener=function(e,t,r){e.removeEventListener(t,r)};const U=z;var k=function(){this.head=null,this.tail=null,this._length=0};k.prototype.insert=function(e){var t=new k.Entry(e);return this.insertEntry(t),t},k.prototype.insertAt=function(e,t){if(!(e<0)){for(var r=this.head,i=0;r&&i!=e;)r=r.next,i++;if(r){var n=new k.Entry(t),a=r.prev;a?(a.next=n,n.prev=a):this.head=n,n.next=r,r.prev=n}else this.insert(t)}},k.prototype.insertBeforeEntry=function(e,t){var r=new k.Entry(e),i=t.prev;i?(i.next=r,r.prev=i):this.head=r,r.next=t,t.prev=r,this._length++},k.prototype.insertEntry=function(e){this.head?(this.tail.next=e,e.prev=this.tail,this.tail=e):this.head=this.tail=e,this._length++},k.prototype.remove=function(e){var t=e.prev,r=e.next;t?t.next=r:this.head=r,r?r.prev=t:this.tail=t,e.next=e.prev=null,this._length--},k.prototype.removeAt=function(e){if(!(e<0)){for(var t=this.head,r=0;t&&r!=e;)t=t.next,r++;return t?(this.remove(t),t.value):void 0}},k.prototype.getHead=function(){if(this.head)return this.head.value},k.prototype.getTail=function(){if(this.tail)return this.tail.value},k.prototype.getAt=function(e){if(!(e<0)){for(var t=this.head,r=0;t&&r!=e;)t=t.next,r++;return t.value}},k.prototype.indexOf=function(e){for(var t=this.head,r=0;t;){if(t.value===e)return r;t=t.next,r++}},k.prototype.length=function(){return this._length},k.prototype.isEmpty=function(){return 0===this._length},k.prototype.forEach=function(e,t){for(var r=this.head,i=0,n=void 0!==t;r;)n?e.call(t,r.value,i):e(r.value,i),r=r.next,i++},k.prototype.clear=function(){this.tail=this.head=null,this._length=0},k.Entry=function(e){this.value=e,this.next=null,this.prev=null};const V=k;var H=function(e){this._list=new V,this._map={},this._maxSize=e||10};H.prototype.setMaxSize=function(e){this._maxSize=e},H.prototype.put=function(e,t){if(!this._map.hasOwnProperty(e)){var r=this._list.length();if(r>=this._maxSize&&r>0){var i=this._list.head;this._list.remove(i),delete this._map[i.key]}var n=this._list.insert(t);n.key=e,this._map[e]=n}},H.prototype.get=function(e){var t=this._map[e];if(this._map.hasOwnProperty(e))return t!==this._list.tail&&(this._list.remove(t),this._list.insertEntry(t)),t.value},H.prototype.remove=function(e){var t=this._map[e];void 0!==t&&(delete this._map[e],this._list.remove(t))},H.prototype.clear=function(){this._list.clear(),this._map={}};const W=H;var j={},X={transparent:[0,0,0,0],aliceblue:[240,248,255,1],antiquewhite:[250,235,215,1],aqua:[0,255,255,1],aquamarine:[127,255,212,1],azure:[240,255,255,1],beige:[245,245,220,1],bisque:[255,228,196,1],black:[0,0,0,1],blanchedalmond:[255,235,205,1],blue:[0,0,255,1],blueviolet:[138,43,226,1],brown:[165,42,42,1],burlywood:[222,184,135,1],cadetblue:[95,158,160,1],chartreuse:[127,255,0,1],chocolate:[210,105,30,1],coral:[255,127,80,1],cornflowerblue:[100,149,237,1],cornsilk:[255,248,220,1],crimson:[220,20,60,1],cyan:[0,255,255,1],darkblue:[0,0,139,1],darkcyan:[0,139,139,1],darkgoldenrod:[184,134,11,1],darkgray:[169,169,169,1],darkgreen:[0,100,0,1],darkgrey:[169,169,169,1],darkkhaki:[189,183,107,1],darkmagenta:[139,0,139,1],darkolivegreen:[85,107,47,1],darkorange:[255,140,0,1],darkorchid:[153,50,204,1],darkred:[139,0,0,1],darksalmon:[233,150,122,1],darkseagreen:[143,188,143,1],darkslateblue:[72,61,139,1],darkslategray:[47,79,79,1],darkslategrey:[47,79,79,1],darkturquoise:[0,206,209,1],darkviolet:[148,0,211,1],deeppink:[255,20,147,1],deepskyblue:[0,191,255,1],dimgray:[105,105,105,1],dimgrey:[105,105,105,1],dodgerblue:[30,144,255,1],firebrick:[178,34,34,1],floralwhite:[255,250,240,1],forestgreen:[34,139,34,1],fuchsia:[255,0,255,1],gainsboro:[220,220,220,1],ghostwhite:[248,248,255,1],gold:[255,215,0,1],goldenrod:[218,165,32,1],gray:[128,128,128,1],green:[0,128,0,1],greenyellow:[173,255,47,1],grey:[128,128,128,1],honeydew:[240,255,240,1],hotpink:[255,105,180,1],indianred:[205,92,92,1],indigo:[75,0,130,1],ivory:[255,255,240,1],khaki:[240,230,140,1],lavender:[230,230,250,1],lavenderblush:[255,240,245,1],lawngreen:[124,252,0,1],lemonchiffon:[255,250,205,1],lightblue:[173,216,230,1],lightcoral:[240,128,128,1],lightcyan:[224,255,255,1],lightgoldenrodyellow:[250,250,210,1],lightgray:[211,211,211,1],lightgreen:[144,238,144,1],lightgrey:[211,211,211,1],lightpink:[255,182,193,1],lightsalmon:[255,160,122,1],lightseagreen:[32,178,170,1],lightskyblue:[135,206,250,1],lightslategray:[119,136,153,1],lightslategrey:[119,136,153,1],lightsteelblue:[176,196,222,1],lightyellow:[255,255,224,1],lime:[0,255,0,1],limegreen:[50,205,50,1],linen:[250,240,230,1],magenta:[255,0,255,1],maroon:[128,0,0,1],mediumaquamarine:[102,205,170,1],mediumblue:[0,0,205,1],mediumorchid:[186,85,211,1],mediumpurple:[147,112,219,1],mediumseagreen:[60,179,113,1],mediumslateblue:[123,104,238,1],mediumspringgreen:[0,250,154,1],mediumturquoise:[72,209,204,1],mediumvioletred:[199,21,133,1],midnightblue:[25,25,112,1],mintcream:[245,255,250,1],mistyrose:[255,228,225,1],moccasin:[255,228,181,1],navajowhite:[255,222,173,1],navy:[0,0,128,1],oldlace:[253,245,230,1],olive:[128,128,0,1],olivedrab:[107,142,35,1],orange:[255,165,0,1],orangered:[255,69,0,1],orchid:[218,112,214,1],palegoldenrod:[238,232,170,1],palegreen:[152,251,152,1],paleturquoise:[175,238,238,1],palevioletred:[219,112,147,1],papayawhip:[255,239,213,1],peachpuff:[255,218,185,1],peru:[205,133,63,1],pink:[255,192,203,1],plum:[221,160,221,1],powderblue:[176,224,230,1],purple:[128,0,128,1],red:[255,0,0,1],rosybrown:[188,143,143,1],royalblue:[65,105,225,1],saddlebrown:[139,69,19,1],salmon:[250,128,114,1],sandybrown:[244,164,96,1],seagreen:[46,139,87,1],seashell:[255,245,238,1],sienna:[160,82,45,1],silver:[192,192,192,1],skyblue:[135,206,235,1],slateblue:[106,90,205,1],slategray:[112,128,144,1],slategrey:[112,128,144,1],snow:[255,250,250,1],springgreen:[0,255,127,1],steelblue:[70,130,180,1],tan:[210,180,140,1],teal:[0,128,128,1],thistle:[216,191,216,1],tomato:[255,99,71,1],turquoise:[64,224,208,1],violet:[238,130,238,1],wheat:[245,222,179,1],white:[255,255,255,1],whitesmoke:[245,245,245,1],yellow:[255,255,0,1],yellowgreen:[154,205,50,1]};function q(e){return(e=Math.round(e))<0?0:e>255?255:e}function Z(e){return e<0?0:e>1?1:e}function Y(e){return e.length&&\"%\"===e.charAt(e.length-1)?q(parseFloat(e)/100*255):q(parseInt(e,10))}function K(e){return e.length&&\"%\"===e.charAt(e.length-1)?Z(parseFloat(e)/100):Z(parseFloat(e))}function Q(e,t,r){return r<0?r+=1:r>1&&(r-=1),6*r<1?e+(t-e)*r*6:2*r<1?t:3*r<2?e+(t-e)*(2/3-r)*6:e}function J(e,t,r){return e+(t-e)*r}function $(e,t,r,i,n){return e[0]=t,e[1]=r,e[2]=i,e[3]=n,e}function ee(e,t){return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e}var te=new W(20),re=null;function ie(e,t){re&&ee(re,t),re=te.put(e,re||t.slice())}function ne(e,t){var r=(parseFloat(e[0])%360+360)%360/360,i=K(e[1]),n=K(e[2]),a=n<=.5?n*(i+1):n+i-n*i,o=2*n-a;return $(t=t||[],q(255*Q(o,a,r+1/3)),q(255*Q(o,a,r)),q(255*Q(o,a,r-1/3)),1),4===e.length&&(t[3]=e[3]),t}j.parse=function(e,t){if(e){t=t||[];var r=te.get(e);if(r)return ee(t,r);var i,n=(e+=\"\").replace(/ /g,\"\").toLowerCase();if(n in X)return ee(t,X[n]),ie(e,t),t;if(\"#\"===n.charAt(0))return 4===n.length?(i=parseInt(n.substr(1),16))>=0&&i<=4095?($(t,(3840&i)>>4|(3840&i)>>8,240&i|(240&i)>>4,15&i|(15&i)<<4,1),ie(e,t),t):void $(t,0,0,0,1):7===n.length?(i=parseInt(n.substr(1),16))>=0&&i<=16777215?($(t,(16711680&i)>>16,(65280&i)>>8,255&i,1),ie(e,t),t):void $(t,0,0,0,1):void 0;var a=n.indexOf(\"(\"),o=n.indexOf(\")\");if(-1!==a&&o+1===n.length){var s=n.substr(0,a),l=n.substr(a+1,o-(a+1)).split(\",\"),h=1;switch(s){case\"rgba\":if(4!==l.length)return void $(t,0,0,0,1);h=K(l.pop());case\"rgb\":return 3!==l.length?void $(t,0,0,0,1):($(t,Y(l[0]),Y(l[1]),Y(l[2]),h),ie(e,t),t);case\"hsla\":return 4!==l.length?void $(t,0,0,0,1):(l[3]=K(l[3]),ne(l,t),ie(e,t),t);case\"hsl\":return 3!==l.length?void $(t,0,0,0,1):(ne(l,t),ie(e,t),t);default:return}}$(t,0,0,0,1)}},j.parseToFloat=function(e,t){if(t=j.parse(e,t))return t[0]/=255,t[1]/=255,t[2]/=255,t},j.lift=function(e,t){var r=j.parse(e);if(r){for(var i=0;i<3;i++)r[i]=t<0?r[i]*(1-t)|0:(255-r[i])*t+r[i]|0;return j.stringify(r,4===r.length?\"rgba\":\"rgb\")}},j.toHex=function(e){var t=j.parse(e);if(t)return((1<<24)+(t[0]<<16)+(t[1]<<8)+ +t[2]).toString(16).slice(1)},j.fastLerp=function(e,t,r){if(t&&t.length&&e>=0&&e<=1){r=r||[];var i=e*(t.length-1),n=Math.floor(i),a=Math.ceil(i),o=t[n],s=t[a],l=i-n;return r[0]=q(J(o[0],s[0],l)),r[1]=q(J(o[1],s[1],l)),r[2]=q(J(o[2],s[2],l)),r[3]=Z(J(o[3],s[3],l)),r}},j.fastMapToColor=j.fastLerp,j.lerp=function(e,t,r){if(t&&t.length&&e>=0&&e<=1){var i=e*(t.length-1),n=Math.floor(i),a=Math.ceil(i),o=j.parse(t[n]),s=j.parse(t[a]),l=i-n,h=j.stringify([q(J(o[0],s[0],l)),q(J(o[1],s[1],l)),q(J(o[2],s[2],l)),Z(J(o[3],s[3],l))],\"rgba\");return r?{color:h,leftIndex:n,rightIndex:a,value:i}:h}},j.mapToColor=j.lerp,j.modifyHSL=function(e,t,r,i){if(e=j.parse(e))return e=function(e){if(e){var t,r,i=e[0]/255,n=e[1]/255,a=e[2]/255,o=Math.min(i,n,a),s=Math.max(i,n,a),l=s-o,h=(s+o)/2;if(0===l)t=0,r=0;else{r=h<.5?l/(s+o):l/(2-s-o);var u=((s-i)/6+l/2)/l,c=((s-n)/6+l/2)/l,d=((s-a)/6+l/2)/l;i===s?t=d-c:n===s?t=1/3+u-d:a===s&&(t=2/3+c-u),t<0&&(t+=1),t>1&&(t-=1)}var f=[360*t,r,h];return null!=e[3]&&f.push(e[3]),f}}(e),null!=t&&(e[0]=(n=t,(n=Math.round(n))<0?0:n>360?360:n)),null!=r&&(e[1]=K(r)),null!=i&&(e[2]=K(i)),j.stringify(ne(e),\"rgba\");var n},j.modifyAlpha=function(e,t){if((e=j.parse(e))&&null!=t)return e[3]=Z(t),j.stringify(e,\"rgba\")},j.stringify=function(e,t){if(e&&e.length){var r=e[0]+\",\"+e[1]+\",\"+e[2];return\"rgba\"!==t&&\"hsva\"!==t&&\"hsla\"!==t||(r+=\",\"+e[3]),t+\"(\"+r+\")\"}};var ae=j.parseToFloat,oe={};function se(e){var t=Object.keys(e);t.sort();for(var r=[],i=0;i=0},getEnabledUniforms:function(){return this._enabledUniforms},getTextureUniforms:function(){return this._textureUniforms},set:function(e,t){if(\"object\"==typeof e)for(var r in e){var i=e[r];this.setUniform(r,i)}else this.setUniform(e,t)},get:function(e){var t=this.uniforms[e];if(t)return t.value},attachShader:function(e,t){var r=this.uniforms;this.uniforms=e.createUniforms(),this.shader=e;var i=this.uniforms;this._enabledUniforms=Object.keys(i),this._enabledUniforms.sort(),this._textureUniforms=this._enabledUniforms.filter((function(e){var t=this.uniforms[e].type;return\"t\"===t||\"tv\"===t}),this);var n=this.vertexDefines,a=this.fragmentDefines;if(this.vertexDefines=f.clone(e.vertexDefines),this.fragmentDefines=f.clone(e.fragmentDefines),t){for(var o in r)i[o]&&(i[o].value=r[o].value);f.defaults(this.vertexDefines,n),f.defaults(this.fragmentDefines,a)}var s={};for(var l in e.textures)s[l]={shaderType:e.textures[l].shaderType,type:e.textures[l].type,enabled:!(!t||!this._textureStatus[l])&&this._textureStatus[l].enabled};this._textureStatus=s,this._programKey=\"\"},clone:function(){var e=new this.constructor({name:this.name,shader:this.shader});for(var t in this.uniforms)e.uniforms[t].value=this.uniforms[t].value;return e.depthTest=this.depthTest,e.depthMask=this.depthMask,e.transparent=this.transparent,e.blend=this.blend,e.vertexDefines=f.clone(this.vertexDefines),e.fragmentDefines=f.clone(this.fragmentDefines),e.enableTexture(this.getEnabledTextures()),e.precision=this.precision,e},define:function(e,t,r){var i=this.vertexDefines,n=this.fragmentDefines;\"vertex\"!==e&&\"fragment\"!==e&&\"both\"!==e&&arguments.length<3&&(r=t,t=e,e=\"both\"),r=null!=r?r:null,\"vertex\"!==e&&\"both\"!==e||i[t]!==r&&(i[t]=r,this._programKey=\"\"),\"fragment\"!==e&&\"both\"!==e||n[t]!==r&&(n[t]=r,\"both\"!==e&&(this._programKey=\"\"))},undefine:function(e,t){\"vertex\"!==e&&\"fragment\"!==e&&\"both\"!==e&&arguments.length<2&&(t=e,e=\"both\"),\"vertex\"!==e&&\"both\"!==e||this.isDefined(\"vertex\",t)&&(delete this.vertexDefines[t],this._programKey=\"\"),\"fragment\"!==e&&\"both\"!==e||this.isDefined(\"fragment\",t)&&(delete this.fragmentDefines[t],\"both\"!==e&&(this._programKey=\"\"))},isDefined:function(e,t){switch(e){case\"vertex\":return void 0!==this.vertexDefines[t];case\"fragment\":return void 0!==this.fragmentDefines[t]}},getDefine:function(e,t){switch(e){case\"vertex\":return this.vertexDefines[t];case\"fragment\":return this.fragmentDefines[t]}},enableTexture:function(e){if(Array.isArray(e))for(var t=0;t0&&(n=1/Math.sqrt(n),e[0]=t[0]*n,e[1]=t[1]*n),e},fe.dot=function(e,t){return e[0]*t[0]+e[1]*t[1]},fe.cross=function(e,t,r){var i=t[0]*r[1]-t[1]*r[0];return e[0]=e[1]=0,e[2]=i,e},fe.lerp=function(e,t,r,i){var n=t[0],a=t[1];return e[0]=n+i*(r[0]-n),e[1]=a+i*(r[1]-a),e},fe.random=function(e,t){t=t||1;var r=2*GLMAT_RANDOM()*Math.PI;return e[0]=Math.cos(r)*t,e[1]=Math.sin(r)*t,e},fe.transformMat2=function(e,t,r){var i=t[0],n=t[1];return e[0]=r[0]*i+r[2]*n,e[1]=r[1]*i+r[3]*n,e},fe.transformMat2d=function(e,t,r){var i=t[0],n=t[1];return e[0]=r[0]*i+r[2]*n+r[4],e[1]=r[1]*i+r[3]*n+r[5],e},fe.transformMat3=function(e,t,r){var i=t[0],n=t[1];return e[0]=r[0]*i+r[3]*n+r[6],e[1]=r[1]*i+r[4]*n+r[7],e},fe.transformMat4=function(e,t,r){var i=t[0],n=t[1];return e[0]=r[0]*i+r[4]*n+r[12],e[1]=r[1]*i+r[5]*n+r[13],e},fe.forEach=(he=fe.create(),function(e,t,r,i,n,a){var o,s;for(t||(t=2),r||(r=0),s=i?Math.min(i*t+r,e.length):e.length,o=r;o0&&i.push(\"#define \"+n.toUpperCase()+\"_COUNT \"+a)}if(r)for(var o=0;ol.getMaxJointNumber()&&(d.USE_SKIN_MATRICES_TEXTURE=null),c+=\"\\n\"+Me(d)+\"\\n\"}a&&(c+=\"\\n#define INSTANCING\\n\");var f=c+Me(t.vertexDefines,s,u),p=c+Me(t.fragmentDefines,s,u),m=f+\"\\n\"+t.shader.vertex,g=[\"OES_standard_derivatives\",\"EXT_shader_texture_lod\"].filter((function(e){return null!=l.getGLExtension(e)}));g.indexOf(\"EXT_shader_texture_lod\")>=0&&(p+=\"\\n#define SUPPORT_TEXTURE_LOD\"),g.indexOf(\"OES_standard_derivatives\")>=0&&(p+=\"\\n#define SUPPORT_STANDARD_DERIVATIVES\");var _,v,y=function(e){for(var t=[],r=0;r=0){if(1!==s&&4!==s){ke();break}s=2,h=[]}else if(1!==s)if(4!==s)u(c),s=0;else{var d=c;Ie.indexOf(d)>=0||Re.indexOf(d)>=0||Be.indexOf(d)>=0?l[o].semantic=d:\"ignore\"===d||\"unconfigurable\"===d?l[o].ignore=!0:l[o].value=\"bool\"===e?\"true\"===d:parseFloat(d)}else l[o].value=\"bool\"===e?\"true\"===c:parseFloat(c),h=null;else{if(2!==s){ke();break}if(!(h instanceof Array)){ke();break}h.push(+i[++a])}else l[o].value=new U.Float32Array(h),h=null,s=5;else if(2===s){if(!(h instanceof Array)){ke();break}h.push(+i[++a])}else s=5;else s=4;else{if(0!==s&&3!==s){ke();break}s=1}}return l}function He(e,t){\"object\"==typeof e&&(t=e.fragment,e=e.vertex),e=Ue(e),t=Ue(t),this._shaderID=function(e,t){var r=\"vertex:\"+e+\"fragment:\"+t;if(ze[r])return ze[r];var i=f.genGUID();return ze[r]=i,Ge[i]={vertex:e,fragment:t},i}(e,t),this._vertexCode=He.parseImport(e),this._fragmentCode=He.parseImport(t),this.attributeSemantics={},this.matrixSemantics={},this.uniformSemantics={},this.matrixSemanticKeys=[],this.uniformTemplates={},this.attributes={},this.textures={},this.vertexDefines={},this.fragmentDefines={},this._parseAttributes(),this._parseUniforms(),this._parseDefines()}He.prototype={constructor:He,createUniforms:function(){var e={};for(var t in this.uniformTemplates){var r=this.uniformTemplates[t];e[t]={type:r.type,value:r.value()}}return e},_parseImport:function(){this._vertexCode=He.parseImport(this.vertex),this._fragmentCode=He.parseImport(this.fragment)},_addSemanticUniform:function(e,t,r){if(Ie.indexOf(r)>=0)this.attributeSemantics[r]={symbol:e,type:t};else if(Be.indexOf(r)>=0){var i=!1,n=r;r.match(/TRANSPOSE$/)&&(i=!0,n=r.slice(0,-9)),this.matrixSemantics[r]={symbol:e,type:t,isTranspose:i,semanticNoTranspose:n}}else Re.indexOf(r)>=0&&(this.uniformSemantics[r]={symbol:e,type:t})},_addMaterialUniform:function(e,t,r,i,n,a){a[e]={type:r,value:n?Ne.array:i||Ne[t],semantic:null}},_parseUniforms:function(){var e={},t=this;function r(e){return null!=e?function(){return e}:null}function i(i,n,a){var o=Ve(n,a),s=[];for(var l in o){var h=o[l],u=h.semantic,c=l,d=Pe[n],f=r(o[l].value);o[l].isArray&&(c+=\"[\"+o[l].arraySize+\"]\",d+=\"v\"),s.push(c),t._uniformList.push(l),h.ignore||(\"sampler2D\"!==n&&\"samplerCube\"!==n||(t.textures[l]={shaderType:\"fragment\",type:n}),u?t._addSemanticUniform(l,d,u):t._addMaterialUniform(l,n,d,f,o[l].isArray,e))}return s.length>0?\"uniform \"+n+\" \"+s.join(\",\")+\";\\n\":\"\"}this._uniformList=[],this._vertexCode=this._vertexCode.replace(Ce,i),this._fragmentCode=this._fragmentCode.replace(Ce,i),t.matrixSemanticKeys=Object.keys(this.matrixSemantics),this.uniformTemplates=e},_parseAttributes:function(){var e={},t=this;this._vertexCode=this._vertexCode.replace(De,(function(r,i,n){var a=Ve(i,n),o=Fe[i]||1,s=[];for(var l in a){var h=a[l].semantic;if(e[l]={type:\"float\",size:o,semantic:h||null},h){if(Ie.indexOf(h)<0)throw new Error('Unkown semantic \"'+h+'\"');t.attributeSemantics[h]={symbol:l,type:i}}s.push(l)}return\"attribute \"+i+\" \"+s.join(\",\")+\";\\n\"})),this.attributes=e},_parseDefines:function(){var e=this;function t(t,r,i){var n=e.fragmentDefines;return n[r]||(n[r]=\"false\"!==i&&(\"true\"===i||(i?isNaN(parseFloat(i))?i.trim():parseFloat(i):null))),\"\"}this._vertexCode=this._vertexCode.replace(Le,t),this._fragmentCode=this._fragmentCode.replace(Le,t)},clone:function(){var e=Ge[this._shaderID];return new He(e.vertex,e.fragment)}},Object.defineProperty&&(Object.defineProperty(He.prototype,\"shaderID\",{get:function(){return this._shaderID}}),Object.defineProperty(He.prototype,\"vertex\",{get:function(){return this._vertexCode}}),Object.defineProperty(He.prototype,\"fragment\",{get:function(){return this._fragmentCode}}),Object.defineProperty(He.prototype,\"uniforms\",{get:function(){return this._uniformList}}));var We=/(@import)\\s*([0-9a-zA-Z_\\-\\.]*)/g;He.parseImport=function(e){return e.replace(We,(function(e,t,r){return(e=He.source(r))?He.parseImport(e):(console.error('Shader chunk \"'+r+'\" not existed in library'),\"\")}))};var je=/(@export)\\s*([0-9a-zA-Z_\\-\\.]*)\\s*\\n([\\s\\S]*?)@end/g;He.import=function(e){e.replace(je,(function(e,t,r,i){if(i=i.replace(/(^[\\s\\t\\xa0\\u3000]+)|([\\u3000\\xa0\\s\\t]+\\x24)/g,\"\")){for(var n,a=r.split(\".\"),o=He.codes,s=0;s 0.0) {\\n if (texture2D(alphaMap, v_Texcoord).a <= alphaCutoff) {\\n discard;\\n }\\n }\\n gl_FragColor = vec4(0.0,0.0,0.0,1.0);\\n}\\n@end\";var Ze={create:function(){var e=new ce(16);return e[0]=1,e[1]=0,e[2]=0,e[3]=0,e[4]=0,e[5]=1,e[6]=0,e[7]=0,e[8]=0,e[9]=0,e[10]=1,e[11]=0,e[12]=0,e[13]=0,e[14]=0,e[15]=1,e},clone:function(e){var t=new ce(16);return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4],t[5]=e[5],t[6]=e[6],t[7]=e[7],t[8]=e[8],t[9]=e[9],t[10]=e[10],t[11]=e[11],t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15],t},copy:function(e,t){return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4],e[5]=t[5],e[6]=t[6],e[7]=t[7],e[8]=t[8],e[9]=t[9],e[10]=t[10],e[11]=t[11],e[12]=t[12],e[13]=t[13],e[14]=t[14],e[15]=t[15],e},identity:function(e){return e[0]=1,e[1]=0,e[2]=0,e[3]=0,e[4]=0,e[5]=1,e[6]=0,e[7]=0,e[8]=0,e[9]=0,e[10]=1,e[11]=0,e[12]=0,e[13]=0,e[14]=0,e[15]=1,e},transpose:function(e,t){if(e===t){var r=t[1],i=t[2],n=t[3],a=t[6],o=t[7],s=t[11];e[1]=t[4],e[2]=t[8],e[3]=t[12],e[4]=r,e[6]=t[9],e[7]=t[13],e[8]=i,e[9]=a,e[11]=t[14],e[12]=n,e[13]=o,e[14]=s}else e[0]=t[0],e[1]=t[4],e[2]=t[8],e[3]=t[12],e[4]=t[1],e[5]=t[5],e[6]=t[9],e[7]=t[13],e[8]=t[2],e[9]=t[6],e[10]=t[10],e[11]=t[14],e[12]=t[3],e[13]=t[7],e[14]=t[11],e[15]=t[15];return e},invert:function(e,t){var r=t[0],i=t[1],n=t[2],a=t[3],o=t[4],s=t[5],l=t[6],h=t[7],u=t[8],c=t[9],d=t[10],f=t[11],p=t[12],m=t[13],g=t[14],_=t[15],v=r*s-i*o,y=r*l-n*o,x=r*h-a*o,b=i*l-n*s,w=i*h-a*s,T=n*h-a*l,S=u*m-c*p,M=u*g-d*p,A=u*_-f*p,E=c*g-d*m,C=c*_-f*m,D=d*_-f*g,L=v*D-y*C+x*E+b*A-w*M+T*S;return L?(L=1/L,e[0]=(s*D-l*C+h*E)*L,e[1]=(n*C-i*D-a*E)*L,e[2]=(m*T-g*w+_*b)*L,e[3]=(d*w-c*T-f*b)*L,e[4]=(l*A-o*D-h*M)*L,e[5]=(r*D-n*A+a*M)*L,e[6]=(g*x-p*T-_*y)*L,e[7]=(u*T-d*x+f*y)*L,e[8]=(o*C-s*A+h*S)*L,e[9]=(i*A-r*C-a*S)*L,e[10]=(p*w-m*x+_*v)*L,e[11]=(c*x-u*w-f*v)*L,e[12]=(s*M-o*E-l*S)*L,e[13]=(r*E-i*M+n*S)*L,e[14]=(m*y-p*b-g*v)*L,e[15]=(u*b-c*y+d*v)*L,e):null},adjoint:function(e,t){var r=t[0],i=t[1],n=t[2],a=t[3],o=t[4],s=t[5],l=t[6],h=t[7],u=t[8],c=t[9],d=t[10],f=t[11],p=t[12],m=t[13],g=t[14],_=t[15];return e[0]=s*(d*_-f*g)-c*(l*_-h*g)+m*(l*f-h*d),e[1]=-(i*(d*_-f*g)-c*(n*_-a*g)+m*(n*f-a*d)),e[2]=i*(l*_-h*g)-s*(n*_-a*g)+m*(n*h-a*l),e[3]=-(i*(l*f-h*d)-s*(n*f-a*d)+c*(n*h-a*l)),e[4]=-(o*(d*_-f*g)-u*(l*_-h*g)+p*(l*f-h*d)),e[5]=r*(d*_-f*g)-u*(n*_-a*g)+p*(n*f-a*d),e[6]=-(r*(l*_-h*g)-o*(n*_-a*g)+p*(n*h-a*l)),e[7]=r*(l*f-h*d)-o*(n*f-a*d)+u*(n*h-a*l),e[8]=o*(c*_-f*m)-u*(s*_-h*m)+p*(s*f-h*c),e[9]=-(r*(c*_-f*m)-u*(i*_-a*m)+p*(i*f-a*c)),e[10]=r*(s*_-h*m)-o*(i*_-a*m)+p*(i*h-a*s),e[11]=-(r*(s*f-h*c)-o*(i*f-a*c)+u*(i*h-a*s)),e[12]=-(o*(c*g-d*m)-u*(s*g-l*m)+p*(s*d-l*c)),e[13]=r*(c*g-d*m)-u*(i*g-n*m)+p*(i*d-n*c),e[14]=-(r*(s*g-l*m)-o*(i*g-n*m)+p*(i*l-n*s)),e[15]=r*(s*d-l*c)-o*(i*d-n*c)+u*(i*l-n*s),e},determinant:function(e){var t=e[0],r=e[1],i=e[2],n=e[3],a=e[4],o=e[5],s=e[6],l=e[7],h=e[8],u=e[9],c=e[10],d=e[11],f=e[12],p=e[13],m=e[14],g=e[15];return(t*o-r*a)*(c*g-d*m)-(t*s-i*a)*(u*g-d*p)+(t*l-n*a)*(u*m-c*p)+(r*s-i*o)*(h*g-d*f)-(r*l-n*o)*(h*m-c*f)+(i*l-n*s)*(h*p-u*f)},multiply:function(e,t,r){var i=t[0],n=t[1],a=t[2],o=t[3],s=t[4],l=t[5],h=t[6],u=t[7],c=t[8],d=t[9],f=t[10],p=t[11],m=t[12],g=t[13],_=t[14],v=t[15],y=r[0],x=r[1],b=r[2],w=r[3];return e[0]=y*i+x*s+b*c+w*m,e[1]=y*n+x*l+b*d+w*g,e[2]=y*a+x*h+b*f+w*_,e[3]=y*o+x*u+b*p+w*v,y=r[4],x=r[5],b=r[6],w=r[7],e[4]=y*i+x*s+b*c+w*m,e[5]=y*n+x*l+b*d+w*g,e[6]=y*a+x*h+b*f+w*_,e[7]=y*o+x*u+b*p+w*v,y=r[8],x=r[9],b=r[10],w=r[11],e[8]=y*i+x*s+b*c+w*m,e[9]=y*n+x*l+b*d+w*g,e[10]=y*a+x*h+b*f+w*_,e[11]=y*o+x*u+b*p+w*v,y=r[12],x=r[13],b=r[14],w=r[15],e[12]=y*i+x*s+b*c+w*m,e[13]=y*n+x*l+b*d+w*g,e[14]=y*a+x*h+b*f+w*_,e[15]=y*o+x*u+b*p+w*v,e},multiplyAffine:function(e,t,r){var i=t[0],n=t[1],a=t[2],o=t[4],s=t[5],l=t[6],h=t[8],u=t[9],c=t[10],d=t[12],f=t[13],p=t[14],m=r[0],g=r[1],_=r[2];return e[0]=m*i+g*o+_*h,e[1]=m*n+g*s+_*u,e[2]=m*a+g*l+_*c,m=r[4],g=r[5],_=r[6],e[4]=m*i+g*o+_*h,e[5]=m*n+g*s+_*u,e[6]=m*a+g*l+_*c,m=r[8],g=r[9],_=r[10],e[8]=m*i+g*o+_*h,e[9]=m*n+g*s+_*u,e[10]=m*a+g*l+_*c,m=r[12],g=r[13],_=r[14],e[12]=m*i+g*o+_*h+d,e[13]=m*n+g*s+_*u+f,e[14]=m*a+g*l+_*c+p,e}};Ze.mul=Ze.multiply,Ze.mulAffine=Ze.multiplyAffine,Ze.translate=function(e,t,r){var i,n,a,o,s,l,h,u,c,d,f,p,m=r[0],g=r[1],_=r[2];return t===e?(e[12]=t[0]*m+t[4]*g+t[8]*_+t[12],e[13]=t[1]*m+t[5]*g+t[9]*_+t[13],e[14]=t[2]*m+t[6]*g+t[10]*_+t[14],e[15]=t[3]*m+t[7]*g+t[11]*_+t[15]):(i=t[0],n=t[1],a=t[2],o=t[3],s=t[4],l=t[5],h=t[6],u=t[7],c=t[8],d=t[9],f=t[10],p=t[11],e[0]=i,e[1]=n,e[2]=a,e[3]=o,e[4]=s,e[5]=l,e[6]=h,e[7]=u,e[8]=c,e[9]=d,e[10]=f,e[11]=p,e[12]=i*m+s*g+c*_+t[12],e[13]=n*m+l*g+d*_+t[13],e[14]=a*m+h*g+f*_+t[14],e[15]=o*m+u*g+p*_+t[15]),e},Ze.scale=function(e,t,r){var i=r[0],n=r[1],a=r[2];return e[0]=t[0]*i,e[1]=t[1]*i,e[2]=t[2]*i,e[3]=t[3]*i,e[4]=t[4]*n,e[5]=t[5]*n,e[6]=t[6]*n,e[7]=t[7]*n,e[8]=t[8]*a,e[9]=t[9]*a,e[10]=t[10]*a,e[11]=t[11]*a,e[12]=t[12],e[13]=t[13],e[14]=t[14],e[15]=t[15],e},Ze.rotate=function(e,t,r,i){var n,a,o,s,l,h,u,c,d,f,p,m,g,_,v,y,x,b,w,T,S,M,A,E,C=i[0],D=i[1],L=i[2],P=Math.sqrt(C*C+D*D+L*L);return Math.abs(P)0&&(a=1/Math.sqrt(a),e[0]=t[0]*a,e[1]=t[1]*a,e[2]=t[2]*a),e},Ke.dot=function(e,t){return e[0]*t[0]+e[1]*t[1]+e[2]*t[2]},Ke.cross=function(e,t,r){var i=t[0],n=t[1],a=t[2],o=r[0],s=r[1],l=r[2];return e[0]=n*l-a*s,e[1]=a*o-i*l,e[2]=i*s-n*o,e},Ke.lerp=function(e,t,r,i){var n=t[0],a=t[1],o=t[2];return e[0]=n+i*(r[0]-n),e[1]=a+i*(r[1]-a),e[2]=o+i*(r[2]-o),e},Ke.random=function(e,t){t=t||1;var r=2*de()*Math.PI,i=2*de()-1,n=Math.sqrt(1-i*i)*t;return e[0]=Math.cos(r)*n,e[1]=Math.sin(r)*n,e[2]=i*t,e},Ke.transformMat4=function(e,t,r){var i=t[0],n=t[1],a=t[2],o=r[3]*i+r[7]*n+r[11]*a+r[15];return o=o||1,e[0]=(r[0]*i+r[4]*n+r[8]*a+r[12])/o,e[1]=(r[1]*i+r[5]*n+r[9]*a+r[13])/o,e[2]=(r[2]*i+r[6]*n+r[10]*a+r[14])/o,e},Ke.transformMat3=function(e,t,r){var i=t[0],n=t[1],a=t[2];return e[0]=i*r[0]+n*r[3]+a*r[6],e[1]=i*r[1]+n*r[4]+a*r[7],e[2]=i*r[2]+n*r[5]+a*r[8],e},Ke.transformQuat=function(e,t,r){var i=t[0],n=t[1],a=t[2],o=r[0],s=r[1],l=r[2],h=r[3],u=h*i+s*a-l*n,c=h*n+l*i-o*a,d=h*a+o*n-s*i,f=-o*i-s*n-l*a;return e[0]=u*h+f*-o+c*-l-d*-s,e[1]=c*h+f*-s+d*-o-u*-l,e[2]=d*h+f*-l+u*-s-c*-o,e},Ke.rotateX=function(e,t,r,i){var n=[],a=[];return n[0]=t[0]-r[0],n[1]=t[1]-r[1],n[2]=t[2]-r[2],a[0]=n[0],a[1]=n[1]*Math.cos(i)-n[2]*Math.sin(i),a[2]=n[1]*Math.sin(i)+n[2]*Math.cos(i),e[0]=a[0]+r[0],e[1]=a[1]+r[1],e[2]=a[2]+r[2],e},Ke.rotateY=function(e,t,r,i){var n=[],a=[];return n[0]=t[0]-r[0],n[1]=t[1]-r[1],n[2]=t[2]-r[2],a[0]=n[2]*Math.sin(i)+n[0]*Math.cos(i),a[1]=n[1],a[2]=n[2]*Math.cos(i)-n[0]*Math.sin(i),e[0]=a[0]+r[0],e[1]=a[1]+r[1],e[2]=a[2]+r[2],e},Ke.rotateZ=function(e,t,r,i){var n=[],a=[];return n[0]=t[0]-r[0],n[1]=t[1]-r[1],n[2]=t[2]-r[2],a[0]=n[0]*Math.cos(i)-n[1]*Math.sin(i),a[1]=n[0]*Math.sin(i)+n[1]*Math.cos(i),a[2]=n[2],e[0]=a[0]+r[0],e[1]=a[1]+r[1],e[2]=a[2]+r[2],e},Ke.forEach=function(){var e=Ke.create();return function(t,r,i,n,a,o){var s,l;for(r||(r=3),i||(i=0),l=n?Math.min(n*r+i,t.length):t.length,s=i;s1?0:Math.acos(n)};const Qe=Ke;Xe.import(qe);var Je=Ye.create,$e={};function et(e){return e.material}function tt(e,t,r){return t.uniforms[r].value}function rt(e,t,r,i){return r!==i}function it(e){return!0}function nt(){}var at={float:S,byte:5120,ubyte:T,short:5122,ushort:5123};function ot(e,t,r){this.availableAttributes=e,this.availableAttributeSymbols=t,this.indicesBuffer=r,this.vao=null}function st(e){var t,r;this.bind=function(e){t||((t=U.createCanvas()).width=t.height=1,t.getContext(\"2d\"));var i=e.gl,n=!r;n&&(r=i.createTexture()),i.bindTexture(i.TEXTURE_2D,r),n&&i.texImage2D(i.TEXTURE_2D,0,i.RGBA,i.RGBA,i.UNSIGNED_BYTE,t)},this.unbind=function(e){e.gl.bindTexture(e.gl.TEXTURE_2D,null)},this.isRenderable=function(){return!0}}var lt=m.extend((function(){return{canvas:null,_width:100,_height:100,devicePixelRatio:\"undefined\"!=typeof window&&window.devicePixelRatio||1,clearColor:[0,0,0,0],clearBit:17664,alpha:!0,depth:!0,stencil:!1,antialias:!0,premultipliedAlpha:!0,preserveDrawingBuffer:!1,throwError:!0,gl:null,viewport:{},maxJointNumber:20,__currentFrameBuffer:null,_viewportStack:[],_clearStack:[],_sceneRendering:null}}),(function(){this.canvas||(this.canvas=U.createCanvas());var e=this.canvas;try{var t={alpha:this.alpha,depth:this.depth,stencil:this.stencil,antialias:this.antialias,premultipliedAlpha:this.premultipliedAlpha,preserveDrawingBuffer:this.preserveDrawingBuffer};if(this.gl=e.getContext(\"webgl\",t)||e.getContext(\"experimental-webgl\",t),!this.gl)throw new Error;this._glinfo=new v(this.gl),this.gl.targetRenderer&&console.error(\"Already created a renderer\"),this.gl.targetRenderer=this,this.resize()}catch(e){throw\"Error creating WebGL Context \"+e}this._programMgr=new Ee(this),this._placeholderTexture=new st(this)}),{resize:function(e,t){var r=this.canvas,i=this.devicePixelRatio;null!=e?(r.style&&(r.style.width=e+\"px\",r.style.height=t+\"px\"),r.width=e*i,r.height=t*i,this._width=e,this._height=t):(this._width=r.width/i,this._height=r.height/i),this.setViewport(0,0,this._width,this._height)},getWidth:function(){return this._width},getHeight:function(){return this._height},getViewportAspect:function(){var e=this.viewport;return e.width/e.height},setDevicePixelRatio:function(e){this.devicePixelRatio=e,this.resize(this._width,this._height)},getDevicePixelRatio:function(){return this.devicePixelRatio},getGLExtension:function(e){return this._glinfo.getExtension(e)},getGLParameter:function(e){return this._glinfo.getParameter(e)},setViewport:function(e,t,r,i,n){if(\"object\"==typeof e){var a=e;e=a.x,t=a.y,r=a.width,i=a.height,n=a.devicePixelRatio}n=n||this.devicePixelRatio,this.gl.viewport(e*n,t*n,r*n,i*n),this.viewport={x:e,y:t,width:r,height:i,devicePixelRatio:n}},saveViewport:function(){this._viewportStack.push(this.viewport)},restoreViewport:function(){this._viewportStack.length>0&&this.setViewport(this._viewportStack.pop())},saveClear:function(){this._clearStack.push({clearBit:this.clearBit,clearColor:this.clearColor})},restoreClear:function(){if(this._clearStack.length>0){var e=this._clearStack.pop();this.clearColor=e.clearColor,this.clearBit=e.clearBit}},bindSceneRendering:function(e){this._sceneRendering=e},render:function(e,t,r,i){var n=this.gl,a=this.clearColor;if(this.clearBit){n.colorMask(!0,!0,!0,!0),n.depthMask(!0);var o=this.viewport,s=!1,l=o.devicePixelRatio;(o.width!==this._width||o.height!==this._height||l&&l!==this.devicePixelRatio||o.x||o.y)&&(s=!0,n.enable(n.SCISSOR_TEST),n.scissor(o.x*l,o.y*l,o.width*l,o.height*l)),n.clearColor(a[0],a[1],a[2],a[3]),n.clear(this.clearBit),s&&n.disable(n.SCISSOR_TEST)}if(r||e.update(!1),e.updateLights(),t=t||e.getMainCamera()){t.update();var h=e.updateRenderList(t,!0);this._sceneRendering=e;var u=h.opaque,c=h.transparent,d=e.material;e.trigger(\"beforerender\",this,e,t,h),i?(this.renderPreZ(u,e,t),n.depthFunc(n.LEQUAL)):n.depthFunc(n.LESS);for(var f=Je(),p=Qe.create(),m=0;m0){var s=e[n-1],l=s.joints?s.joints.length:0;if((a.joints?a.joints.length:0)===l&&a.material===s.material&&a.lightGroup===s.lightGroup){a.__program=s.__program;continue}}var h=this._programMgr.getProgram(a,o,t);this.validateProgram(h),a.__program=h}},renderPass:function(e,t,r){this.trigger(\"beforerenderpass\",this,e,t,r),(r=r||{}).getMaterial=r.getMaterial||et,r.getUniform=r.getUniform||tt,r.isMaterialChanged=r.isMaterialChanged||rt,r.beforeRender=r.beforeRender||nt,r.afterRender=r.afterRender||nt;var i=r.ifRender||it;this.updatePrograms(e,this._sceneRendering,r),r.sortCompare&&e.sort(r.sortCompare);var n=this.viewport,a=n.devicePixelRatio,o=[n.x*a,n.y*a,n.width*a,n.height*a],s=this.devicePixelRatio,l=this.__currentFrameBuffer?[this.__currentFrameBuffer.getTextureWidth(),this.__currentFrameBuffer.getTextureHeight()]:[this._width*s,this._height*s],h=[o[2],o[3]],u=Date.now();t?(Ye.copy(ht.VIEW,t.viewMatrix.array),Ye.copy(ht.PROJECTION,t.projectionMatrix.array),Ye.copy(ht.VIEWINVERSE,t.worldTransform.array)):(Ye.identity(ht.VIEW),Ye.identity(ht.PROJECTION),Ye.identity(ht.VIEWINVERSE)),Ye.multiply(ht.VIEWPROJECTION,ht.PROJECTION,ht.VIEW),Ye.invert(ht.PROJECTIONINVERSE,ht.PROJECTION),Ye.invert(ht.VIEWPROJECTIONINVERSE,ht.VIEWPROJECTION);for(var c,d,f,p,m,g,_,v,y,x,b,w,T=this.gl,S=this._sceneRendering,M=0;Mthis.getMaxJointNumber()){var a=n.getSubSkinMatricesTexture(e.__uid__,e.joints);t.useTextureSlot(this,a,r),t.setUniform(i,\"1i\",\"skinMatricesTexture\",r),t.setUniform(i,\"1f\",\"skinMatricesTextureSize\",a.width)}else{var o=n.getSubSkinMatrices(e.__uid__,e.joints);t.setUniformOfSemantic(i,\"SKIN_MATRIX\",o)}},_renderObject:function(e,t,r){var i=this.gl,n=e.geometry,a=e.mode;null==a&&(a=4);var o=null,s=e.isInstancedMesh&&e.isInstancedMesh();if(!s||(o=this.getGLExtension(\"ANGLE_instanced_arrays\"))){var l;if(s&&(l=this._bindInstancedAttributes(e,r,o)),t.indicesBuffer){var h=this.getGLExtension(\"OES_element_index_uint\")&&n.indices instanceof Uint32Array?i.UNSIGNED_INT:i.UNSIGNED_SHORT;s?o.drawElementsInstancedANGLE(a,t.indicesBuffer.count,h,0,e.getInstanceCount()):i.drawElements(a,t.indicesBuffer.count,h,0)}else s?o.drawArraysInstancedANGLE(a,0,n.vertexCount,e.getInstanceCount()):i.drawArrays(a,0,n.vertexCount);if(s)for(var u=0;ur?r:e}ct.add=function(e,t,r){return Qe.add(e.array,t.array,r.array),e._dirty=!0,e},ct.set=function(e,t,r,i){Qe.set(e.array,t,r,i),e._dirty=!0},ct.copy=function(e,t){return Qe.copy(e.array,t.array),e._dirty=!0,e},ct.cross=function(e,t,r){return Qe.cross(e.array,t.array,r.array),e._dirty=!0,e},ct.distance=ct.dist=function(e,t){return Qe.distance(e.array,t.array)},ct.divide=ct.div=function(e,t,r){return Qe.divide(e.array,t.array,r.array),e._dirty=!0,e},ct.dot=function(e,t){return Qe.dot(e.array,t.array)},ct.len=function(e){return Qe.length(e.array)},ct.lerp=function(e,t,r,i){return Qe.lerp(e.array,t.array,r.array,i),e._dirty=!0,e},ct.min=function(e,t,r){return Qe.min(e.array,t.array,r.array),e._dirty=!0,e},ct.max=function(e,t,r){return Qe.max(e.array,t.array,r.array),e._dirty=!0,e},ct.multiply=ct.mul=function(e,t,r){return Qe.multiply(e.array,t.array,r.array),e._dirty=!0,e},ct.negate=function(e,t){return Qe.negate(e.array,t.array),e._dirty=!0,e},ct.normalize=function(e,t){return Qe.normalize(e.array,t.array),e._dirty=!0,e},ct.random=function(e,t){return Qe.random(e.array,t),e._dirty=!0,e},ct.scale=function(e,t,r){return Qe.scale(e.array,t.array,r),e._dirty=!0,e},ct.scaleAndAdd=function(e,t,r,i){return Qe.scaleAndAdd(e.array,t.array,r.array,i),e._dirty=!0,e},ct.squaredDistance=ct.sqrDist=function(e,t){return Qe.sqrDist(e.array,t.array)},ct.squaredLength=ct.sqrLen=function(e){return Qe.sqrLen(e.array)},ct.subtract=ct.sub=function(e,t,r){return Qe.subtract(e.array,t.array,r.array),e._dirty=!0,e},ct.transformMat3=function(e,t,r){return Qe.transformMat3(e.array,t.array,r.array),e._dirty=!0,e},ct.transformMat4=function(e,t,r){return Qe.transformMat4(e.array,t.array,r.array),e._dirty=!0,e},ct.transformQuat=function(e,t,r){return Qe.transformQuat(e.array,t.array,r.array),e._dirty=!0,e};var mt=Math.atan2,gt=Math.asin,_t=Math.abs;ct.eulerFromQuat=function(e,t,r){e._dirty=!0,t=t.array;var i=e.array,n=t[0],a=t[1],o=t[2],s=t[3],l=n*n,h=a*a,u=o*o,c=s*s;switch(r=(r||\"XYZ\").toUpperCase()){case\"XYZ\":i[0]=mt(2*(n*s-a*o),c-l-h+u),i[1]=gt(pt(2*(n*o+a*s),-1,1)),i[2]=mt(2*(o*s-n*a),c+l-h-u);break;case\"YXZ\":i[0]=gt(pt(2*(n*s-a*o),-1,1)),i[1]=mt(2*(n*o+a*s),c-l-h+u),i[2]=mt(2*(n*a+o*s),c-l+h-u);break;case\"ZXY\":i[0]=gt(pt(2*(n*s+a*o),-1,1)),i[1]=mt(2*(a*s-o*n),c-l-h+u),i[2]=mt(2*(o*s-n*a),c-l+h-u);break;case\"ZYX\":i[0]=mt(2*(n*s+o*a),c-l-h+u),i[1]=gt(pt(2*(a*s-n*o),-1,1)),i[2]=mt(2*(n*a+o*s),c+l-h-u);break;case\"YZX\":i[0]=mt(2*(n*s-o*a),c-l+h-u),i[1]=mt(2*(a*s-n*o),c+l-h-u),i[2]=gt(pt(2*(n*a+o*s),-1,1));break;case\"XZY\":i[0]=mt(2*(n*s+a*o),c-l+h-u),i[1]=mt(2*(n*o+a*s),c+l-h-u),i[2]=gt(pt(2*(o*s-n*a),-1,1));break;default:console.warn(\"Unkown order: \"+r)}return e},ct.eulerFromMat3=function(e,t,r){var i=t.array,n=i[0],a=i[3],o=i[6],s=i[1],l=i[4],h=i[7],u=i[2],c=i[5],d=i[8],f=e.array;switch(r=(r||\"XYZ\").toUpperCase()){case\"XYZ\":f[1]=gt(pt(o,-1,1)),_t(o)<.99999?(f[0]=mt(-h,d),f[2]=mt(-a,n)):(f[0]=mt(c,l),f[2]=0);break;case\"YXZ\":f[0]=gt(-pt(h,-1,1)),_t(h)<.99999?(f[1]=mt(o,d),f[2]=mt(s,l)):(f[1]=mt(-u,n),f[2]=0);break;case\"ZXY\":f[0]=gt(pt(c,-1,1)),_t(c)<.99999?(f[1]=mt(-u,d),f[2]=mt(-a,l)):(f[1]=0,f[2]=mt(s,n));break;case\"ZYX\":f[1]=gt(-pt(u,-1,1)),_t(u)<.99999?(f[0]=mt(c,d),f[2]=mt(s,n)):(f[0]=0,f[2]=mt(-a,l));break;case\"YZX\":f[2]=gt(pt(s,-1,1)),_t(s)<.99999?(f[0]=mt(-h,l),f[1]=mt(-u,n)):(f[0]=0,f[1]=mt(o,d));break;case\"XZY\":f[2]=gt(-pt(a,-1,1)),_t(a)<.99999?(f[0]=mt(c,l),f[1]=mt(o,n)):(f[0]=mt(-h,d),f[1]=0);break;default:console.warn(\"Unkown order: \"+r)}return e._dirty=!0,e},Object.defineProperties(ct,{POSITIVE_X:{get:function(){return new ct(1,0,0)}},NEGATIVE_X:{get:function(){return new ct(-1,0,0)}},POSITIVE_Y:{get:function(){return new ct(0,1,0)}},NEGATIVE_Y:{get:function(){return new ct(0,-1,0)}},POSITIVE_Z:{get:function(){return new ct(0,0,1)}},NEGATIVE_Z:{get:function(){return new ct(0,0,-1)}},UP:{get:function(){return new ct(0,1,0)}},ZERO:{get:function(){return new ct}}});const vt=ct;var yt,xt,bt,wt,Tt,St=function(e,t){this.origin=e||new vt,this.direction=t||new vt};St.prototype={constructor:St,intersectPlane:function(e,t){var r=e.normal.array,i=e.distance,n=this.origin.array,a=this.direction.array,o=Qe.dot(r,a);if(0===o)return null;t||(t=new vt);var s=(Qe.dot(r,n)-i)/o;return Qe.scaleAndAdd(t.array,n,a,-s),t._dirty=!0,t},mirrorAgainstPlane:function(e){var t=Qe.dot(e.normal.array,this.direction.array);Qe.scaleAndAdd(this.direction.array,this.direction.array,e.normal.array,2*-t),this.direction._dirty=!0},distanceToPoint:(Tt=Qe.create(),function(e){Qe.sub(Tt,e,this.origin.array);var t=Qe.dot(Tt,this.direction.array);if(t<0)return Qe.distance(this.origin.array,e);var r=Qe.lenSquared(Tt);return Math.sqrt(r-t*t)}),intersectSphere:function(){var e=Qe.create();return function(t,r,i){var n=this.origin.array,a=this.direction.array;t=t.array,Qe.sub(e,t,n);var o=Qe.dot(e,a),s=Qe.squaredLength(e)-o*o,l=r*r;if(!(s>l)){var h=Math.sqrt(l-s),u=o-h,c=o+h;return i||(i=new vt),u<0?c<0?null:(Qe.scaleAndAdd(i.array,n,a,c),i):(Qe.scaleAndAdd(i.array,n,a,u),i)}}}(),intersectBoundingBox:function(e,t){var r,i,n,a,o,s,l=this.direction.array,h=this.origin.array,u=e.min.array,c=e.max.array,d=1/l[0],f=1/l[1],p=1/l[2];if(d>=0?(r=(u[0]-h[0])*d,i=(c[0]-h[0])*d):(i=(u[0]-h[0])*d,r=(c[0]-h[0])*d),f>=0?(n=(u[1]-h[1])*f,a=(c[1]-h[1])*f):(a=(u[1]-h[1])*f,n=(c[1]-h[1])*f),r>a||n>i)return null;if((n>r||r!=r)&&(r=n),(a=0?(o=(u[2]-h[2])*p,s=(c[2]-h[2])*p):(s=(u[2]-h[2])*p,o=(c[2]-h[2])*p),r>s||o>i)return null;if((o>r||r!=r)&&(r=o),(s=0?r:i;return t||(t=new vt),Qe.scaleAndAdd(t.array,h,l,m),t},intersectTriangle:(yt=Qe.create(),xt=Qe.create(),bt=Qe.create(),wt=Qe.create(),function(e,t,r,i,n,a){var o=this.direction.array,s=this.origin.array;e=e.array,t=t.array,r=r.array,Qe.sub(yt,t,e),Qe.sub(xt,r,e),Qe.cross(wt,xt,o);var l=Qe.dot(yt,wt);if(i){if(l>-1e-5)return null}else if(l>-1e-5&&l<1e-5)return null;Qe.sub(bt,s,e);var h=Qe.dot(wt,bt)/l;if(h<0||h>1)return null;Qe.cross(wt,yt,bt);var u=Qe.dot(o,wt)/l;if(u<0||u>1||h+u>1)return null;Qe.cross(wt,yt,xt);var c=-Qe.dot(bt,wt)/l;return c<0?null:(n||(n=new vt),a&&vt.set(a,1-h-u,h,u),Qe.scaleAndAdd(n.array,s,o,c),n)}),applyTransform:function(e){vt.add(this.direction,this.direction,this.origin),vt.transformMat4(this.origin,this.origin,e),vt.transformMat4(this.direction,this.direction,e),vt.sub(this.direction,this.direction,this.origin),vt.normalize(this.direction,this.direction)},copy:function(e){vt.copy(this.origin,e.origin),vt.copy(this.direction,e.direction)},clone:function(){var e=new St;return e.copy(this),e}};const Mt=St;var At={create:function(){var e=new ce(4);return e[0]=0,e[1]=0,e[2]=0,e[3]=0,e},clone:function(e){var t=new ce(4);return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t},fromValues:function(e,t,r,i){var n=new ce(4);return n[0]=e,n[1]=t,n[2]=r,n[3]=i,n},copy:function(e,t){return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e},set:function(e,t,r,i,n){return e[0]=t,e[1]=r,e[2]=i,e[3]=n,e},add:function(e,t,r){return e[0]=t[0]+r[0],e[1]=t[1]+r[1],e[2]=t[2]+r[2],e[3]=t[3]+r[3],e},subtract:function(e,t,r){return e[0]=t[0]-r[0],e[1]=t[1]-r[1],e[2]=t[2]-r[2],e[3]=t[3]-r[3],e}};At.sub=At.subtract,At.multiply=function(e,t,r){return e[0]=t[0]*r[0],e[1]=t[1]*r[1],e[2]=t[2]*r[2],e[3]=t[3]*r[3],e},At.mul=At.multiply,At.divide=function(e,t,r){return e[0]=t[0]/r[0],e[1]=t[1]/r[1],e[2]=t[2]/r[2],e[3]=t[3]/r[3],e},At.div=At.divide,At.min=function(e,t,r){return e[0]=Math.min(t[0],r[0]),e[1]=Math.min(t[1],r[1]),e[2]=Math.min(t[2],r[2]),e[3]=Math.min(t[3],r[3]),e},At.max=function(e,t,r){return e[0]=Math.max(t[0],r[0]),e[1]=Math.max(t[1],r[1]),e[2]=Math.max(t[2],r[2]),e[3]=Math.max(t[3],r[3]),e},At.scale=function(e,t,r){return e[0]=t[0]*r,e[1]=t[1]*r,e[2]=t[2]*r,e[3]=t[3]*r,e},At.scaleAndAdd=function(e,t,r,i){return e[0]=t[0]+r[0]*i,e[1]=t[1]+r[1]*i,e[2]=t[2]+r[2]*i,e[3]=t[3]+r[3]*i,e},At.distance=function(e,t){var r=t[0]-e[0],i=t[1]-e[1],n=t[2]-e[2],a=t[3]-e[3];return Math.sqrt(r*r+i*i+n*n+a*a)},At.dist=At.distance,At.squaredDistance=function(e,t){var r=t[0]-e[0],i=t[1]-e[1],n=t[2]-e[2],a=t[3]-e[3];return r*r+i*i+n*n+a*a},At.sqrDist=At.squaredDistance,At.length=function(e){var t=e[0],r=e[1],i=e[2],n=e[3];return Math.sqrt(t*t+r*r+i*i+n*n)},At.len=At.length,At.squaredLength=function(e){var t=e[0],r=e[1],i=e[2],n=e[3];return t*t+r*r+i*i+n*n},At.sqrLen=At.squaredLength,At.negate=function(e,t){return e[0]=-t[0],e[1]=-t[1],e[2]=-t[2],e[3]=-t[3],e},At.inverse=function(e,t){return e[0]=1/t[0],e[1]=1/t[1],e[2]=1/t[2],e[3]=1/t[3],e},At.normalize=function(e,t){var r=t[0],i=t[1],n=t[2],a=t[3],o=r*r+i*i+n*n+a*a;return o>0&&(o=1/Math.sqrt(o),e[0]=t[0]*o,e[1]=t[1]*o,e[2]=t[2]*o,e[3]=t[3]*o),e},At.dot=function(e,t){return e[0]*t[0]+e[1]*t[1]+e[2]*t[2]+e[3]*t[3]},At.lerp=function(e,t,r,i){var n=t[0],a=t[1],o=t[2],s=t[3];return e[0]=n+i*(r[0]-n),e[1]=a+i*(r[1]-a),e[2]=o+i*(r[2]-o),e[3]=s+i*(r[3]-s),e},At.random=function(e,t){return t=t||1,e[0]=de(),e[1]=de(),e[2]=de(),e[3]=de(),At.normalize(e,e),At.scale(e,e,t),e},At.transformMat4=function(e,t,r){var i=t[0],n=t[1],a=t[2],o=t[3];return e[0]=r[0]*i+r[4]*n+r[8]*a+r[12]*o,e[1]=r[1]*i+r[5]*n+r[9]*a+r[13]*o,e[2]=r[2]*i+r[6]*n+r[10]*a+r[14]*o,e[3]=r[3]*i+r[7]*n+r[11]*a+r[15]*o,e},At.transformQuat=function(e,t,r){var i=t[0],n=t[1],a=t[2],o=r[0],s=r[1],l=r[2],h=r[3],u=h*i+s*a-l*n,c=h*n+l*i-o*a,d=h*a+o*n-s*i,f=-o*i-s*n-l*a;return e[0]=u*h+f*-o+c*-l-d*-s,e[1]=c*h+f*-s+d*-o-u*-l,e[2]=d*h+f*-l+u*-s-c*-o,e},At.forEach=function(){var e=At.create();return function(t,r,i,n,a,o){var s,l;for(r||(r=4),i||(i=0),l=n?Math.min(n*r+i,t.length):t.length,s=i;s.999999?(e[0]=0,e[1]=0,e[2]=0,e[3]=1,e):(Qe.cross(Lt,t,r),e[0]=Lt[0],e[1]=Lt[1],e[2]=Lt[2],e[3]=1+i,It.normalize(e,e))}),It.setAxes=(Nt=Dt.create(),function(e,t,r,i){return Nt[0]=r[0],Nt[3]=r[1],Nt[6]=r[2],Nt[1]=i[0],Nt[4]=i[1],Nt[7]=i[2],Nt[2]=-t[0],Nt[5]=-t[1],Nt[8]=-t[2],It.normalize(e,It.fromMat3(e,Nt))}),It.clone=Et.clone,It.fromValues=Et.fromValues,It.copy=Et.copy,It.set=Et.set,It.identity=function(e){return e[0]=0,e[1]=0,e[2]=0,e[3]=1,e},It.setAxisAngle=function(e,t,r){r*=.5;var i=Math.sin(r);return e[0]=i*t[0],e[1]=i*t[1],e[2]=i*t[2],e[3]=Math.cos(r),e},It.add=Et.add,It.multiply=function(e,t,r){var i=t[0],n=t[1],a=t[2],o=t[3],s=r[0],l=r[1],h=r[2],u=r[3];return e[0]=i*u+o*s+n*h-a*l,e[1]=n*u+o*l+a*s-i*h,e[2]=a*u+o*h+i*l-n*s,e[3]=o*u-i*s-n*l-a*h,e},It.mul=It.multiply,It.scale=Et.scale,It.rotateX=function(e,t,r){r*=.5;var i=t[0],n=t[1],a=t[2],o=t[3],s=Math.sin(r),l=Math.cos(r);return e[0]=i*l+o*s,e[1]=n*l+a*s,e[2]=a*l-n*s,e[3]=o*l-i*s,e},It.rotateY=function(e,t,r){r*=.5;var i=t[0],n=t[1],a=t[2],o=t[3],s=Math.sin(r),l=Math.cos(r);return e[0]=i*l-a*s,e[1]=n*l+o*s,e[2]=a*l+i*s,e[3]=o*l-n*s,e},It.rotateZ=function(e,t,r){r*=.5;var i=t[0],n=t[1],a=t[2],o=t[3],s=Math.sin(r),l=Math.cos(r);return e[0]=i*l+n*s,e[1]=n*l-i*s,e[2]=a*l+o*s,e[3]=o*l-a*s,e},It.calculateW=function(e,t){var r=t[0],i=t[1],n=t[2];return e[0]=r,e[1]=i,e[2]=n,e[3]=Math.sqrt(Math.abs(1-r*r-i*i-n*n)),e},It.dot=Et.dot,It.lerp=Et.lerp,It.slerp=function(e,t,r,i){var n,a,o,s,l,h=t[0],u=t[1],c=t[2],d=t[3],f=r[0],p=r[1],m=r[2],g=r[3];return(a=h*f+u*p+c*m+d*g)<0&&(a=-a,f=-f,p=-p,m=-m,g=-g),1-a>1e-6?(n=Math.acos(a),o=Math.sin(n),s=Math.sin((1-i)*n)/o,l=Math.sin(i*n)/o):(s=1-i,l=i),e[0]=s*h+l*f,e[1]=s*u+l*p,e[2]=s*c+l*m,e[3]=s*d+l*g,e},It.invert=function(e,t){var r=t[0],i=t[1],n=t[2],a=t[3],o=r*r+i*i+n*n+a*a,s=o?1/o:0;return e[0]=-r*s,e[1]=-i*s,e[2]=-n*s,e[3]=a*s,e},It.conjugate=function(e,t){return e[0]=-t[0],e[1]=-t[1],e[2]=-t[2],e[3]=t[3],e},It.length=Et.length,It.len=It.length,It.squaredLength=Et.squaredLength,It.sqrLen=It.squaredLength,It.normalize=Et.normalize,It.fromMat3=function(e,t){var r,i=t[0]+t[4]+t[8];if(i>0)r=Math.sqrt(i+1),e[3]=.5*r,r=.5/r,e[0]=(t[5]-t[7])*r,e[1]=(t[6]-t[2])*r,e[2]=(t[1]-t[3])*r;else{var n=0;t[4]>t[0]&&(n=1),t[8]>t[3*n+n]&&(n=2);var a=(n+1)%3,o=(n+2)%3;r=Math.sqrt(t[3*n+n]-t[3*a+a]-t[3*o+o]+1),e[n]=.5*r,r=.5/r,e[3]=(t[3*a+o]-t[3*o+a])*r,e[a]=(t[3*a+n]+t[3*n+a])*r,e[o]=(t[3*o+n]+t[3*n+o])*r}return e};const Rt=It;var Bt,Ft,zt,Gt,Ut=function(){this._axisX=new vt,this._axisY=new vt,this._axisZ=new vt,this.array=Ye.create(),this._dirty=!0};Ut.prototype={constructor:Ut,setArray:function(e){for(var t=0;t0){var t=this.min,r=this.max,i=t.array,n=r.array;tr(i,e[0]),tr(n,e[0]);for(var a=1;an[0]&&(n[0]=o[0]),o[1]>n[1]&&(n[1]=o[1]),o[2]>n[2]&&(n[2]=o[2])}t._dirty=!0,r._dirty=!0}},union:function(e){var t=this.min,r=this.max;return Qe.min(t.array,t.array,e.min.array),Qe.max(r.array,r.array,e.max.array),t._dirty=!0,r._dirty=!0,this},intersection:function(e){var t=this.min,r=this.max;return Qe.max(t.array,t.array,e.min.array),Qe.min(r.array,r.array,e.max.array),t._dirty=!0,r._dirty=!0,this},intersectBoundingBox:function(e){var t=this.min.array,r=this.max.array,i=e.min.array,n=e.max.array;return!(t[0]>n[0]||t[1]>n[1]||t[2]>n[2]||r[0]=n[0]&&r[1]>=n[1]&&r[2]>=n[2]},containPoint:function(e){var t=this.min.array,r=this.max.array,i=e.array;return t[0]<=i[0]&&t[1]<=i[1]&&t[2]<=i[2]&&r[0]>=i[0]&&r[1]>=i[1]&&r[2]>=i[2]},isFinite:function(){var e=this.min.array,t=this.max.array;return isFinite(e[0])&&isFinite(e[1])&&isFinite(e[2])&&isFinite(t[0])&&isFinite(t[1])&&isFinite(t[2])},applyTransform:function(e){this.transformFrom(this,e)},transformFrom:(Zt=Qe.create(),Yt=Qe.create(),Kt=Qe.create(),Qt=Qe.create(),Jt=Qe.create(),$t=Qe.create(),function(e,t){var r=e.min.array,i=e.max.array,n=t.array;return Zt[0]=n[0]*r[0],Zt[1]=n[1]*r[0],Zt[2]=n[2]*r[0],Yt[0]=n[0]*i[0],Yt[1]=n[1]*i[0],Yt[2]=n[2]*i[0],Kt[0]=n[4]*r[1],Kt[1]=n[5]*r[1],Kt[2]=n[6]*r[1],Qt[0]=n[4]*i[1],Qt[1]=n[5]*i[1],Qt[2]=n[6]*i[1],Jt[0]=n[8]*r[2],Jt[1]=n[9]*r[2],Jt[2]=n[10]*r[2],$t[0]=n[8]*i[2],$t[1]=n[9]*i[2],$t[2]=n[10]*i[2],r=this.min.array,i=this.max.array,r[0]=Math.min(Zt[0],Yt[0])+Math.min(Kt[0],Qt[0])+Math.min(Jt[0],$t[0])+n[12],r[1]=Math.min(Zt[1],Yt[1])+Math.min(Kt[1],Qt[1])+Math.min(Jt[1],$t[1])+n[13],r[2]=Math.min(Zt[2],Yt[2])+Math.min(Kt[2],Qt[2])+Math.min(Jt[2],$t[2])+n[14],i[0]=Math.max(Zt[0],Yt[0])+Math.max(Kt[0],Qt[0])+Math.max(Jt[0],$t[0])+n[12],i[1]=Math.max(Zt[1],Yt[1])+Math.max(Kt[1],Qt[1])+Math.max(Jt[1],$t[1])+n[13],i[2]=Math.max(Zt[2],Yt[2])+Math.max(Kt[2],Qt[2])+Math.max(Jt[2],$t[2])+n[14],this.min._dirty=!0,this.max._dirty=!0,this}),applyProjection:function(e){var t=this.min.array,r=this.max.array,i=e.array,n=t[0],a=t[1],o=t[2],s=r[0],l=r[1],h=t[2],u=r[0],c=r[1],d=r[2];if(1===i[15])t[0]=i[0]*n+i[12],t[1]=i[5]*a+i[13],r[2]=i[10]*o+i[14],r[0]=i[0]*u+i[12],r[1]=i[5]*c+i[13],t[2]=i[10]*d+i[14];else{var f=-1/o;t[0]=i[0]*n*f,t[1]=i[5]*a*f,r[2]=(i[10]*o+i[14])*f,f=-1/h,r[0]=i[0]*s*f,r[1]=i[5]*l*f,f=-1/d,t[2]=(i[10]*d+i[14])*f}return this.min._dirty=!0,this.max._dirty=!0,this},updateVertices:function(){var e=this.vertices;if(!e){e=[];for(var t=0;t<8;t++)e[t]=Qe.fromValues(0,0,0);this.vertices=e}var r=this.min.array,i=this.max.array;return er(e[0],r[0],r[1],r[2]),er(e[1],r[0],i[1],r[2]),er(e[2],i[0],r[1],r[2]),er(e[3],i[0],i[1],r[2]),er(e[4],r[0],r[1],i[2]),er(e[5],r[0],i[1],i[2]),er(e[6],i[0],r[1],i[2]),er(e[7],i[0],i[1],i[2]),this},copy:function(e){var t=this.min,r=this.max;return tr(t.array,e.min.array),tr(r.array,e.max.array),t._dirty=!0,r._dirty=!0,this},clone:function(){var e=new rr;return e.copy(this),e}};const ir=rr;var nr,ar,or=0;const sr=m.extend({name:\"\",position:null,rotation:null,scale:null,worldTransform:null,localTransform:null,autoUpdateLocalTransform:!0,_parent:null,_scene:null,_needsUpdateWorldTransform:!0,_inIterating:!1,__depth:0},(function(){this.name||(this.name=(this.type||\"NODE\")+\"_\"+or++),this.position||(this.position=new vt),this.rotation||(this.rotation=new qt),this.scale||(this.scale=new vt(1,1,1)),this.worldTransform=new Ht,this.localTransform=new Ht,this._children=[]}),{target:null,invisible:!1,isSkinnedMesh:function(){return!1},isRenderable:function(){return!1},setName:function(e){var t=this._scene;if(t){var r=t._nodeRepository;delete r[this.name],r[e]=this}this.name=e},add:function(e){var t=e._parent;if(t!==this){t&&t.remove(e),e._parent=this,this._children.push(e);var r=this._scene;r&&r!==e.scene&&e.traverse(this._addSelfToScene,this),e._needsUpdateWorldTransform=!0}},remove:function(e){var t=this._children,r=t.indexOf(e);r<0||(t.splice(r,1),e._parent=null,this._scene&&e.traverse(this._removeSelfFromScene,this))},removeAll:function(){for(var e=this._children,t=0;t0},beforeRender:function(e){},afterRender:function(e,t){},getBoundingBox:function(e,t){return t=sr.prototype.getBoundingBox.call(this,e,t),this.geometry&&this.geometry.boundingBox&&t.union(this.geometry.boundingBox),t},clone:(lr=[\"castShadow\",\"receiveShadow\",\"mode\",\"culling\",\"cullFace\",\"frontFace\",\"frustumCulling\",\"renderOrder\",\"lineWidth\",\"ignorePicking\",\"ignorePreZ\",\"ignoreGBuffer\"],function(){var e=sr.prototype.clone.call(this);e.geometry=this.geometry,e.material=this.material;for(var t=0;t=0&&g[p]>1e-4&&(Qe.transformMat4(T,m,d[_[p]]),Qe.scaleAndAdd(v,v,T,g[p]));S.set(f,v)}}for(f=0;f>t;return e+1},dispose:function(e){var t=this._cache;t.use(e.__uid__);var r=t.get(\"webgl_texture\");r&&e.gl.deleteTexture(r),t.deleteContext(e.__uid__)},isRenderable:function(){},isPowerOfTwo:function(){}});Object.defineProperty(br.prototype,\"width\",{get:function(){return this._width},set:function(e){this._width=e}}),Object.defineProperty(br.prototype,\"height\",{get:function(){return this._height},set:function(e){this._height=e}}),br.BYTE=5120,br.UNSIGNED_BYTE=T,br.SHORT=5122,br.UNSIGNED_SHORT=5123,br.INT=5124,br.UNSIGNED_INT=5125,br.FLOAT=S,br.HALF_FLOAT=36193,br.UNSIGNED_INT_24_8_WEBGL=34042,br.DEPTH_COMPONENT=M,br.DEPTH_STENCIL=34041,br.ALPHA=6406,br.RGB=6407,br.RGBA=A,br.LUMINANCE=6409,br.LUMINANCE_ALPHA=6410,br.SRGB=35904,br.SRGB_ALPHA=35906,br.COMPRESSED_RGB_S3TC_DXT1_EXT=33776,br.COMPRESSED_RGBA_S3TC_DXT1_EXT=33777,br.COMPRESSED_RGBA_S3TC_DXT3_EXT=33778,br.COMPRESSED_RGBA_S3TC_DXT5_EXT=33779,br.NEAREST=E,br.LINEAR=C,br.NEAREST_MIPMAP_NEAREST=D,br.LINEAR_MIPMAP_NEAREST=L,br.NEAREST_MIPMAP_LINEAR=P,br.LINEAR_MIPMAP_LINEAR=O,br.REPEAT=N,br.CLAMP_TO_EDGE=I,br.MIRRORED_REPEAT=33648;const wr=br;var Tr=ur.extend({skeleton:null,joints:null},(function(){this.joints||(this.joints=[])}),{offsetMatrix:null,isInstancedMesh:function(){return!1},isSkinnedMesh:function(){return!!(this.skeleton&&this.joints&&this.joints.length>0)},clone:function(){var e=ur.prototype.clone.call(this);return e.skeleton=this.skeleton,this.joints&&(e.joints=this.joints.slice()),e}});Tr.POINTS=0,Tr.LINES=1,Tr.LINE_LOOP=2,Tr.LINE_STRIP=3,Tr.TRIANGLES=4,Tr.TRIANGLE_STRIP=5,Tr.TRIANGLE_FAN=6,Tr.BACK=x,Tr.FRONT=y,Tr.FRONT_AND_BACK=1032,Tr.CW=b,Tr.CCW=w;const Sr=Tr;const Mr={isPowerOfTwo:function(e){return 0==(e&e-1)},nextPowerOfTwo:function(e){return e--,e|=e>>1,e|=e>>2,e|=e>>4,e|=e>>8,e|=e>>16,++e},nearestPowerOfTwo:function(e){return Math.pow(2,Math.round(Math.log(e)/Math.LN2))}};var Ar=Mr.isPowerOfTwo;function Er(e){return Math.pow(2,Math.round(Math.log(e)/Math.LN2))}var Cr=wr.extend((function(){return{image:null,pixels:null,mipmaps:[],convertToPOT:!1}}),{textureType:\"texture2D\",update:function(e){var t=e.gl;t.bindTexture(t.TEXTURE_2D,this._cache.get(\"webgl_texture\")),this.updateCommon(e);var r=this.format,i=this.type,n=!(!this.convertToPOT||this.mipmaps.length||!this.image||this.wrapS!==wr.REPEAT&&this.wrapT!==wr.REPEAT||!this.NPOT);t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,n?this.wrapS:this.getAvailableWrapS()),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,n?this.wrapT:this.getAvailableWrapT()),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,n?this.magFilter:this.getAvailableMagFilter()),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,n?this.minFilter:this.getAvailableMinFilter());var a=e.getGLExtension(\"EXT_texture_filter_anisotropic\");if(a&&this.anisotropic>1&&t.texParameterf(t.TEXTURE_2D,a.TEXTURE_MAX_ANISOTROPY_EXT,this.anisotropic),36193===i&&(e.getGLExtension(\"OES_texture_half_float\")||(i=S)),this.mipmaps.length)for(var o=this.width,s=this.height,l=0;l=wr.COMPRESSED_RGB_S3TC_DXT1_EXT?e.compressedTexImage2D(e.TEXTURE_2D,r,a,i,n,0,t.pixels):e.texImage2D(e.TEXTURE_2D,r,a,i,n,0,a,o,t.pixels)},generateMipmap:function(e){var t=e.gl;this.useMipmap&&!this.NPOT&&(t.bindTexture(t.TEXTURE_2D,this._cache.get(\"webgl_texture\")),t.generateMipmap(t.TEXTURE_2D))},isPowerOfTwo:function(){return Ar(this.width)&&Ar(this.height)},isRenderable:function(){return this.image?this.image.width>0&&this.image.height>0:!(!this.width||!this.height)},bind:function(e){e.gl.bindTexture(e.gl.TEXTURE_2D,this.getWebGLTexture(e))},unbind:function(e){e.gl.bindTexture(e.gl.TEXTURE_2D,null)},load:function(e,t){var r=U.createImage();t&&(r.crossOrigin=t);var i=this;return r.onload=function(){i.dirty(),i.trigger(\"success\",i)},r.onerror=function(){i.trigger(\"error\",i)},r.src=e,this.image=r,this}});Object.defineProperty(Cr.prototype,\"width\",{get:function(){return this.image?this.image.width:this._width},set:function(e){this.image?console.warn(\"Texture from image can't set width\"):(this._width!==e&&this.dirty(),this._width=e)}}),Object.defineProperty(Cr.prototype,\"height\",{get:function(){return this.image?this.image.height:this._height},set:function(e){this.image?console.warn(\"Texture from image can't set height\"):(this._height!==e&&this.dirty(),this._height=e)}});const Dr=Cr;function Lr(e){return{byte:U.Int8Array,ubyte:U.Uint8Array,short:U.Int16Array,ushort:U.Uint16Array}[e]||U.Float32Array}function Pr(e){return\"attr_\"+e}function Or(e,t,r,i){switch(this.name=e,this.type=t,this.size=r,this.semantic=i||\"\",this.value=null,r){case 1:this.get=function(e){return this.value[e]},this.set=function(e,t){this.value[e]=t},this.copy=function(e,t){this.value[e]=this.value[e]};break;case 2:this.get=function(e,t){var r=this.value;return t[0]=r[2*e],t[1]=r[2*e+1],t},this.set=function(e,t){var r=this.value;r[2*e]=t[0],r[2*e+1]=t[1]},this.copy=function(e,t){var r=this.value;t*=2,r[e*=2]=r[t],r[e+1]=r[t+1]};break;case 3:this.get=function(e,t){var r=3*e,i=this.value;return t[0]=i[r],t[1]=i[r+1],t[2]=i[r+2],t},this.set=function(e,t){var r=3*e,i=this.value;i[r]=t[0],i[r+1]=t[1],i[r+2]=t[2]},this.copy=function(e,t){var r=this.value;t*=3,r[e*=3]=r[t],r[e+1]=r[t+1],r[e+2]=r[t+2]};break;case 4:this.get=function(e,t){var r=this.value,i=4*e;return t[0]=r[i],t[1]=r[i+1],t[2]=r[i+2],t[3]=r[i+3],t},this.set=function(e,t){var r=this.value,i=4*e;r[i]=t[0],r[i+1]=t[1],r[i+2]=t[2],r[i+3]=t[3]},this.copy=function(e,t){var r=this.value;t*=4,r[e*=4]=r[t],r[e+1]=r[t+1],r[e+2]=r[t+2],r[e+3]=r[t+3]}}}function Nr(e,t,r,i,n){this.name=e,this.type=t,this.buffer=r,this.size=i,this.semantic=n,this.symbol=\"\",this.needsRemove=!1}function Ir(e){this.buffer=e,this.count=0}Or.prototype.init=function(e){if(!this.value||this.value.length!==e*this.size){var t=Lr(this.type);this.value=new t(e*this.size)}},Or.prototype.fromArray=function(e){var t,r=Lr(this.type);if(e[0]&&e[0].length){var i=0,n=this.size;t=new r(e.length*n);for(var a=0;a=0){t||(t=[]);var r=this.indices;return t[0]=r[3*e],t[1]=r[3*e+1],t[2]=r[3*e+2],t}},setTriangleIndices:function(e,t){var r=this.indices;r[3*e]=t[0],r[3*e+1]=t[1],r[3*e+2]=t[2]},isUseIndices:function(){return!!this.indices},initIndicesFromArray:function(e){var t,r=this.vertexCount>65535?U.Uint32Array:U.Uint16Array;if(e[0]&&e[0].length){var i=0;t=new r(3*e.length);for(var n=0;n=0&&(t.splice(r,1),delete this.attributes[e],!0)},getAttribute:function(e){return this.attributes[e]},getEnabledAttributes:function(){var e=this._enabledAttributes,t=this._attributeList;if(e)return e;for(var r=[],i=this.vertexCount,n=0;na[0]&&(a[0]=s),l>a[1]&&(a[1]=l),h>a[2]&&(a[2]=h)}r._dirty=!0,i._dirty=!0}},generateVertexNormals:function(){if(this.vertexCount){var e=this.indices,t=this.attributes,r=t.position.value,i=t.normal.value;if(i&&i.length===r.length)for(var n=0;n65535&&(this.indices=new U.Uint32Array(this.indices));for(var e=this.attributes,t=this.indices,r=this.getEnabledAttributes(),i={},n=0;nthis.distance,n=1;n<8;n++)if(Qe.dot(t[n].array,r)>this.distance!=i)return!0},intersectLine:(Qr=Qe.create(),function(e,t,r){var i=this.distanceToPoint(e),n=this.distanceToPoint(t);if(i>0&&n>0||i<0&&n<0)return null;var a=this.normal.array,o=this.distance,s=e.array;Qe.sub(Qr,t.array,e.array),Qe.normalize(Qr,Qr);var l=Qe.dot(a,Qr);if(0===l)return null;r||(r=new vt);var h=(Qe.dot(a,s)-o)/l;return Qe.scaleAndAdd(r.array,s,Qr,-h),r._dirty=!0,r}),applyTransform:(Zr=Ye.create(),Yr=Et.create(),Kr=Et.create(),Kr[3]=1,function(e){e=e.array,Qe.scale(Kr,this.normal.array,this.distance),Et.transformMat4(Kr,Kr,e),this.distance=Qe.dot(Kr,this.normal.array),Ye.invert(Zr,e),Ye.transpose(Zr,Zr),Yr[3]=0,Qe.copy(Yr,this.normal.array),Et.transformMat4(Yr,Yr,Zr),Qe.copy(this.normal.array,Yr)}),copy:function(e){Qe.copy(this.normal.array,e.normal.array),this.normal._dirty=!0,this.distance=e.distance},clone:function(){var e=new Jr;return e.copy(this),e}};const $r=Jr;var ei,ti=Qe.set,ri=Qe.copy,ii=Qe.transformMat4,ni=Math.min,ai=Math.max,oi=function(){this.planes=[];for(var e=0;e<6;e++)this.planes.push(new $r);for(this.boundingBox=new ir,this.vertices=[],e=0;e<8;e++)this.vertices[e]=Qe.fromValues(0,0,0)};oi.prototype={setFromProjection:function(e){var t=this.planes,r=e.array,i=r[0],n=r[1],a=r[2],o=r[3],s=r[4],l=r[5],h=r[6],u=r[7],c=r[8],d=r[9],f=r[10],p=r[11],m=r[12],g=r[13],_=r[14],v=r[15];ti(t[0].normal.array,o-i,u-s,p-c),t[0].distance=-(v-m),t[0].normalize(),ti(t[1].normal.array,o+i,u+s,p+c),t[1].distance=-(v+m),t[1].normalize(),ti(t[2].normal.array,o+n,u+l,p+d),t[2].distance=-(v+g),t[2].normalize(),ti(t[3].normal.array,o-n,u-l,p-d),t[3].distance=-(v-g),t[3].normalize(),ti(t[4].normal.array,o-a,u-h,p-f),t[4].distance=-(v-_),t[4].normalize(),ti(t[5].normal.array,o+a,u+h,p+f),t[5].distance=-(v+_),t[5].normalize();var y=this.boundingBox,x=this.vertices;if(0===v){var b=l/i,w=-_/(f-1),T=-_/(f+1),S=-T/l,M=-w/l;y.min.set(-S*b,-S,T),y.max.set(S*b,S,w),ti(x[0],-S*b,-S,T),ti(x[1],-S*b,S,T),ti(x[2],S*b,-S,T),ti(x[3],S*b,S,T),ti(x[4],-M*b,-M,w),ti(x[5],-M*b,M,w),ti(x[6],M*b,-M,w),ti(x[7],M*b,M,w)}else{var A=(-1-m)/i,E=(1-m)/i,C=(1-g)/l,D=(-1-g)/l,L=(-1-_)/f,P=(1-_)/f;y.min.set(Math.min(A,E),Math.min(D,C),Math.min(P,L)),y.max.set(Math.max(E,A),Math.max(C,D),Math.max(L,P));var O=y.min.array,N=y.max.array;ti(x[0],O[0],O[1],O[2]),ti(x[1],O[0],N[1],O[2]),ti(x[2],N[0],O[1],O[2]),ti(x[3],N[0],N[1],O[2]),ti(x[4],O[0],O[1],N[2]),ti(x[5],O[0],N[1],N[2]),ti(x[6],N[0],O[1],N[2]),ti(x[7],N[0],N[1],N[2])}},getTransformedBoundingBox:(ei=Qe.create(),function(e,t){var r=this.vertices,i=t.array,n=e.min,a=e.max,o=n.array,s=a.array,l=r[0];ii(ei,l,i),ri(o,ei),ri(s,ei);for(var h=1;h<8;h++)l=r[h],ii(ei,l,i),o[0]=ni(ei[0],o[0]),o[1]=ni(ei[1],o[1]),o[2]=ni(ei[2],o[2]),s[0]=ai(ei[0],s[0]),s[1]=ai(ei[1],s[1]),s[2]=ai(ei[2],s[2]);return n._dirty=!0,a._dirty=!0,e})};const si=oi;var li;const hi=sr.extend((function(){return{projectionMatrix:new Ht,invProjectionMatrix:new Ht,viewMatrix:new Ht,frustum:new si}}),(function(){this.update(!0)}),{update:function(e){sr.prototype.update.call(this,e),Ht.invert(this.viewMatrix,this.worldTransform),this.updateProjectionMatrix(),Ht.invert(this.invProjectionMatrix,this.projectionMatrix),this.frustum.setFromProjection(this.projectionMatrix)},setViewMatrix:function(e){Ht.copy(this.viewMatrix,e),Ht.invert(this.worldTransform,e),this.decomposeWorldTransform()},decomposeProjectionMatrix:function(){},setProjectionMatrix:function(e){Ht.copy(this.projectionMatrix,e),Ht.invert(this.invProjectionMatrix,e),this.decomposeProjectionMatrix()},updateProjectionMatrix:function(){},castRay:(li=Et.create(),function(e,t){var r=void 0!==t?t:new Mt,i=e.array[0],n=e.array[1];return Et.set(li,i,n,-1,1),Et.transformMat4(li,li,this.invProjectionMatrix.array),Et.transformMat4(li,li,this.worldTransform.array),Qe.scale(r.origin.array,li,1/li[3]),Et.set(li,i,n,1,1),Et.transformMat4(li,li,this.invProjectionMatrix.array),Et.transformMat4(li,li,this.worldTransform.array),Qe.scale(li,li,1/li[3]),Qe.sub(r.direction.array,li,r.origin.array),Qe.normalize(r.direction.array,r.direction.array),r.direction._dirty=!0,r.origin._dirty=!0,r})});var ui,ci,di=Ye.create(),fi=Ye.create(),pi={};function mi(e){var t=[],r=Object.keys(e);r.sort();for(var i=0;i0&&console.warn(\"Found multiple camera in one scene. Use the fist one.\"),this._cameraList.push(e)):e instanceof qr&&this.lights.push(e),e.name&&(this._nodeRepository[e.name]=e)},removeFromScene:function(e){var t;e instanceof hi?(t=this._cameraList.indexOf(e))>=0&&this._cameraList.splice(t,1):e instanceof qr&&(t=this.lights.indexOf(e))>=0&&this.lights.splice(t,1),e.name&&delete this._nodeRepository[e.name]},getNode:function(e){return this._nodeRepository[e]},setMainCamera:function(e){var t=this._cameraList.indexOf(e);t>=0&&this._cameraList.splice(t,1),this._cameraList.unshift(e)},getMainCamera:function(){return this._cameraList[0]},getLights:function(){return this.lights},updateLights:function(){var e=this.lights;this._previousLightNumber=this._lightNumber;for(var t={},r=0;r0&&this._doUpdateRenderList(o,t,r,i,n)}},isFrustumCulled:(ui=new ir,ci=new Ht,function(e,t,r){var i=e.boundingBox;if(i||(i=e.skeleton&&e.skeleton.boundingBox?e.skeleton.boundingBox:e.geometry.boundingBox),!i)return!1;if(ci.array=r,ui.transformFrom(i,ci),e.castShadow&&this.viewBoundingBoxLastFrame.union(ui),e.frustumCulling){if(!ui.intersectBoundingBox(t.frustum.boundingBox))return!0;ci.array=t.projectionMatrix.array,ui.max.array[2]>0&&ui.min.array[2]<0&&(ui.max.array[2]=-1e-20),ui.applyProjection(ci);var n=ui.min.array,a=ui.max.array;if(a[0]<-1||n[0]>1||a[1]<-1||n[1]>1||a[2]<-1||n[2]>1)return!0}return!1}),_updateLightUniforms:function(){var e=this.lights;e.sort(_i);var t=this._lightUniforms;for(var r in t)for(var i in t[r])t[r][i].value.length=0;for(var n=0;n=this._maxSize&&a>0){var s=r.head;r.remove(s),delete i[s.key],n=s.value,this._lastRemovedEntry=s}o?o.value=t:o=new yi(t),o.key=e,r.insertEntry(o),i[e]=o}return n},e.prototype.get=function(e){var t=this._map[e],r=this._list;if(null!=t)return t!==r.tail&&(r.remove(t),r.insertEntry(t)),t.value},e.prototype.clear=function(){this._list.clear(),this._map={}},e.prototype.len=function(){return this._list.len()},e}();var wi=Mr.isPowerOfTwo,Ti=[\"px\",\"nx\",\"py\",\"ny\",\"pz\",\"nz\"],Si=wr.extend((function(){return{image:{px:null,nx:null,py:null,ny:null,pz:null,nz:null},pixels:{px:null,nx:null,py:null,ny:null,pz:null,nz:null},mipmaps:[]}}),{textureType:\"textureCube\",update:function(e){var t=e.gl;t.bindTexture(t.TEXTURE_CUBE_MAP,this._cache.get(\"webgl_texture\")),this.updateCommon(e);var r=this.format,i=this.type;t.texParameteri(t.TEXTURE_CUBE_MAP,t.TEXTURE_WRAP_S,this.getAvailableWrapS()),t.texParameteri(t.TEXTURE_CUBE_MAP,t.TEXTURE_WRAP_T,this.getAvailableWrapT()),t.texParameteri(t.TEXTURE_CUBE_MAP,t.TEXTURE_MAG_FILTER,this.getAvailableMagFilter()),t.texParameteri(t.TEXTURE_CUBE_MAP,t.TEXTURE_MIN_FILTER,this.getAvailableMinFilter());var n=e.getGLExtension(\"EXT_texture_filter_anisotropic\");if(n&&this.anisotropic>1&&t.texParameterf(t.TEXTURE_CUBE_MAP,n.TEXTURE_MAX_ANISOTROPY_EXT,this.anisotropic),36193===i&&(e.getGLExtension(\"OES_texture_half_float\")||(i=S)),this.mipmaps.length)for(var a=this.width,o=this.height,s=0;s0&&e.height>0}Object.defineProperty(Si.prototype,\"width\",{get:function(){return this.image&&this.image.px?this.image.px.width:this._width},set:function(e){this.image&&this.image.px?console.warn(\"Texture from image can't set width\"):(this._width!==e&&this.dirty(),this._width=e)}}),Object.defineProperty(Si.prototype,\"height\",{get:function(){return this.image&&this.image.px?this.image.px.height:this._height},set:function(e){this.image&&this.image.px?console.warn(\"Texture from image can't set height\"):(this._height!==e&&this.dirty(),this._height=e)}});const Ai=Si,Ei=hi.extend({fov:50,aspect:1,near:.1,far:2e3},{updateProjectionMatrix:function(){var e=this.fov/180*Math.PI;this.projectionMatrix.perspective(e,this.aspect,this.near,this.far)},decomposeProjectionMatrix:function(){var e=this.projectionMatrix.array,t=2*Math.atan(1/e[5]);this.fov=t/Math.PI*180,this.aspect=e[5]/e[0],this.near=e[14]/(e[10]-1),this.far=e[14]/(e[10]+1)},clone:function(){var e=hi.prototype.clone.call(this);return e.fov=this.fov,e.aspect=this.aspect,e.near=this.near,e.far=this.far,e}});var Ci=\"framebuffer\",Di=\"renderbuffer\",Li=\"renderbuffer_width\",Pi=\"renderbuffer_height\",Oi=\"renderbuffer_attached\",Ni=\"depthtexture_attached\",Ii=36160,Ri=36161,Bi=36096,Fi=m.extend({depthBuffer:!0,viewport:null,_width:0,_height:0,_textures:null,_boundRenderer:null},(function(){this._cache=new xr,this._textures={}}),{getTextureWidth:function(){return this._width},getTextureHeight:function(){return this._height},bind:function(e){if(e.__currentFrameBuffer){if(e.__currentFrameBuffer===this)return;console.warn(\"Renderer already bound with another framebuffer. Unbind it first\")}e.__currentFrameBuffer=this;var t=e.gl;t.bindFramebuffer(Ii,this._getFrameBufferGL(e)),this._boundRenderer=e;var r=this._cache;r.put(\"viewport\",e.viewport);var i,n,a=!1;for(var o in this._textures){a=!0;var s=this._textures[o];s&&(i=s.texture.width,n=s.texture.height,this._doAttach(e,s.texture,o,s.target))}this._width=i,this._height=n,!a&&this.depthBuffer&&console.error(\"Must attach texture before bind, or renderbuffer may have incorrect width and height.\"),this.viewport?e.setViewport(this.viewport):e.setViewport(0,0,i,n,1);var l=r.get(\"attached_textures\");if(l)for(var o in l)if(!this._textures[o]){var h=l[o];this._doDetach(t,o,h)}if(!r.get(Ni)&&this.depthBuffer){r.miss(Di)&&r.put(Di,t.createRenderbuffer());var u=r.get(Di);i===r.get(Li)&&n===r.get(Pi)||(t.bindRenderbuffer(Ri,u),t.renderbufferStorage(Ri,t.DEPTH_COMPONENT16,i,n),r.put(Li,i),r.put(Pi,n),t.bindRenderbuffer(Ri,null)),r.get(Oi)||(t.framebufferRenderbuffer(Ii,Bi,Ri,u),r.put(Oi,!0))}},unbind:function(e){e.__currentFrameBuffer=null,e.gl.bindFramebuffer(Ii,null),this._boundRenderer=null,this._cache.use(e.__uid__);var t=this._cache.get(\"viewport\");t&&e.setViewport(t),this.updateMipmap(e)},updateMipmap:function(e){var t=e.gl;for(var r in this._textures){var i=this._textures[r];if(i){var n=i.texture;if(!n.NPOT&&n.useMipmap&&n.minFilter===wr.LINEAR_MIPMAP_LINEAR){var a=\"textureCube\"===n.textureType?34067:3553;t.bindTexture(a,n.getWebGLTexture(e)),t.generateMipmap(a),t.bindTexture(a,null)}}}},checkStatus:function(e){return e.checkFramebufferStatus(Ii)},_getFrameBufferGL:function(e){var t=this._cache;return t.use(e.__uid__),t.miss(Ci)&&t.put(Ci,e.gl.createFramebuffer()),t.get(Ci)},attach:function(e,t,r){if(!e.width)throw new Error(\"The texture attached to color buffer is not a valid.\");t=t||36064,r=r||3553;var i,n=this._boundRenderer;if(n&&n.gl){var a=this._cache;a.use(n.__uid__),i=a.get(\"attached_textures\")}var o=this._textures[t];if(!o||o.target!==r||o.texture!==e||!i||null==i[t]){var s=!0;n&&(s=this._doAttach(n,e,t,r),this.viewport||n.setViewport(0,0,e.width,e.height,1)),s&&(this._textures[t]=this._textures[t]||{},this._textures[t].texture=e,this._textures[t].target=r)}},_doAttach:function(e,t,r,i){var n=e.gl,a=t.getWebGLTexture(e),o=this._cache.get(\"attached_textures\");if(o&&o[r]){var s=o[r];if(s.texture===t&&s.target===i)return}var l=!0;if(((r=+r)===Bi||r===R)&&(e.getGLExtension(\"WEBGL_depth_texture\")||(console.error(\"Depth texture is not supported by the browser\"),l=!1),t.format!==M&&34041!==t.format&&(console.error(\"The texture attached to depth buffer is not a valid.\"),l=!1),l)){var h=this._cache.get(Di);h&&(n.framebufferRenderbuffer(Ii,Bi,Ri,null),n.deleteRenderbuffer(h),this._cache.put(Di,!1)),this._cache.put(Oi,!1),this._cache.put(Ni,!0)}return n.framebufferTexture2D(Ii,r,i,a,0),o||(o={},this._cache.put(\"attached_textures\",o)),o[r]=o[r]||{},o[r].texture=t,o[r].target=i,l},_doDetach:function(e,t,r){e.framebufferTexture2D(Ii,t,r,null,0);var i=this._cache.get(\"attached_textures\");i&&i[t]&&(i[t]=null),t!==Bi&&t!==R||this._cache.put(Ni,!1)},detach:function(e,t){this._textures[e]=null,this._boundRenderer&&(this._cache.use(this._boundRenderer.__uid__),this._doDetach(this._boundRenderer.gl,e,t))},dispose:function(e){var t=e.gl,r=this._cache;r.use(e.__uid__);var i=r.get(Di);i&&t.deleteRenderbuffer(i);var n=r.get(Ci);n&&t.deleteFramebuffer(n),r.deleteContext(e.__uid__),this._textures={}}});Fi.DEPTH_ATTACHMENT=Bi,Fi.COLOR_ATTACHMENT0=36064,Fi.STENCIL_ATTACHMENT=36128,Fi.DEPTH_STENCIL_ATTACHMENT=R;const zi=Fi;var Gi=[\"px\",\"nx\",\"py\",\"ny\",\"pz\",\"nz\"];const Ui=m.extend((function(){var e={position:new vt,far:1e3,near:.1,texture:null,shadowMapPass:null},t=e._cameras={px:new Ei({fov:90}),nx:new Ei({fov:90}),py:new Ei({fov:90}),ny:new Ei({fov:90}),pz:new Ei({fov:90}),nz:new Ei({fov:90})};return t.px.lookAt(vt.POSITIVE_X,vt.NEGATIVE_Y),t.nx.lookAt(vt.NEGATIVE_X,vt.NEGATIVE_Y),t.py.lookAt(vt.POSITIVE_Y,vt.POSITIVE_Z),t.ny.lookAt(vt.NEGATIVE_Y,vt.NEGATIVE_Z),t.pz.lookAt(vt.POSITIVE_Z,vt.NEGATIVE_Y),t.nz.lookAt(vt.NEGATIVE_Z,vt.NEGATIVE_Y),e._frameBuffer=new zi,e}),{getCamera:function(e){return this._cameras[e]},render:function(e,t,r){var i=e.gl;r||t.update();for(var n=this.texture.width,a=2*Math.atan(n/(n-.5))/Math.PI*180,o=0;o<6;o++){var s=Gi[o],l=this._cameras[s];if(vt.copy(l.position,this.position),l.far=this.far,l.near=this.near,l.fov=a,this.shadowMapPass){l.update();var h=t.getBoundingBox();h.applyTransform(l.viewMatrix),t.viewBoundingBoxLastFrame.copy(h),this.shadowMapPass.render(e,t,l,!0)}this._frameBuffer.attach(this.texture,i.COLOR_ATTACHMENT0,i.TEXTURE_CUBE_MAP_POSITIVE_X+o),this._frameBuffer.bind(e),e.render(t,l,!0),this._frameBuffer.unbind(e)}},dispose:function(e){this._frameBuffer.dispose(e)}}),ki=Vr.extend({dynamic:!1,widthSegments:1,heightSegments:1},(function(){this.build()}),{build:function(){for(var e=this.heightSegments,t=this.widthSegments,r=this.attributes,i=[],n=[],a=[],o=[],s=0;s<=e;s++)for(var l=s/e,h=0;h<=t;h++){var u=h/t;if(i.push([2*u-1,2*l-1,0]),n&&n.push([u,l]),a&&a.push([0,0,1]),h0?this.material.define(\"fragment\",\"LOD\"):this.material.undefine(\"fragment\",\"LOD\"),e.renderPass([this],r)}}),Xi=ji;function qi(e){return e.charCodeAt(0)+(e.charCodeAt(1)<<8)+(e.charCodeAt(2)<<16)+(e.charCodeAt(3)<<24)}var Zi=qi(\"DXT1\"),Yi=qi(\"DXT3\"),Ki=qi(\"DXT5\");const Qi=function(e,t){var r=new Int32Array(e,0,31);if(542327876!==r[0])return null;if(4&!r(20))return null;var i,n,a=r(21),o=r[4],s=r[3],l=512&r[28],h=131072&r[2];switch(a){case Zi:i=8,n=wr.COMPRESSED_RGB_S3TC_DXT1_EXT;break;case Yi:i=16,n=wr.COMPRESSED_RGBA_S3TC_DXT3_EXT;break;case Ki:i=16,n=wr.COMPRESSED_RGBA_S3TC_DXT5_EXT;break;default:return null}var u=r[1]+4,c=l?6:1,d=1;h&&(d=Math.max(1,r[7]));for(var f=[],p=0;p0){var n=Math.pow(2,e[3]-128-8+i);t[r+0]=e[0]*n,t[r+1]=e[1]*n,t[r+2]=e[2]*n}else t[r+0]=0,t[r+1]=0,t[r+2]=0;return t[r+3]=1,t}function en(e,t,r,i){for(var n,a,o=0,s=0,l=i;l>0;)if(e[s][0]=t[r++],e[s][1]=t[r++],e[s][2]=t[r++],e[s][3]=t[r++],1===e[s][0]&&1===e[s][1]&&1===e[s][2]){for(var h=e[s][3]<>>0;h>0;h--)n=e[s-1],(a=e[s])[0]=n[0],a[1]=n[1],a[2]=n[2],a[3]=n[3],s++,l--;o+=8}else s++,l--,o=0;return r}function tn(e,t,r,i){if(i<8|i>32767)return en(e,t,r,i);if(2!=(n=t[r++]))return en(e,t,r-1,i);if(e[0][1]=t[r++],e[0][2]=t[r++],n=t[r++],(e[0][2]<<8>>>0|n)>>>0!==i)return null;for(var n=0;n<4;n++)for(var a=0;a128){o=(127&o)>>>0;for(var s=t[r++];o--;)e[a++][n]=s}else for(;o--;)e[a++][n]=t[r++]}return r}const rn=function(e,t,r){null==r&&(r=0);var i=new Uint8Array(e),n=i.length;if(\"#?\"===function(e,t,r){for(var i=\"\",n=0;n<2;n++)i+=Ji(e[n]);return i}(i)){for(var a=2;a=n)){a+=2;for(var o=\"\";a20)return console.warn(\"Given image is not a height map\"),e}var d,f,p,m;l%(4*i)==0?(d=o.data[l],p=o.data[l+4]):l%(4*i)==4*(i-1)?(d=o.data[l-4],p=o.data[l]):(d=o.data[l-4],p=o.data[l+4]),l<4*i?(f=o.data[l],m=o.data[l+4*i]):l>i*(n-1)*4?(f=o.data[l-4*i],m=o.data[l]):(f=o.data[l-4*i],m=o.data[l+4*i]),s.data[l]=d-p+127,s.data[l+1]=f-m+127,s.data[l+2]=255,s.data[l+3]=255}return a.putImageData(s,0,0),r},isHeightImage:function(e,t,r){if(!e||!e.width||!e.height)return!1;var i=document.createElement(\"canvas\"),n=i.getContext(\"2d\"),a=t||32;r=r||20,i.width=i.height=a,n.drawImage(e,0,0,a,a);for(var o=n.getImageData(0,0,a,a),s=0;sr)return!1}return!0},_fetchTexture:function(e,t,r){U.request.get({url:e,responseType:\"arraybuffer\",onload:t,onerror:r})},createChessboard:function(e,t,r,i){e=e||512,t=t||64,r=r||\"black\",i=i||\"white\";var n=Math.ceil(e/t),a=document.createElement(\"canvas\");a.width=e,a.height=e;var o=a.getContext(\"2d\");o.fillStyle=i,o.fillRect(0,0,e,e),o.fillStyle=r;for(var s=0;s=0||(on.forEach((function(t){e.on(t,this[sn(t)],this)}),this),this._meshes.push(e))},detachFromMesh:function(e){var t=this._meshes.indexOf(e);t>=0&&this._meshes.splice(t,1),on.forEach((function(t){e.off(t,this[sn(t)])}),this)},dispose:function(){this._meshes.forEach((function(e){this.detachFromMesh(e)}),this)}};const hn=ln,un=hi.extend({left:-1,right:1,near:-1,far:1,top:1,bottom:-1},{updateProjectionMatrix:function(){this.projectionMatrix.ortho(this.left,this.right,this.bottom,this.top,this.near,this.far)},decomposeProjectionMatrix:function(){var e=this.projectionMatrix.array;this.left=(-1-e[12])/e[0],this.right=(1-e[12])/e[0],this.top=(1-e[13])/e[5],this.bottom=(-1-e[13])/e[5],this.near=-(-1-e[14])/e[10],this.far=-(1-e[14])/e[10]},clone:function(){var e=hi.prototype.clone.call(this);return e.left=this.left,e.right=this.right,e.near=this.near,e.far=this.far,e.top=this.top,e.bottom=this.bottom,e}});Xe.import(\"\\n@export clay.compositor.vertex\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\nattribute vec3 position : POSITION;\\nattribute vec2 texcoord : TEXCOORD_0;\\nvarying vec2 v_Texcoord;\\nvoid main()\\n{\\n v_Texcoord = texcoord;\\n gl_Position = worldViewProjection * vec4(position, 1.0);\\n}\\n@end\");var cn=new ki,dn=new Sr({geometry:cn,frustumCulling:!1}),fn=new un;const pn=m.extend((function(){return{fragment:\"\",outputs:null,material:null,blendWithPrevious:!1,clearColor:!1,clearDepth:!0}}),(function(){var e=new Xe(Xe.source(\"clay.compositor.vertex\"),this.fragment),t=new le({shader:e});t.enableTexturesAll(),this.material=t}),{setUniform:function(e,t){this.material.setUniform(e,t)},getUniform:function(e){var t=this.material.uniforms[e];if(t)return t.value},attachOutput:function(e,t){this.outputs||(this.outputs={}),t=t||36064,this.outputs[t]=e},detachOutput:function(e){for(var t in this.outputs)this.outputs[t]===e&&(this.outputs[t]=null)},bind:function(e,t){if(this.outputs)for(var r in this.outputs){var i=this.outputs[r];i&&t.attach(i,r)}t&&t.bind(e)},unbind:function(e,t){t.unbind(e)},render:function(e,t){var r=e.gl;if(t){this.bind(e,t);var i=e.getGLExtension(\"EXT_draw_buffers\");if(i&&this.outputs){var n=[];for(var a in this.outputs)(a=+a)>=r.COLOR_ATTACHMENT0&&a<=r.COLOR_ATTACHMENT0+8&&n.push(a);i.drawBuffersEXT(n)}}this.trigger(\"beforerender\",this,e);var o=this.clearDepth?r.DEPTH_BUFFER_BIT:0;if(r.depthMask(!0),this.clearColor){o|=r.COLOR_BUFFER_BIT,r.colorMask(!0,!0,!0,!0);var s=this.clearColor;Array.isArray(s)&&r.clearColor(s[0],s[1],s[2],s[3])}r.clear(o),this.blendWithPrevious?(r.enable(r.BLEND),this.material.transparent=!0):(r.disable(r.BLEND),this.material.transparent=!1),this.renderQuad(e),this.trigger(\"afterrender\",this,e),t&&this.unbind(e,t)},renderQuad:function(e){dn.material=this.material,e.renderPass([dn],fn)},dispose:function(e){}});var mn={},gn=[\"px\",\"nx\",\"py\",\"ny\",\"pz\",\"nz\"];mn.prefilterEnvironmentMap=function(e,t,r,i,n){n&&i||(i=mn.generateNormalDistribution(),n=mn.integrateBRDF(e,i));var a=(r=r||{}).width||64,o=r.height||64,s=r.type||t.type,l=new Ai({width:a,height:o,type:s,flipY:!1,mipmaps:[]});l.isPowerOfTwo()||console.warn(\"Width and height must be power of two to enable mipmap.\");var h=Math.min(a,o),u=Math.log(h)/Math.log(2)+1,c=new le({shader:new Xe({vertex:Xe.source(\"clay.skybox.vertex\"),fragment:\"#define SHADER_NAME prefilter\\n#define SAMPLE_NUMBER 1024\\n#define PI 3.14159265358979\\nuniform mat4 viewInverse : VIEWINVERSE;\\nuniform samplerCube environmentMap;\\nuniform sampler2D normalDistribution;\\nuniform float roughness : 0.5;\\nvarying vec2 v_Texcoord;\\nvarying vec3 v_WorldPosition;\\n@import clay.util.rgbm\\nvec3 importanceSampleNormal(float i, float roughness, vec3 N) {\\n vec3 H = texture2D(normalDistribution, vec2(roughness, i)).rgb;\\n vec3 upVector = abs(N.y) > 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\\n vec3 tangentX = normalize(cross(N, upVector));\\n vec3 tangentZ = cross(N, tangentX);\\n return normalize(tangentX * H.x + N * H.y + tangentZ * H.z);\\n}\\nvoid main() {\\n vec3 eyePos = viewInverse[3].xyz;\\n vec3 V = normalize(v_WorldPosition - eyePos);\\n vec3 N = V;\\n vec3 prefilteredColor = vec3(0.0);\\n float totalWeight = 0.0;\\n float fMaxSampleNumber = float(SAMPLE_NUMBER);\\n for (int i = 0; i < SAMPLE_NUMBER; i++) {\\n vec3 H = importanceSampleNormal(float(i) / fMaxSampleNumber, roughness, N);\\n vec3 L = reflect(-V, H);\\n float NoL = clamp(dot(N, L), 0.0, 1.0);\\n if (NoL > 0.0) {\\n prefilteredColor += decodeHDR(textureCube(environmentMap, L)).rgb * NoL;\\n totalWeight += NoL;\\n }\\n }\\n gl_FragColor = encodeHDR(vec4(prefilteredColor / totalWeight, 1.0));\\n}\\n\"})});c.set(\"normalDistribution\",i),r.encodeRGBM&&c.define(\"fragment\",\"RGBM_ENCODE\"),r.decodeRGBM&&c.define(\"fragment\",\"RGBM_DECODE\");var d,f=new vi;if(\"texture2D\"===t.textureType){var p=new Ai({width:a,height:o,type:s===wr.FLOAT?wr.HALF_FLOAT:s});an.panoramaToCubeMap(e,t,p,{encodeRGBM:r.decodeRGBM}),t=p}(d=new ji({scene:f,material:c})).material.set(\"environmentMap\",t);var m=new Ui({texture:l});r.encodeRGBM&&(s=l.type=wr.UNSIGNED_BYTE);for(var g=new Dr({width:a,height:o,type:s}),_=new zi({depthBuffer:!1}),v=U[s===wr.UNSIGNED_BYTE?\"Uint8Array\":\"Float32Array\"],y=0;y 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\\n vec3 tangentX = normalize(cross(N, upVector));\\n vec3 tangentZ = cross(N, tangentX);\\n return normalize(tangentX * H.x + N * H.y + tangentZ * H.z);\\n}\\nfloat G_Smith(float roughness, float NoV, float NoL) {\\n float k = roughness * roughness / 2.0;\\n float G1V = NoV / (NoV * (1.0 - k) + k);\\n float G1L = NoL / (NoL * (1.0 - k) + k);\\n return G1L * G1V;\\n}\\nvoid main() {\\n vec2 uv = gl_FragCoord.xy / viewportSize;\\n float NoV = uv.x;\\n float roughness = uv.y;\\n vec3 V;\\n V.x = sqrt(1.0 - NoV * NoV);\\n V.y = 0.0;\\n V.z = NoV;\\n float A = 0.0;\\n float B = 0.0;\\n for (int i = 0; i < SAMPLE_NUMBER; i++) {\\n vec3 H = importanceSampleNormal(float(i) / fSampleNumber, roughness, N);\\n vec3 L = reflect(-V, H);\\n float NoL = clamp(L.z, 0.0, 1.0);\\n float NoH = clamp(H.z, 0.0, 1.0);\\n float VoH = clamp(dot(V, H), 0.0, 1.0);\\n if (NoL > 0.0) {\\n float G = G_Smith(roughness, NoV, NoL);\\n float G_Vis = G * VoH / (NoH * NoV);\\n float Fc = pow(1.0 - VoH, 5.0);\\n A += (1.0 - Fc) * G_Vis;\\n B += Fc * G_Vis;\\n }\\n }\\n gl_FragColor = vec4(vec2(A, B) / fSampleNumber, 0.0, 1.0);\\n}\\n\"}),n=new Dr({width:512,height:256,type:wr.HALF_FLOAT,wrapS:wr.CLAMP_TO_EDGE,wrapT:wr.CLAMP_TO_EDGE,minFilter:wr.NEAREST,magFilter:wr.NEAREST,useMipmap:!1});return i.setUniform(\"normalDistribution\",t),i.setUniform(\"viewportSize\",[512,256]),i.attachOutput(n),i.render(e,r),r.dispose(e),n},mn.generateNormalDistribution=function(e,t){for(var r=new Dr({width:e=e||256,height:t=t||1024,type:wr.FLOAT,minFilter:wr.NEAREST,magFilter:wr.NEAREST,wrapS:wr.CLAMP_TO_EDGE,wrapT:wr.CLAMP_TO_EDGE,useMipmap:!1}),i=new Float32Array(t*e*4),n=[],a=0;a>>16)>>>0;h=(((16711935&(h=((252645135&(h=((858993459&(h=((1431655765&h)<<1|(2863311530&h)>>>1)>>>0))<<2|(3435973836&h)>>>2)>>>0))<<4|(4042322160&h)>>>4)>>>0))<<8|(4278255360&h)>>>8)>>>0)/4294967296;var u=Math.sqrt((1-h)/(1+(s*s-1)*h));n[l]=u}for(l=0;l65535?Uint32Array:Uint16Array,v=this.indices=new _(t*e*6),y=this.radius,x=this.phiStart,b=this.phiLength,w=this.thetaStart,T=this.thetaLength,S=[],M=[],A=0,E=1/(y=this.radius);for(d=0;d<=e;d++)for(c=0;c<=t;c++)h=c/t,u=d/e,o=-y*Math.cos(x+h*b)*Math.sin(w+u*T),s=y*Math.cos(w+u*T),l=y*Math.sin(x+h*b)*Math.sin(w+u*T),S[0]=o,S[1]=s,S[2]=l,M[0]=h,M[1]=u,r.set(A,S),i.set(A,M),S[0]*=E,S[1]*=E,S[2]*=E,n.set(A,S),A++;var C=t+1,D=0;for(d=0;d255?255:e}function Qn(e){return e<0?0:e>1?1:e}function Jn(e){var t=e;return t.length&&\"%\"===t.charAt(t.length-1)?Kn(parseFloat(t)/100*255):Kn(parseInt(t,10))}function $n(e){var t=e;return t.length&&\"%\"===t.charAt(t.length-1)?Qn(parseFloat(t)/100):Qn(parseFloat(t))}function ea(e,t,r){return r<0?r+=1:r>1&&(r-=1),6*r<1?e+(t-e)*r*6:2*r<1?t:3*r<2?e+(t-e)*(2/3-r)*6:e}function ta(e,t,r,i,n){return e[0]=t,e[1]=r,e[2]=i,e[3]=n,e}function ra(e,t){return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e}var ia=new bi(20),na=null;function aa(e,t){na&&ra(na,t),na=ia.put(e,na||t.slice())}function oa(e,t){if(e){t=t||[];var r=ia.get(e);if(r)return ra(t,r);var i=(e+=\"\").replace(/ /g,\"\").toLowerCase();if(i in Yn)return ra(t,Yn[i]),aa(e,t),t;var n,a=i.length;if(\"#\"===i.charAt(0))return 4===a||5===a?(n=parseInt(i.slice(1,4),16))>=0&&n<=4095?(ta(t,(3840&n)>>4|(3840&n)>>8,240&n|(240&n)>>4,15&n|(15&n)<<4,5===a?parseInt(i.slice(4),16)/15:1),aa(e,t),t):void ta(t,0,0,0,1):7===a||9===a?(n=parseInt(i.slice(1,7),16))>=0&&n<=16777215?(ta(t,(16711680&n)>>16,(65280&n)>>8,255&n,9===a?parseInt(i.slice(7),16)/255:1),aa(e,t),t):void ta(t,0,0,0,1):void 0;var o=i.indexOf(\"(\"),s=i.indexOf(\")\");if(-1!==o&&s+1===a){var l=i.substr(0,o),h=i.substr(o+1,s-(o+1)).split(\",\"),u=1;switch(l){case\"rgba\":if(4!==h.length)return 3===h.length?ta(t,+h[0],+h[1],+h[2],1):ta(t,0,0,0,1);u=$n(h.pop());case\"rgb\":return 3!==h.length?void ta(t,0,0,0,1):(ta(t,Jn(h[0]),Jn(h[1]),Jn(h[2]),u),aa(e,t),t);case\"hsla\":return 4!==h.length?void ta(t,0,0,0,1):(h[3]=$n(h[3]),sa(h,t),aa(e,t),t);case\"hsl\":return 3!==h.length?void ta(t,0,0,0,1):(sa(h,t),aa(e,t),t);default:return}}ta(t,0,0,0,1)}}function sa(e,t){var r=(parseFloat(e[0])%360+360)%360/360,i=$n(e[1]),n=$n(e[2]),a=n<=.5?n*(i+1):n+i-n*i,o=2*n-a;return ta(t=t||[],Kn(255*ea(o,a,r+1/3)),Kn(255*ea(o,a,r)),Kn(255*ea(o,a,r-1/3)),1),4===e.length&&(t[3]=e[3]),t}var la=Object.prototype.toString,ha=Array.prototype,ua=ha.forEach,ca=ha.filter,da=ha.slice,fa=ha.map,pa=function(){}.constructor,ma=pa?pa.prototype:null;function ga(e,t){if(Object.assign)Object.assign(e,t);else for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r]);return e}function _a(e,t,r){if(e=\"prototype\"in e?e.prototype:e,t=\"prototype\"in t?t.prototype:t,Object.getOwnPropertyNames)for(var i=Object.getOwnPropertyNames(t),n=0;no)i.length=o;else for(var s=a;s=2&&this.interpolable},e.prototype.getAdditiveTrack=function(){return this._additiveTrack},e.prototype.addKeyframe=function(e,t){e>=this.maxTime?this.maxTime=e:this._needsSort=!0;var r=this.keyframes,i=r.length;if(this.interpolable)if(va(t)){var n=function(e){return va(e&&e[0])?2:1}(t);if(i>0&&this.arrDim!==n)return void(this.interpolable=!1);if(1===n&&\"number\"!=typeof t[0]||2===n&&\"number\"!=typeof t[0][0])return void(this.interpolable=!1);if(i>0){var a=r[i-1];this._isAllValueEqual&&(1===n&&Na(t,a.value)||(this._isAllValueEqual=!1))}this.arrDim=n}else{if(this.arrDim>0)return void(this.interpolable=!1);if(\"string\"==typeof t){var o=oa(t);o?(t=o,this.isValueColor=!0):this.interpolable=!1}else if(\"number\"!=typeof t||isNaN(t))return void(this.interpolable=!1);this._isAllValueEqual&&i>0&&(a=r[i-1],(this.isValueColor&&!Na(a.value,t)||a.value!==t)&&(this._isAllValueEqual=!1))}var s={time:e,value:t,percent:0};return this.keyframes.push(s),s},e.prototype.prepare=function(e){var t=this.keyframes;this._needsSort&&t.sort((function(e,t){return e.time-t.time}));for(var r=this.arrDim,i=t.length,n=t[i-1],a=0;a0&&a!==i-1&&Oa(t[a].value,n.value,r);if(e&&this.needsAnimate()&&e.needsAnimate()&&r===e.arrDim&&this.isValueColor===e.isValueColor&&!e._finished){this._additiveTrack=e;var o=t[0].value;for(a=0;a=0&&!(a[r].percent<=t);r--);r=Math.min(r,o-2)}else{for(r=this._lastFrame;rt);r++);r=Math.min(r-1,o-2)}var u=a[r+1],c=a[r];if(c&&u){this._lastFrame=r,this._lastFramePercent=t;var d=u.percent-c.percent;if(0!==d){var f=(t-c.percent)/d,p=i?this._additiveValue:h?za:e[s];if((l>0||h)&&!p&&(p=this._additiveValue=[]),this.useSpline){var m=a[r][n],g=a[0===r?r:r-1][n],_=a[r>o-2?o-1:r+1][n],v=a[r>o-3?o-1:r+2][n];if(l>0)1===l?Ra(p,g,m,_,v,f,f*f,f*f*f):function(e,t,r,i,n,a,o,s){for(var l=t.length,h=t[0].length,u=0;u0?1===l?Da(p,c[n],u[n],f):function(e,t,r,i){for(var n=t.length,a=n&&t[0].length,o=0;o.5?t:e}(c[n],u[n],f),i?this._additiveValue=y:e[s]=y);i&&this._addToTarget(e)}}}},e.prototype._addToTarget=function(e){var t=this.arrDim,r=this.propName,i=this._additiveValue;0===t?this.isValueColor?(oa(e[r],za),La(za,za,i,1),e[r]=Fa(za)):e[r]=e[r]+i:1===t?La(e[r],e[r],i,1):2===t&&Pa(e[r],e[r],i,1)},e}();const Ua=function(){function e(e,t,r){this._tracks={},this._trackKeys=[],this._delay=0,this._maxTime=0,this._paused=!1,this._started=0,this._clip=null,this._target=e,this._loop=t,t&&r?function(){for(var e=[],t=0;t0)){this._started=1;for(var r=this,i=[],n=0;n1){var o=a.pop();n.addKeyframe(o.time,e[i]),n.prepare(n.getAdditiveTrack())}}}},e}(),ka={_animators:null,getAnimators:function(){return this._animators=this._animators||[],this._animators},animate:function(e,t){var r;if(this._animators=this._animators||[],e){for(var i=e.split(\".\"),n=this,a=0,o=i.length;a=0&&s.splice(e,1)})),s.push(l),this.__zr&&this.__zr.animation.addAnimator(l),l},stopAnimation:function(e){this._animators=this._animators||[];for(var t=this._animators,r=t.length,i=0;i 1e-4)\\n{\\n skinMatrixWS += getSkinMatrix(joint.y) * weight.y;\\n}\\nif (weight.z > 1e-4)\\n{\\n skinMatrixWS += getSkinMatrix(joint.z) * weight.z;\\n}\\nfloat weightW = 1.0-weight.x-weight.y-weight.z;\\nif (weightW > 1e-4)\\n{\\n skinMatrixWS += getSkinMatrix(joint.w) * weightW;\\n}\\n@end\\n@export clay.chunk.instancing_header\\n#ifdef INSTANCING\\nattribute vec4 instanceMat1;\\nattribute vec4 instanceMat2;\\nattribute vec4 instanceMat3;\\n#endif\\n@end\\n@export clay.chunk.instancing_matrix\\nmat4 instanceMat = mat4(\\n vec4(instanceMat1.xyz, 0.0),\\n vec4(instanceMat2.xyz, 0.0),\\n vec4(instanceMat3.xyz, 0.0),\\n vec4(instanceMat1.w, instanceMat2.w, instanceMat3.w, 1.0)\\n);\\n@end\\n@export clay.util.parallax_correct\\nvec3 parallaxCorrect(in vec3 dir, in vec3 pos, in vec3 boxMin, in vec3 boxMax) {\\n vec3 first = (boxMax - pos) / dir;\\n vec3 second = (boxMin - pos) / dir;\\n vec3 further = max(first, second);\\n float dist = min(further.x, min(further.y, further.z));\\n vec3 fixedPos = pos + dir * dist;\\n vec3 boxCenter = (boxMax + boxMin) * 0.5;\\n return normalize(fixedPos - boxCenter);\\n}\\n@end\\n@export clay.util.clamp_sample\\nvec4 clampSample(const in sampler2D texture, const in vec2 coord)\\n{\\n#ifdef STEREO\\n float eye = step(0.5, coord.x) * 0.5;\\n vec2 coordClamped = clamp(coord, vec2(eye, 0.0), vec2(0.5 + eye, 1.0));\\n#else\\n vec2 coordClamped = clamp(coord, vec2(0.0), vec2(1.0));\\n#endif\\n return texture2D(texture, coordClamped);\\n}\\n@end\\n@export clay.util.ACES\\nvec3 ACESToneMapping(vec3 color)\\n{\\n const float A = 2.51;\\n const float B = 0.03;\\n const float C = 2.43;\\n const float D = 0.59;\\n const float E = 0.14;\\n return (color * (A * color + B)) / (color * (C * color + D) + E);\\n}\\n@end\";function Ha(e){return e instanceof HTMLCanvasElement||e instanceof HTMLImageElement||e instanceof Image}Object.assign(sr.prototype,ka),Xe.import(Va),Xe.import(qe),Xe.import(\"\\n@export ecgl.common.transformUniforms\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\\nuniform mat4 world : WORLD;\\n@end\\n\\n@export ecgl.common.attributes\\nattribute vec3 position : POSITION;\\nattribute vec2 texcoord : TEXCOORD_0;\\nattribute vec3 normal : NORMAL;\\n@end\\n\\n@export ecgl.common.uv.header\\nuniform vec2 uvRepeat : [1.0, 1.0];\\nuniform vec2 uvOffset : [0.0, 0.0];\\nuniform vec2 detailUvRepeat : [1.0, 1.0];\\nuniform vec2 detailUvOffset : [0.0, 0.0];\\n\\nvarying vec2 v_Texcoord;\\nvarying vec2 v_DetailTexcoord;\\n@end\\n\\n@export ecgl.common.uv.main\\nv_Texcoord = texcoord * uvRepeat + uvOffset;\\nv_DetailTexcoord = texcoord * detailUvRepeat + detailUvOffset;\\n@end\\n\\n@export ecgl.common.uv.fragmentHeader\\nvarying vec2 v_Texcoord;\\nvarying vec2 v_DetailTexcoord;\\n@end\\n\\n\\n@export ecgl.common.albedo.main\\n\\n vec4 albedoTexel = vec4(1.0);\\n#ifdef DIFFUSEMAP_ENABLED\\n albedoTexel = texture2D(diffuseMap, v_Texcoord);\\n #ifdef SRGB_DECODE\\n albedoTexel = sRGBToLinear(albedoTexel);\\n #endif\\n#endif\\n\\n#ifdef DETAILMAP_ENABLED\\n vec4 detailTexel = texture2D(detailMap, v_DetailTexcoord);\\n #ifdef SRGB_DECODE\\n detailTexel = sRGBToLinear(detailTexel);\\n #endif\\n albedoTexel.rgb = mix(albedoTexel.rgb, detailTexel.rgb, detailTexel.a);\\n albedoTexel.a = detailTexel.a + (1.0 - detailTexel.a) * albedoTexel.a;\\n#endif\\n\\n@end\\n\\n@export ecgl.common.wireframe.vertexHeader\\n\\n#ifdef WIREFRAME_QUAD\\nattribute vec4 barycentric;\\nvarying vec4 v_Barycentric;\\n#elif defined(WIREFRAME_TRIANGLE)\\nattribute vec3 barycentric;\\nvarying vec3 v_Barycentric;\\n#endif\\n\\n@end\\n\\n@export ecgl.common.wireframe.vertexMain\\n\\n#if defined(WIREFRAME_QUAD) || defined(WIREFRAME_TRIANGLE)\\n v_Barycentric = barycentric;\\n#endif\\n\\n@end\\n\\n\\n@export ecgl.common.wireframe.fragmentHeader\\n\\nuniform float wireframeLineWidth : 1;\\nuniform vec4 wireframeLineColor: [0, 0, 0, 0.5];\\n\\n#ifdef WIREFRAME_QUAD\\nvarying vec4 v_Barycentric;\\nfloat edgeFactor () {\\n vec4 d = fwidth(v_Barycentric);\\n vec4 a4 = smoothstep(vec4(0.0), d * wireframeLineWidth, v_Barycentric);\\n return min(min(min(a4.x, a4.y), a4.z), a4.w);\\n}\\n#elif defined(WIREFRAME_TRIANGLE)\\nvarying vec3 v_Barycentric;\\nfloat edgeFactor () {\\n vec3 d = fwidth(v_Barycentric);\\n vec3 a3 = smoothstep(vec3(0.0), d * wireframeLineWidth, v_Barycentric);\\n return min(min(a3.x, a3.y), a3.z);\\n}\\n#endif\\n\\n@end\\n\\n\\n@export ecgl.common.wireframe.fragmentMain\\n\\n#if defined(WIREFRAME_QUAD) || defined(WIREFRAME_TRIANGLE)\\n if (wireframeLineWidth > 0.) {\\n vec4 lineColor = wireframeLineColor;\\n#ifdef SRGB_DECODE\\n lineColor = sRGBToLinear(lineColor);\\n#endif\\n\\n gl_FragColor.rgb = mix(gl_FragColor.rgb, lineColor.rgb, (1.0 - edgeFactor()) * lineColor.a);\\n }\\n#endif\\n@end\\n\\n\\n\\n\\n@export ecgl.common.bumpMap.header\\n\\n#ifdef BUMPMAP_ENABLED\\nuniform sampler2D bumpMap;\\nuniform float bumpScale : 1.0;\\n\\n\\nvec3 bumpNormal(vec3 surfPos, vec3 surfNormal, vec3 baseNormal)\\n{\\n vec2 dSTdx = dFdx(v_Texcoord);\\n vec2 dSTdy = dFdy(v_Texcoord);\\n\\n float Hll = bumpScale * texture2D(bumpMap, v_Texcoord).x;\\n float dHx = bumpScale * texture2D(bumpMap, v_Texcoord + dSTdx).x - Hll;\\n float dHy = bumpScale * texture2D(bumpMap, v_Texcoord + dSTdy).x - Hll;\\n\\n vec3 vSigmaX = dFdx(surfPos);\\n vec3 vSigmaY = dFdy(surfPos);\\n vec3 vN = surfNormal;\\n\\n vec3 R1 = cross(vSigmaY, vN);\\n vec3 R2 = cross(vN, vSigmaX);\\n\\n float fDet = dot(vSigmaX, R1);\\n\\n vec3 vGrad = sign(fDet) * (dHx * R1 + dHy * R2);\\n return normalize(abs(fDet) * baseNormal - vGrad);\\n\\n}\\n#endif\\n\\n@end\\n\\n@export ecgl.common.normalMap.vertexHeader\\n\\n#ifdef NORMALMAP_ENABLED\\nattribute vec4 tangent : TANGENT;\\nvarying vec3 v_Tangent;\\nvarying vec3 v_Bitangent;\\n#endif\\n\\n@end\\n\\n@export ecgl.common.normalMap.vertexMain\\n\\n#ifdef NORMALMAP_ENABLED\\n if (dot(tangent, tangent) > 0.0) {\\n v_Tangent = normalize((worldInverseTranspose * vec4(tangent.xyz, 0.0)).xyz);\\n v_Bitangent = normalize(cross(v_Normal, v_Tangent) * tangent.w);\\n }\\n#endif\\n\\n@end\\n\\n\\n@export ecgl.common.normalMap.fragmentHeader\\n\\n#ifdef NORMALMAP_ENABLED\\nuniform sampler2D normalMap;\\nvarying vec3 v_Tangent;\\nvarying vec3 v_Bitangent;\\n#endif\\n\\n@end\\n\\n@export ecgl.common.normalMap.fragmentMain\\n#ifdef NORMALMAP_ENABLED\\n if (dot(v_Tangent, v_Tangent) > 0.0) {\\n vec3 normalTexel = texture2D(normalMap, v_DetailTexcoord).xyz;\\n if (dot(normalTexel, normalTexel) > 0.0) { N = normalTexel * 2.0 - 1.0;\\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\\n N = normalize(tbn * N);\\n }\\n }\\n#endif\\n@end\\n\\n\\n\\n@export ecgl.common.vertexAnimation.header\\n\\n#ifdef VERTEX_ANIMATION\\nattribute vec3 prevPosition;\\nattribute vec3 prevNormal;\\nuniform float percent;\\n#endif\\n\\n@end\\n\\n@export ecgl.common.vertexAnimation.main\\n\\n#ifdef VERTEX_ANIMATION\\n vec3 pos = mix(prevPosition, position, percent);\\n vec3 norm = mix(prevNormal, normal, percent);\\n#else\\n vec3 pos = position;\\n vec3 norm = normal;\\n#endif\\n\\n@end\\n\\n\\n@export ecgl.common.ssaoMap.header\\n#ifdef SSAOMAP_ENABLED\\nuniform sampler2D ssaoMap;\\nuniform vec4 viewport : VIEWPORT;\\n#endif\\n@end\\n\\n@export ecgl.common.ssaoMap.main\\n float ao = 1.0;\\n#ifdef SSAOMAP_ENABLED\\n ao = texture2D(ssaoMap, (gl_FragCoord.xy - viewport.xy) / viewport.zw).r;\\n#endif\\n@end\\n\\n\\n\\n\\n@export ecgl.common.diffuseLayer.header\\n\\n#if (LAYER_DIFFUSEMAP_COUNT > 0)\\nuniform float layerDiffuseIntensity[LAYER_DIFFUSEMAP_COUNT];\\nuniform sampler2D layerDiffuseMap[LAYER_DIFFUSEMAP_COUNT];\\n#endif\\n\\n@end\\n\\n@export ecgl.common.emissiveLayer.header\\n\\n#if (LAYER_EMISSIVEMAP_COUNT > 0)\\nuniform float layerEmissionIntensity[LAYER_EMISSIVEMAP_COUNT];\\nuniform sampler2D layerEmissiveMap[LAYER_EMISSIVEMAP_COUNT];\\n#endif\\n\\n@end\\n\\n@export ecgl.common.layers.header\\n@import ecgl.common.diffuseLayer.header\\n@import ecgl.common.emissiveLayer.header\\n@end\\n\\n@export ecgl.common.diffuseLayer.main\\n\\n#if (LAYER_DIFFUSEMAP_COUNT > 0)\\n for (int _idx_ = 0; _idx_ < LAYER_DIFFUSEMAP_COUNT; _idx_++) {{\\n float intensity = layerDiffuseIntensity[_idx_];\\n vec4 texel2 = texture2D(layerDiffuseMap[_idx_], v_Texcoord);\\n #ifdef SRGB_DECODE\\n texel2 = sRGBToLinear(texel2);\\n #endif\\n albedoTexel.rgb = mix(albedoTexel.rgb, texel2.rgb * intensity, texel2.a);\\n albedoTexel.a = texel2.a + (1.0 - texel2.a) * albedoTexel.a;\\n }}\\n#endif\\n\\n@end\\n\\n@export ecgl.common.emissiveLayer.main\\n\\n#if (LAYER_EMISSIVEMAP_COUNT > 0)\\n for (int _idx_ = 0; _idx_ < LAYER_EMISSIVEMAP_COUNT; _idx_++)\\n {{\\n vec4 texel2 = texture2D(layerEmissiveMap[_idx_], v_Texcoord) * layerEmissionIntensity[_idx_];\\n #ifdef SRGB_DECODE\\n texel2 = sRGBToLinear(texel2);\\n #endif\\n float intensity = layerEmissionIntensity[_idx_];\\n gl_FragColor.rgb += texel2.rgb * texel2.a * intensity;\\n }}\\n#endif\\n\\n@end\\n\"),Xe.import(\"@export ecgl.color.vertex\\n\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\n\\n@import ecgl.common.uv.header\\n\\nattribute vec2 texcoord : TEXCOORD_0;\\nattribute vec3 position: POSITION;\\n\\n@import ecgl.common.wireframe.vertexHeader\\n\\n#ifdef VERTEX_COLOR\\nattribute vec4 a_Color : COLOR;\\nvarying vec4 v_Color;\\n#endif\\n\\n#ifdef VERTEX_ANIMATION\\nattribute vec3 prevPosition;\\nuniform float percent : 1.0;\\n#endif\\n\\n#ifdef ATMOSPHERE_ENABLED\\nattribute vec3 normal: NORMAL;\\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\\nvarying vec3 v_Normal;\\n#endif\\n\\nvoid main()\\n{\\n#ifdef VERTEX_ANIMATION\\n vec3 pos = mix(prevPosition, position, percent);\\n#else\\n vec3 pos = position;\\n#endif\\n\\n gl_Position = worldViewProjection * vec4(pos, 1.0);\\n\\n @import ecgl.common.uv.main\\n\\n#ifdef VERTEX_COLOR\\n v_Color = a_Color;\\n#endif\\n\\n#ifdef ATMOSPHERE_ENABLED\\n v_Normal = normalize((worldInverseTranspose * vec4(normal, 0.0)).xyz);\\n#endif\\n\\n @import ecgl.common.wireframe.vertexMain\\n\\n}\\n\\n@end\\n\\n@export ecgl.color.fragment\\n\\n#define LAYER_DIFFUSEMAP_COUNT 0\\n#define LAYER_EMISSIVEMAP_COUNT 0\\n\\nuniform sampler2D diffuseMap;\\nuniform sampler2D detailMap;\\n\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\n\\n#ifdef ATMOSPHERE_ENABLED\\nuniform mat4 viewTranspose: VIEWTRANSPOSE;\\nuniform vec3 glowColor;\\nuniform float glowPower;\\nvarying vec3 v_Normal;\\n#endif\\n\\n#ifdef VERTEX_COLOR\\nvarying vec4 v_Color;\\n#endif\\n\\n@import ecgl.common.layers.header\\n\\n@import ecgl.common.uv.fragmentHeader\\n\\n@import ecgl.common.wireframe.fragmentHeader\\n\\n@import clay.util.srgb\\n\\nvoid main()\\n{\\n#ifdef SRGB_DECODE\\n gl_FragColor = sRGBToLinear(color);\\n#else\\n gl_FragColor = color;\\n#endif\\n\\n#ifdef VERTEX_COLOR\\n gl_FragColor *= v_Color;\\n#endif\\n\\n @import ecgl.common.albedo.main\\n\\n @import ecgl.common.diffuseLayer.main\\n\\n gl_FragColor *= albedoTexel;\\n\\n#ifdef ATMOSPHERE_ENABLED\\n float atmoIntensity = pow(1.0 - dot(v_Normal, (viewTranspose * vec4(0.0, 0.0, 1.0, 0.0)).xyz), glowPower);\\n gl_FragColor.rgb += glowColor * atmoIntensity;\\n#endif\\n\\n @import ecgl.common.emissiveLayer.main\\n\\n @import ecgl.common.wireframe.fragmentMain\\n\\n}\\n@end\"),Xe.import(\"/**\\n * http: */\\n\\n@export ecgl.lambert.vertex\\n\\n@import ecgl.common.transformUniforms\\n\\n@import ecgl.common.uv.header\\n\\n\\n@import ecgl.common.attributes\\n\\n@import ecgl.common.wireframe.vertexHeader\\n\\n#ifdef VERTEX_COLOR\\nattribute vec4 a_Color : COLOR;\\nvarying vec4 v_Color;\\n#endif\\n\\n\\n@import ecgl.common.vertexAnimation.header\\n\\n\\nvarying vec3 v_Normal;\\nvarying vec3 v_WorldPosition;\\n\\nvoid main()\\n{\\n @import ecgl.common.uv.main\\n\\n @import ecgl.common.vertexAnimation.main\\n\\n\\n gl_Position = worldViewProjection * vec4(pos, 1.0);\\n\\n v_Normal = normalize((worldInverseTranspose * vec4(norm, 0.0)).xyz);\\n v_WorldPosition = (world * vec4(pos, 1.0)).xyz;\\n\\n#ifdef VERTEX_COLOR\\n v_Color = a_Color;\\n#endif\\n\\n @import ecgl.common.wireframe.vertexMain\\n}\\n\\n@end\\n\\n\\n@export ecgl.lambert.fragment\\n\\n#define LAYER_DIFFUSEMAP_COUNT 0\\n#define LAYER_EMISSIVEMAP_COUNT 0\\n\\n#define NORMAL_UP_AXIS 1\\n#define NORMAL_FRONT_AXIS 2\\n\\n@import ecgl.common.uv.fragmentHeader\\n\\nvarying vec3 v_Normal;\\nvarying vec3 v_WorldPosition;\\n\\nuniform sampler2D diffuseMap;\\nuniform sampler2D detailMap;\\n\\n@import ecgl.common.layers.header\\n\\nuniform float emissionIntensity: 1.0;\\n\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\n\\nuniform mat4 viewInverse : VIEWINVERSE;\\n\\n#ifdef ATMOSPHERE_ENABLED\\nuniform mat4 viewTranspose: VIEWTRANSPOSE;\\nuniform vec3 glowColor;\\nuniform float glowPower;\\n#endif\\n\\n#ifdef AMBIENT_LIGHT_COUNT\\n@import clay.header.ambient_light\\n#endif\\n#ifdef AMBIENT_SH_LIGHT_COUNT\\n@import clay.header.ambient_sh_light\\n#endif\\n\\n#ifdef DIRECTIONAL_LIGHT_COUNT\\n@import clay.header.directional_light\\n#endif\\n\\n#ifdef VERTEX_COLOR\\nvarying vec4 v_Color;\\n#endif\\n\\n\\n@import ecgl.common.ssaoMap.header\\n\\n@import ecgl.common.bumpMap.header\\n\\n@import clay.util.srgb\\n\\n@import ecgl.common.wireframe.fragmentHeader\\n\\n@import clay.plugin.compute_shadow_map\\n\\nvoid main()\\n{\\n#ifdef SRGB_DECODE\\n gl_FragColor = sRGBToLinear(color);\\n#else\\n gl_FragColor = color;\\n#endif\\n\\n#ifdef VERTEX_COLOR\\n #ifdef SRGB_DECODE\\n gl_FragColor *= sRGBToLinear(v_Color);\\n #else\\n gl_FragColor *= v_Color;\\n #endif\\n#endif\\n\\n @import ecgl.common.albedo.main\\n\\n @import ecgl.common.diffuseLayer.main\\n\\n gl_FragColor *= albedoTexel;\\n\\n vec3 N = v_Normal;\\n#ifdef DOUBLE_SIDED\\n vec3 eyePos = viewInverse[3].xyz;\\n vec3 V = normalize(eyePos - v_WorldPosition);\\n\\n if (dot(N, V) < 0.0) {\\n N = -N;\\n }\\n#endif\\n\\n float ambientFactor = 1.0;\\n\\n#ifdef BUMPMAP_ENABLED\\n N = bumpNormal(v_WorldPosition, v_Normal, N);\\n ambientFactor = dot(v_Normal, N);\\n#endif\\n\\n vec3 N2 = vec3(N.x, N[NORMAL_UP_AXIS], N[NORMAL_FRONT_AXIS]);\\n\\n vec3 diffuseColor = vec3(0.0, 0.0, 0.0);\\n\\n @import ecgl.common.ssaoMap.main\\n\\n#ifdef AMBIENT_LIGHT_COUNT\\n for(int i = 0; i < AMBIENT_LIGHT_COUNT; i++)\\n {\\n diffuseColor += ambientLightColor[i] * ambientFactor * ao;\\n }\\n#endif\\n#ifdef AMBIENT_SH_LIGHT_COUNT\\n for(int _idx_ = 0; _idx_ < AMBIENT_SH_LIGHT_COUNT; _idx_++)\\n {{\\n diffuseColor += calcAmbientSHLight(_idx_, N2) * ambientSHLightColor[_idx_] * ao;\\n }}\\n#endif\\n#ifdef DIRECTIONAL_LIGHT_COUNT\\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\\n float shadowContribsDir[DIRECTIONAL_LIGHT_COUNT];\\n if(shadowEnabled)\\n {\\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribsDir);\\n }\\n#endif\\n for(int i = 0; i < DIRECTIONAL_LIGHT_COUNT; i++)\\n {\\n vec3 lightDirection = -directionalLightDirection[i];\\n vec3 lightColor = directionalLightColor[i];\\n\\n float shadowContrib = 1.0;\\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\\n if (shadowEnabled)\\n {\\n shadowContrib = shadowContribsDir[i];\\n }\\n#endif\\n\\n float ndl = dot(N, normalize(lightDirection)) * shadowContrib;\\n\\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0);\\n }\\n#endif\\n\\n gl_FragColor.rgb *= diffuseColor;\\n\\n#ifdef ATMOSPHERE_ENABLED\\n float atmoIntensity = pow(1.0 - dot(v_Normal, (viewTranspose * vec4(0.0, 0.0, 1.0, 0.0)).xyz), glowPower);\\n gl_FragColor.rgb += glowColor * atmoIntensity;\\n#endif\\n\\n @import ecgl.common.emissiveLayer.main\\n\\n @import ecgl.common.wireframe.fragmentMain\\n}\\n\\n@end\"),Xe.import(\"@export ecgl.realistic.vertex\\n\\n@import ecgl.common.transformUniforms\\n\\n@import ecgl.common.uv.header\\n\\n@import ecgl.common.attributes\\n\\n\\n@import ecgl.common.wireframe.vertexHeader\\n\\n#ifdef VERTEX_COLOR\\nattribute vec4 a_Color : COLOR;\\nvarying vec4 v_Color;\\n#endif\\n\\n#ifdef NORMALMAP_ENABLED\\nattribute vec4 tangent : TANGENT;\\nvarying vec3 v_Tangent;\\nvarying vec3 v_Bitangent;\\n#endif\\n\\n@import ecgl.common.vertexAnimation.header\\n\\nvarying vec3 v_Normal;\\nvarying vec3 v_WorldPosition;\\n\\nvoid main()\\n{\\n\\n @import ecgl.common.uv.main\\n\\n @import ecgl.common.vertexAnimation.main\\n\\n gl_Position = worldViewProjection * vec4(pos, 1.0);\\n\\n v_Normal = normalize((worldInverseTranspose * vec4(norm, 0.0)).xyz);\\n v_WorldPosition = (world * vec4(pos, 1.0)).xyz;\\n\\n#ifdef VERTEX_COLOR\\n v_Color = a_Color;\\n#endif\\n\\n#ifdef NORMALMAP_ENABLED\\n v_Tangent = normalize((worldInverseTranspose * vec4(tangent.xyz, 0.0)).xyz);\\n v_Bitangent = normalize(cross(v_Normal, v_Tangent) * tangent.w);\\n#endif\\n\\n @import ecgl.common.wireframe.vertexMain\\n\\n}\\n\\n@end\\n\\n\\n\\n@export ecgl.realistic.fragment\\n\\n#define LAYER_DIFFUSEMAP_COUNT 0\\n#define LAYER_EMISSIVEMAP_COUNT 0\\n#define PI 3.14159265358979\\n#define ROUGHNESS_CHANEL 0\\n#define METALNESS_CHANEL 1\\n\\n#define NORMAL_UP_AXIS 1\\n#define NORMAL_FRONT_AXIS 2\\n\\n#ifdef VERTEX_COLOR\\nvarying vec4 v_Color;\\n#endif\\n\\n@import ecgl.common.uv.fragmentHeader\\n\\nvarying vec3 v_Normal;\\nvarying vec3 v_WorldPosition;\\n\\nuniform sampler2D diffuseMap;\\n\\nuniform sampler2D detailMap;\\nuniform sampler2D metalnessMap;\\nuniform sampler2D roughnessMap;\\n\\n@import ecgl.common.layers.header\\n\\nuniform float emissionIntensity: 1.0;\\n\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\n\\nuniform float metalness : 0.0;\\nuniform float roughness : 0.5;\\n\\nuniform mat4 viewInverse : VIEWINVERSE;\\n\\n#ifdef ATMOSPHERE_ENABLED\\nuniform mat4 viewTranspose: VIEWTRANSPOSE;\\nuniform vec3 glowColor;\\nuniform float glowPower;\\n#endif\\n\\n#ifdef AMBIENT_LIGHT_COUNT\\n@import clay.header.ambient_light\\n#endif\\n\\n#ifdef AMBIENT_SH_LIGHT_COUNT\\n@import clay.header.ambient_sh_light\\n#endif\\n\\n#ifdef AMBIENT_CUBEMAP_LIGHT_COUNT\\n@import clay.header.ambient_cubemap_light\\n#endif\\n\\n#ifdef DIRECTIONAL_LIGHT_COUNT\\n@import clay.header.directional_light\\n#endif\\n\\n@import ecgl.common.normalMap.fragmentHeader\\n\\n@import ecgl.common.ssaoMap.header\\n\\n@import ecgl.common.bumpMap.header\\n\\n@import clay.util.srgb\\n\\n@import clay.util.rgbm\\n\\n@import ecgl.common.wireframe.fragmentHeader\\n\\n@import clay.plugin.compute_shadow_map\\n\\nvec3 F_Schlick(float ndv, vec3 spec) {\\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\\n}\\n\\nfloat D_Phong(float g, float ndh) {\\n float a = pow(8192.0, g);\\n return (a + 2.0) / 8.0 * pow(ndh, a);\\n}\\n\\nvoid main()\\n{\\n vec4 albedoColor = color;\\n\\n vec3 eyePos = viewInverse[3].xyz;\\n vec3 V = normalize(eyePos - v_WorldPosition);\\n#ifdef VERTEX_COLOR\\n #ifdef SRGB_DECODE\\n albedoColor *= sRGBToLinear(v_Color);\\n #else\\n albedoColor *= v_Color;\\n #endif\\n#endif\\n\\n @import ecgl.common.albedo.main\\n\\n @import ecgl.common.diffuseLayer.main\\n\\n albedoColor *= albedoTexel;\\n\\n float m = metalness;\\n\\n#ifdef METALNESSMAP_ENABLED\\n float m2 = texture2D(metalnessMap, v_DetailTexcoord)[METALNESS_CHANEL];\\n m = clamp(m2 + (m - 0.5) * 2.0, 0.0, 1.0);\\n#endif\\n\\n vec3 baseColor = albedoColor.rgb;\\n albedoColor.rgb = baseColor * (1.0 - m);\\n vec3 specFactor = mix(vec3(0.04), baseColor, m);\\n\\n float g = 1.0 - roughness;\\n\\n#ifdef ROUGHNESSMAP_ENABLED\\n float g2 = 1.0 - texture2D(roughnessMap, v_DetailTexcoord)[ROUGHNESS_CHANEL];\\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\\n#endif\\n\\n vec3 N = v_Normal;\\n\\n#ifdef DOUBLE_SIDED\\n if (dot(N, V) < 0.0) {\\n N = -N;\\n }\\n#endif\\n\\n float ambientFactor = 1.0;\\n\\n#ifdef BUMPMAP_ENABLED\\n N = bumpNormal(v_WorldPosition, v_Normal, N);\\n ambientFactor = dot(v_Normal, N);\\n#endif\\n\\n@import ecgl.common.normalMap.fragmentMain\\n\\n vec3 N2 = vec3(N.x, N[NORMAL_UP_AXIS], N[NORMAL_FRONT_AXIS]);\\n\\n vec3 diffuseTerm = vec3(0.0);\\n vec3 specularTerm = vec3(0.0);\\n\\n float ndv = clamp(dot(N, V), 0.0, 1.0);\\n vec3 fresnelTerm = F_Schlick(ndv, specFactor);\\n\\n @import ecgl.common.ssaoMap.main\\n\\n#ifdef AMBIENT_LIGHT_COUNT\\n for(int _idx_ = 0; _idx_ < AMBIENT_LIGHT_COUNT; _idx_++)\\n {{\\n diffuseTerm += ambientLightColor[_idx_] * ambientFactor * ao;\\n }}\\n#endif\\n\\n#ifdef AMBIENT_SH_LIGHT_COUNT\\n for(int _idx_ = 0; _idx_ < AMBIENT_SH_LIGHT_COUNT; _idx_++)\\n {{\\n diffuseTerm += calcAmbientSHLight(_idx_, N2) * ambientSHLightColor[_idx_] * ao;\\n }}\\n#endif\\n\\n#ifdef DIRECTIONAL_LIGHT_COUNT\\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\\n float shadowContribsDir[DIRECTIONAL_LIGHT_COUNT];\\n if(shadowEnabled)\\n {\\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribsDir);\\n }\\n#endif\\n for(int _idx_ = 0; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++)\\n {{\\n vec3 L = -directionalLightDirection[_idx_];\\n vec3 lc = directionalLightColor[_idx_];\\n\\n vec3 H = normalize(L + V);\\n float ndl = clamp(dot(N, normalize(L)), 0.0, 1.0);\\n float ndh = clamp(dot(N, H), 0.0, 1.0);\\n\\n float shadowContrib = 1.0;\\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\\n if (shadowEnabled)\\n {\\n shadowContrib = shadowContribsDir[_idx_];\\n }\\n#endif\\n\\n vec3 li = lc * ndl * shadowContrib;\\n\\n diffuseTerm += li;\\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\\n }}\\n#endif\\n\\n\\n#ifdef AMBIENT_CUBEMAP_LIGHT_COUNT\\n vec3 L = reflect(-V, N);\\n L = vec3(L.x, L[NORMAL_UP_AXIS], L[NORMAL_FRONT_AXIS]);\\n float rough2 = clamp(1.0 - g, 0.0, 1.0);\\n float bias2 = rough2 * 5.0;\\n vec2 brdfParam2 = texture2D(ambientCubemapLightBRDFLookup[0], vec2(rough2, ndv)).xy;\\n vec3 envWeight2 = specFactor * brdfParam2.x + brdfParam2.y;\\n vec3 envTexel2;\\n for(int _idx_ = 0; _idx_ < AMBIENT_CUBEMAP_LIGHT_COUNT; _idx_++)\\n {{\\n envTexel2 = RGBMDecode(textureCubeLodEXT(ambientCubemapLightCubemap[_idx_], L, bias2), 8.12);\\n specularTerm += ambientCubemapLightColor[_idx_] * envTexel2 * envWeight2 * ao;\\n }}\\n#endif\\n\\n gl_FragColor.rgb = albedoColor.rgb * diffuseTerm + specularTerm;\\n gl_FragColor.a = albedoColor.a;\\n\\n#ifdef ATMOSPHERE_ENABLED\\n float atmoIntensity = pow(1.0 - dot(v_Normal, (viewTranspose * vec4(0.0, 0.0, 1.0, 0.0)).xyz), glowPower);\\n gl_FragColor.rgb += glowColor * atmoIntensity;\\n#endif\\n\\n#ifdef SRGB_ENCODE\\n gl_FragColor = linearTosRGB(gl_FragColor);\\n#endif\\n\\n @import ecgl.common.emissiveLayer.main\\n\\n @import ecgl.common.wireframe.fragmentMain\\n}\\n\\n@end\"),Xe.import(\"@export ecgl.hatching.vertex\\n\\n@import ecgl.realistic.vertex\\n\\n@end\\n\\n\\n@export ecgl.hatching.fragment\\n\\n#define NORMAL_UP_AXIS 1\\n#define NORMAL_FRONT_AXIS 2\\n\\n@import ecgl.common.uv.fragmentHeader\\n\\nvarying vec3 v_Normal;\\nvarying vec3 v_WorldPosition;\\n\\nuniform vec4 color : [0.0, 0.0, 0.0, 1.0];\\nuniform vec4 paperColor : [1.0, 1.0, 1.0, 1.0];\\n\\nuniform mat4 viewInverse : VIEWINVERSE;\\n\\n#ifdef AMBIENT_LIGHT_COUNT\\n@import clay.header.ambient_light\\n#endif\\n#ifdef AMBIENT_SH_LIGHT_COUNT\\n@import clay.header.ambient_sh_light\\n#endif\\n\\n#ifdef DIRECTIONAL_LIGHT_COUNT\\n@import clay.header.directional_light\\n#endif\\n\\n#ifdef VERTEX_COLOR\\nvarying vec4 v_Color;\\n#endif\\n\\n\\n@import ecgl.common.ssaoMap.header\\n\\n@import ecgl.common.bumpMap.header\\n\\n@import clay.util.srgb\\n\\n@import ecgl.common.wireframe.fragmentHeader\\n\\n@import clay.plugin.compute_shadow_map\\n\\nuniform sampler2D hatch1;\\nuniform sampler2D hatch2;\\nuniform sampler2D hatch3;\\nuniform sampler2D hatch4;\\nuniform sampler2D hatch5;\\nuniform sampler2D hatch6;\\n\\nfloat shade(in float tone) {\\n vec4 c = vec4(1. ,1., 1., 1.);\\n float step = 1. / 6.;\\n vec2 uv = v_DetailTexcoord;\\n if (tone <= step / 2.0) {\\n c = mix(vec4(0.), texture2D(hatch6, uv), 12. * tone);\\n }\\n else if (tone <= step) {\\n c = mix(texture2D(hatch6, uv), texture2D(hatch5, uv), 6. * tone);\\n }\\n if(tone > step && tone <= 2. * step){\\n c = mix(texture2D(hatch5, uv), texture2D(hatch4, uv) , 6. * (tone - step));\\n }\\n if(tone > 2. * step && tone <= 3. * step){\\n c = mix(texture2D(hatch4, uv), texture2D(hatch3, uv), 6. * (tone - 2. * step));\\n }\\n if(tone > 3. * step && tone <= 4. * step){\\n c = mix(texture2D(hatch3, uv), texture2D(hatch2, uv), 6. * (tone - 3. * step));\\n }\\n if(tone > 4. * step && tone <= 5. * step){\\n c = mix(texture2D(hatch2, uv), texture2D(hatch1, uv), 6. * (tone - 4. * step));\\n }\\n if(tone > 5. * step){\\n c = mix(texture2D(hatch1, uv), vec4(1.), 6. * (tone - 5. * step));\\n }\\n\\n return c.r;\\n}\\n\\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\\n\\nvoid main()\\n{\\n#ifdef SRGB_DECODE\\n vec4 inkColor = sRGBToLinear(color);\\n#else\\n vec4 inkColor = color;\\n#endif\\n\\n#ifdef VERTEX_COLOR\\n #ifdef SRGB_DECODE\\n inkColor *= sRGBToLinear(v_Color);\\n #else\\n inkColor *= v_Color;\\n #endif\\n#endif\\n\\n vec3 N = v_Normal;\\n#ifdef DOUBLE_SIDED\\n vec3 eyePos = viewInverse[3].xyz;\\n vec3 V = normalize(eyePos - v_WorldPosition);\\n\\n if (dot(N, V) < 0.0) {\\n N = -N;\\n }\\n#endif\\n\\n float tone = 0.0;\\n\\n float ambientFactor = 1.0;\\n\\n#ifdef BUMPMAP_ENABLED\\n N = bumpNormal(v_WorldPosition, v_Normal, N);\\n ambientFactor = dot(v_Normal, N);\\n#endif\\n\\n vec3 N2 = vec3(N.x, N[NORMAL_UP_AXIS], N[NORMAL_FRONT_AXIS]);\\n\\n @import ecgl.common.ssaoMap.main\\n\\n#ifdef AMBIENT_LIGHT_COUNT\\n for(int i = 0; i < AMBIENT_LIGHT_COUNT; i++)\\n {\\n tone += dot(ambientLightColor[i], w) * ambientFactor * ao;\\n }\\n#endif\\n#ifdef AMBIENT_SH_LIGHT_COUNT\\n for(int _idx_ = 0; _idx_ < AMBIENT_SH_LIGHT_COUNT; _idx_++)\\n {{\\n tone += dot(calcAmbientSHLight(_idx_, N2) * ambientSHLightColor[_idx_], w) * ao;\\n }}\\n#endif\\n#ifdef DIRECTIONAL_LIGHT_COUNT\\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\\n float shadowContribsDir[DIRECTIONAL_LIGHT_COUNT];\\n if(shadowEnabled)\\n {\\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribsDir);\\n }\\n#endif\\n for(int i = 0; i < DIRECTIONAL_LIGHT_COUNT; i++)\\n {\\n vec3 lightDirection = -directionalLightDirection[i];\\n float lightTone = dot(directionalLightColor[i], w);\\n\\n float shadowContrib = 1.0;\\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\\n if (shadowEnabled)\\n {\\n shadowContrib = shadowContribsDir[i];\\n }\\n#endif\\n\\n float ndl = dot(N, normalize(lightDirection)) * shadowContrib;\\n\\n tone += lightTone * clamp(ndl, 0.0, 1.0);\\n }\\n#endif\\n\\n gl_FragColor = mix(inkColor, paperColor, shade(clamp(tone, 0.0, 1.0)));\\n }\\n@end\\n\"),Xe.import(\"@export ecgl.sm.depth.vertex\\n\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\n\\nattribute vec3 position : POSITION;\\nattribute vec2 texcoord : TEXCOORD_0;\\n\\n#ifdef VERTEX_ANIMATION\\nattribute vec3 prevPosition;\\nuniform float percent : 1.0;\\n#endif\\n\\nvarying vec4 v_ViewPosition;\\nvarying vec2 v_Texcoord;\\n\\nvoid main(){\\n\\n#ifdef VERTEX_ANIMATION\\n vec3 pos = mix(prevPosition, position, percent);\\n#else\\n vec3 pos = position;\\n#endif\\n\\n v_ViewPosition = worldViewProjection * vec4(pos, 1.0);\\n gl_Position = v_ViewPosition;\\n\\n v_Texcoord = texcoord;\\n\\n}\\n@end\\n\\n\\n\\n@export ecgl.sm.depth.fragment\\n\\n@import clay.sm.depth.fragment\\n\\n@end\");var Wa=vi.prototype.addToScene,ja=vi.prototype.removeFromScene;vi.prototype.addToScene=function(e){if(Wa.call(this,e),this.__zr){var t=this.__zr;e.traverse((function(e){e.__zr=t,e.addAnimatorsToZr&&e.addAnimatorsToZr(t)}))}},vi.prototype.removeFromScene=function(e){ja.call(this,e),e.traverse((function(e){var t=e.__zr;e.__zr=null,t&&e.removeAnimatorsFromZr&&e.removeAnimatorsFromZr(t)}))},le.prototype.setTextureImage=function(e,t,r,i){if(this.shader){var n,a=r.getZr(),o=this;return o.autoUpdateTextureStatus=!1,o.disableTexture(e),!(s=t)||\"none\"===s||(n=Xa.loadTexture(t,r,i,(function(t){o.enableTexture(e),a&&a.refresh()})),o.set(e,n)),n}var s};var Xa={};Xa.Renderer=ut,Xa.Node=sr,Xa.Mesh=Sr,Xa.Shader=Xe,Xa.Material=le,Xa.Texture=wr,Xa.Texture2D=Dr,Xa.Geometry=Vr,Xa.SphereGeometry=En,Xa.PlaneGeometry=ki,Xa.CubeGeometry=Wi,Xa.AmbientLight=Cn,Xa.DirectionalLight=Dn,Xa.PointLight=Ln,Xa.SpotLight=Pn,Xa.PerspectiveCamera=Ei,Xa.OrthographicCamera=un,Xa.Vector2=_e,Xa.Vector3=vt,Xa.Vector4=Rn,Xa.Quaternion=qt,Xa.Matrix2=Gn,Xa.Matrix2d=Hn,Xa.Matrix3=jn,Xa.Matrix4=Ht,Xa.Plane=$r,Xa.Ray=Mt,Xa.BoundingBox=ir,Xa.Frustum=si;var qa=null;function Za(e){return Math.pow(2,Math.round(Math.log(e)/Math.LN2))}function Ya(e){if((e.wrapS===wr.REPEAT||e.wrapT===wr.REPEAT)&&e.image){var t=Za(e.width),r=Za(e.height);if(t!==e.width||r!==e.height){var i=document.createElement(\"canvas\");i.width=t,i.height=r,i.getContext(\"2d\").drawImage(e.image,0,0,t,r),e.image=i}}}Xa.loadTexture=function(e,t,r,i){\"function\"==typeof r&&(i=r,r={}),r=r||{};for(var n=Object.keys(r).sort(),a=\"\",o=0;o3?t[3]=e[3]:t[3]=1,t):((t=i.color.parse(e||\"#000\",t)||[0,0,0,0])[0]/=255,t[1]/=255,t[2]/=255,t)},Xa.directionFromAlphaBeta=function(e,t){var r=e/180*Math.PI+Math.PI/2,i=-t/180*Math.PI+Math.PI/2,n=[],a=Math.sin(r);return n[0]=a*Math.cos(i),n[1]=-Math.cos(r),n[2]=a*Math.sin(i),n},Xa.getShadowResolution=function(e){var t=1024;switch(e){case\"low\":t=512;break;case\"medium\":break;case\"high\":t=2048;break;case\"ultra\":t=4096}return t},Xa.COMMON_SHADERS=[\"lambert\",\"color\",\"realistic\",\"hatching\",\"shadow\"],Xa.createShader=function(e){\"ecgl.shadow\"===e&&(e=\"ecgl.displayShadow\");var t=Xe.source(e+\".vertex\"),r=Xe.source(e+\".fragment\");t||console.error(\"Vertex shader of '%s' not exits\",e),r||console.error(\"Fragment shader of '%s' not exits\",e);var i=new Xe(t,r);return i.name=e,i},Xa.createMaterial=function(e,t){t instanceof Array||(t=[t]);var r=Xa.createShader(e),i=new le({shader:r});return t.forEach((function(e){\"string\"==typeof e&&i.define(e)})),i},Xa.setMaterialFromModel=function(e,t,r,i){t.autoUpdateTextureStatus=!1;var n=r.getModel(e+\"Material\"),a=n.get(\"detailTexture\"),o=Mn(n.get(\"textureTiling\"),1),s=Mn(n.get(\"textureOffset\"),0);\"number\"==typeof o&&(o=[o,o]),\"number\"==typeof s&&(s=[s,s]);var l=o[0]>1||o[1]>1?Xa.Texture.REPEAT:Xa.Texture.CLAMP_TO_EDGE,h={anisotropic:8,wrapS:l,wrapT:l};if(\"realistic\"===e){var u=n.get(\"roughness\"),c=n.get(\"metalness\");null!=c?isNaN(c)&&(t.setTextureImage(\"metalnessMap\",c,i,h),c=Mn(n.get(\"metalnessAdjust\"),.5)):c=0,null!=u?isNaN(u)&&(t.setTextureImage(\"roughnessMap\",u,i,h),u=Mn(n.get(\"roughnessAdjust\"),.5)):u=.5;var d=n.get(\"normalTexture\");t.setTextureImage(\"detailMap\",a,i,h),t.setTextureImage(\"normalMap\",d,i,h),t.set({roughness:u,metalness:c,detailUvRepeat:o,detailUvOffset:s})}else if(\"lambert\"===e)t.setTextureImage(\"detailMap\",a,i,h),t.set({detailUvRepeat:o,detailUvOffset:s});else if(\"color\"===e)t.setTextureImage(\"detailMap\",a,i,h),t.set({detailUvRepeat:o,detailUvOffset:s});else if(\"hatching\"===e){var f=n.get(\"hatchingTextures\")||[];f.length;for(var p=0;p<6;p++)t.setTextureImage(\"hatch\"+(p+1),f[p],i,{anisotropic:8,wrapS:Xa.Texture.REPEAT,wrapT:Xa.Texture.REPEAT});t.set({detailUvRepeat:o,detailUvOffset:s})}},Xa.updateVertexAnimation=function(e,t,r,i){var n=i.get(\"animation\"),a=i.get(\"animationDurationUpdate\"),o=i.get(\"animationEasingUpdate\"),s=r.shadowDepthMaterial;if(n&&t&&a>0&&t.geometry.vertexCount===r.geometry.vertexCount){r.material.define(\"vertex\",\"VERTEX_ANIMATION\"),r.ignorePreZ=!0,s&&s.define(\"vertex\",\"VERTEX_ANIMATION\");for(var l=0;l=0&&this._viewsToDispose.splice(t,1),this.views.push(e),e.layer=this;var r=this.zr;e.scene.traverse((function(e){e.__zr=r,e.addAnimatorsToZr&&e.addAnimatorsToZr(r)}))}},Ja.prototype.removeView=function(e){if(e.layer===this){var t=this.views.indexOf(e);t>=0&&(this.views.splice(t,1),e.scene.traverse($a,this),e.layer=null,this._viewsToDispose.push(e))}},Ja.prototype.removeViewsAll=function(){this.views.forEach((function(e){e.scene.traverse($a,this),e.layer=null,this._viewsToDispose.push(e)}),this),this.views.length=0},Ja.prototype.resize=function(e,t){this.renderer.resize(e,t)},Ja.prototype.clear=function(){var e=this.renderer.gl,t=this._backgroundColor||[0,0,0,0];e.clearColor(t[0],t[1],t[2],t[3]),e.depthMask(!0),e.colorMask(!0,!0,!0,!0),e.clear(e.DEPTH_BUFFER_BIT|e.COLOR_BUFFER_BIT)},Ja.prototype.clearDepth=function(){var e=this.renderer.gl;e.clear(e.DEPTH_BUFFER_BIT)},Ja.prototype.clearColor=function(){var e=this.renderer.gl;e.clearColor(0,0,0,0),e.clear(e.COLOR_BUFFER_BIT)},Ja.prototype.needsRefresh=function(){this.zr.refresh()},Ja.prototype.refresh=function(e){this._backgroundColor=e?Ka.parseColor(e):[0,0,0,0],this.renderer.clearColor=this._backgroundColor;for(var t=0;t20)){e=e.event;var i=this.pickObject(e.offsetX,e.offsetY);i&&(this._dispatchEvent(e.type,e,i),this._dispatchDataEvent(e.type,e,i));var n=this._clickToSetFocusPoint(e);n&&n.view.setDOFFocusOnPoint(n.distance)&&this.zr.refresh()}}},Ja.prototype._clickToSetFocusPoint=function(e){for(var t=this.renderer,r=t.viewport,i=this.views.length-1;i>=0;i--){var n=this.views[i];if(n.hasDOF()&&n.containPoint(e.offsetX,e.offsetY)){this._picking.scene=n.scene,this._picking.camera=n.camera,t.viewport=n.viewport;var a=this._picking.pick(e.offsetX,e.offsetY,!0);if(a)return a.view=n,a}}t.viewport=r},Ja.prototype.onglobalout=function(e){var t=this._hovered;t&&this._dispatchEvent(\"mouseout\",e,{target:t.target})},Ja.prototype.pickObject=function(e,t){for(var r=[],i=this.renderer,n=i.viewport,a=0;a=0&&(c.dataIndex=this._lastDataIndex,c.seriesIndex=this._lastSeriesIndex,this.zr.handler.dispatchToElement(u,\"mouseout\",t)),l=!0):null!=s&&s!==this._lastEventData&&(null!=this._lastEventData&&(c.eventData=this._lastEventData,this.zr.handler.dispatchToElement(u,\"mouseout\",t)),l=!0),this._lastEventData=s,this._lastDataIndex=a,this._lastSeriesIndex=o),c.eventData=s,c.dataIndex=a,c.seriesIndex=o,(null!=s||parseInt(a,10)>=0&&parseInt(o,10)>=0)&&(this.zr.handler.dispatchToElement(u,e,t),l&&this.zr.handler.dispatchToElement(u,\"mouseover\",t))},Ja.prototype._dispatchToView=function(e,t){for(var r=0;re&&o=0&&(function(e){so(e,\"itemStyle\"),so(e,\"lineStyle\"),so(e,\"areaStyle\"),so(e,\"label\")}(t),\"mapbox\"===t.coordinateSystem&&(t.coordinateSystem=\"mapbox3D\",e.mapbox3D=e.mapbox))})),lo(e.xAxis3D),lo(e.yAxis3D),lo(e.zAxis3D),lo(e.grid3D),so(e.geo3D)}));const uo={defaultOption:{viewControl:{projection:\"perspective\",autoRotate:!1,autoRotateDirection:\"cw\",autoRotateSpeed:10,autoRotateAfterStill:3,damping:.8,rotateSensitivity:1,zoomSensitivity:1,panSensitivity:1,panMouseButton:\"middle\",rotateMouseButton:\"left\",distance:150,minDistance:40,maxDistance:400,orthographicSize:150,maxOrthographicSize:400,minOrthographicSize:20,center:[0,0,0],alpha:0,beta:0,minAlpha:-90,maxAlpha:90}},setView:function(e){e=e||{},this.option.viewControl=this.option.viewControl||{},null!=e.alpha&&(this.option.viewControl.alpha=e.alpha),null!=e.beta&&(this.option.viewControl.beta=e.beta),null!=e.distance&&(this.option.viewControl.distance=e.distance),null!=e.center&&(this.option.viewControl.center=e.center)}},co={defaultOption:{postEffect:{enable:!1,bloom:{enable:!0,intensity:.1},depthOfField:{enable:!1,focalRange:20,focalDistance:50,blurRadius:10,fstop:2.8,quality:\"medium\"},screenSpaceAmbientOcclusion:{enable:!1,radius:2,quality:\"medium\",intensity:1},screenSpaceReflection:{enable:!1,quality:\"medium\",maxRoughness:.8},colorCorrection:{enable:!0,exposure:0,brightness:0,contrast:1,saturation:1,lookupTexture:\"\"},edge:{enable:!1},FXAA:{enable:!1}},temporalSuperSampling:{enable:\"auto\"}}},fo={defaultOption:{light:{main:{shadow:!1,shadowQuality:\"high\",color:\"#fff\",intensity:1,alpha:0,beta:0},ambient:{color:\"#fff\",intensity:.2},ambientCubemap:{texture:null,exposure:1,diffuseIntensity:.5,specularIntensity:.5}}}};var po=i.ComponentModel.extend({type:\"grid3D\",dependencies:[\"xAxis3D\",\"yAxis3D\",\"zAxis3D\"],defaultOption:{show:!0,zlevel:-10,left:0,top:0,width:\"100%\",height:\"100%\",environment:\"auto\",boxWidth:100,boxHeight:100,boxDepth:100,axisPointer:{show:!0,lineStyle:{color:\"rgba(0, 0, 0, 0.8)\",width:1},label:{show:!0,formatter:null,margin:8,textStyle:{fontSize:14,color:\"#fff\",backgroundColor:\"rgba(0,0,0,0.5)\",padding:3,borderRadius:3}}},axisLine:{show:!0,lineStyle:{color:\"#333\",width:2,type:\"solid\"}},axisTick:{show:!0,inside:!1,length:3,lineStyle:{width:1}},axisLabel:{show:!0,inside:!1,rotate:0,margin:8,textStyle:{fontSize:12}},splitLine:{show:!0,lineStyle:{color:[\"#ccc\"],width:1,type:\"solid\"}},splitArea:{show:!1,areaStyle:{color:[\"rgba(250,250,250,0.3)\",\"rgba(200,200,200,0.3)\"]}},light:{main:{alpha:30,beta:40},ambient:{intensity:.4}},viewControl:{alpha:20,beta:40,autoRotate:!1,distance:200,minDistance:40,maxDistance:400}}});i.util.merge(po.prototype,uo),i.util.merge(po.prototype,co),i.util.merge(po.prototype,fo);const mo=po;function go(e,t){switch(e){case\"center\":case\"middle\":e=\"50%\";break;case\"left\":case\"top\":e=\"0%\";break;case\"right\":case\"bottom\":e=\"100%\"}return\"string\"==typeof e?(r=e,r.replace(/^\\s+|\\s+$/g,\"\")).match(/%$/)?parseFloat(e)/100*t:parseFloat(e):null==e?NaN:+e;var r}function _o(){var e=\"__ec_inner_\"+vo++;return function(t){return t[e]||(t[e]={})}}var vo=Math.round(9*Math.random()),yo={};function xo(e,t,r,i,n){var a={};return function(e,t,r,i,n){r=r||yo;var a,o=t.ecModel,s=o&&o.option.textStyle,l=function(e){for(var t;e&&e!==e.ecModel;){var r=(e.option||yo).rich;if(r){t=t||{};for(var i=ba(r),n=0;n0&&this._notFirst?this.animateTo({alpha:h,beta:u,center:c,distance:a,orthographicSize:o,easing:l.animationEasingUpdate,duration:l.animationDurationUpdate}):(this.setDistance(a),this.setAlpha(h),this.setBeta(u),this.setCenter(c),this.setOrthographicSize(o)),this._notFirst=!0,this._validateProperties()},_validateProperties:function(){},animateTo:function(e){var t=this.zr,r=this,i={},n={};return null!=e.distance&&(i.distance=this.getDistance(),n.distance=e.distance),null!=e.orthographicSize&&(i.orthographicSize=this.getOrthographicSize(),n.orthographicSize=e.orthographicSize),null!=e.alpha&&(i.alpha=this.getAlpha(),n.alpha=e.alpha),null!=e.beta&&(i.beta=this.getBeta(),n.beta=e.beta),null!=e.center&&(i.center=this.getCenter(),n.center=e.center),this._addAnimator(t.animation.animate(i).when(e.duration||1e3,n).during((function(){null!=i.alpha&&r.setAlpha(i.alpha),null!=i.beta&&r.setBeta(i.beta),null!=i.distance&&r.setDistance(i.distance),null!=i.center&&r.setCenter(i.center),null!=i.orthographicSize&&r.setOrthographicSize(i.orthographicSize),r._needsUpdate=!0}))).start(e.easing||\"linear\")},stopAllAnimation:function(){for(var e=0;e0},_update:function(e){if(this._rotating){var t=(\"cw\"===this.autoRotateDirection?1:-1)*this.autoRotateSpeed/180*Math.PI;this._phi-=t*e/1e3,this._needsUpdate=!0}else this._rotateVelocity.len()>0&&(this._needsUpdate=!0);(Math.abs(this._zoomSpeed)>.1||this._panVelocity.len()>0)&&(this._needsUpdate=!0),this._needsUpdate&&(e=Math.min(e,50),this._updateDistanceOrSize(e),this._updatePan(e),this._updateRotate(e),this._updateTransform(),this.getCamera().update(),this.zr&&this.zr.refresh(),this.trigger(\"update\"),this._needsUpdate=!1)},_updateRotate:function(e){var t=this._rotateVelocity;this._phi=t.y*e/20+this._phi,this._theta=t.x*e/20+this._theta,this.setAlpha(this.getAlpha()),this.setBeta(this.getBeta()),this._vectorDamping(t,Math.pow(this.damping,e/16))},_updateDistanceOrSize:function(e){\"perspective\"===this._projection?this._setDistance(this._distance+this._zoomSpeed*e/20):this._setOrthoSize(this._orthoSize+this._zoomSpeed*e/20),this._zoomSpeed*=Math.pow(this.damping,e/16)},_setDistance:function(e){this._distance=Math.max(Math.min(e,this.maxDistance),this.minDistance)},_setOrthoSize:function(e){this._orthoSize=Math.max(Math.min(e,this.maxOrthographicSize),this.minOrthographicSize);var t=this.getCamera(),r=this._orthoSize,i=r/this.viewGL.viewport.height*this.viewGL.viewport.width;t.left=-i/2,t.right=i/2,t.top=r/2,t.bottom=-r/2},_updatePan:function(e){var t=this._panVelocity,r=this._distance,i=this.getCamera(),n=i.worldTransform.y,a=i.worldTransform.x;this._center.scaleAndAdd(a,-t.x*r/200).scaleAndAdd(n,-t.y*r/200),this._vectorDamping(t,0)},_updateTransform:function(){var e=this.getCamera(),t=new vt,r=this._theta+Math.PI/2,i=this._phi+Math.PI/2,n=Math.sin(r);t.x=n*Math.cos(i),t.y=-Math.cos(r),t.z=n*Math.sin(i),e.position.copy(this._center).scaleAndAdd(t,this._distance),e.rotation.identity().rotateY(-this._phi).rotateX(-this._theta)},_startCountingStill:function(){clearTimeout(this._stillTimeout);var e=this.autoRotateAfterStill,t=this;!isNaN(e)&&e>0&&(this._stillTimeout=setTimeout((function(){t._rotating=!0}),1e3*e))},_vectorDamping:function(e,t){var r=e.len();(r*=t)<1e-4&&(r=0),e.normalize().scale(r)},_decomposeTransform:function(){if(this.getCamera()){this.getCamera().updateWorldTransform();var e=this.getCamera().worldTransform.z,t=Math.asin(e.y),r=Math.atan2(e.x,e.z);this._theta=t,this._phi=-r,this.setBeta(this.getBeta()),this.setAlpha(this.getAlpha()),this.getCamera().aspect?this._setDistance(this.getCamera().position.dist(this._center)):this._setOrthoSize(this.getCamera().top-this.getCamera().bottom)}},_mouseDownHandler:function(e){if(!e.target&&!this._isAnimating()){var t=e.offsetX,r=e.offsetY;this.viewGL&&!this.viewGL.containPoint(t,r)||(this.zr.on(\"mousemove\",this._mouseMoveHandler),this.zr.on(\"mouseup\",this._mouseUpHandler),e.event.targetTouches?1===e.event.targetTouches.length&&(this._mode=\"rotate\"):e.event.button===Ao[this.rotateMouseButton]?this._mode=\"rotate\":e.event.button===Ao[this.panMouseButton]?this._mode=\"pan\":this._mode=\"\",this._rotateVelocity.set(0,0),this._rotating=!1,this.autoRotate&&this._startCountingStill(),this._mouseX=e.offsetX,this._mouseY=e.offsetY)}},_mouseMoveHandler:function(e){if(!(e.target&&e.target.__isGLToZRProxy||this._isAnimating())){var t=Eo(this.panSensitivity),r=Eo(this.rotateSensitivity);\"rotate\"===this._mode?(this._rotateVelocity.y=(e.offsetX-this._mouseX)/this.zr.getHeight()*2*r[0],this._rotateVelocity.x=(e.offsetY-this._mouseY)/this.zr.getWidth()*2*r[1]):\"pan\"===this._mode&&(this._panVelocity.x=(e.offsetX-this._mouseX)/this.zr.getWidth()*t[0]*400,this._panVelocity.y=(-e.offsetY+this._mouseY)/this.zr.getHeight()*t[1]*400),this._mouseX=e.offsetX,this._mouseY=e.offsetY,e.event.preventDefault()}},_mouseWheelHandler:function(e){if(!this._isAnimating()){var t=e.event.wheelDelta||-e.event.detail;this._zoomHandler(e,t)}},_pinchHandler:function(e){this._isAnimating()||(this._zoomHandler(e,e.pinchScale>1?1:-1),this._mode=\"\")},_zoomHandler:function(e,t){if(0!==t){var r,i=e.offsetX,n=e.offsetY;this.viewGL&&!this.viewGL.containPoint(i,n)||(r=\"perspective\"===this._projection?Math.max(Math.max(Math.min(this._distance-this.minDistance,this.maxDistance-this._distance))/20,.5):Math.max(Math.max(Math.min(this._orthoSize-this.minOrthographicSize,this.maxOrthographicSize-this._orthoSize))/20,.5),this._zoomSpeed=(t>0?-1:1)*r*this.zoomSensitivity,this._rotating=!1,this.autoRotate&&\"rotate\"===this._mode&&this._startCountingStill(),e.event.preventDefault())}},_mouseUpHandler:function(){this.zr.off(\"mousemove\",this._mouseMoveHandler),this.zr.off(\"mouseup\",this._mouseUpHandler)},_isRightMouseButtonUsed:function(){return\"right\"===this.rotateMouseButton||\"right\"===this.panMouseButton},_contextMenuHandler:function(e){this._isRightMouseButtonUsed()&&e.preventDefault()},_addAnimator:function(e){var t=this._animators;return t.push(e),e.done((function(){var r=t.indexOf(e);r>=0&&t.splice(r,1)})),e}});Object.defineProperty(Co.prototype,\"autoRotate\",{get:function(e){return this._autoRotate},set:function(e){this._autoRotate=e,this._rotating=e}});const Do=Co,Lo={convertToDynamicArray:function(e){e&&this.resetOffset();var t=this.attributes;for(var r in t)e||!t[r].value?t[r].value=[]:t[r].value=Array.prototype.slice.call(t[r].value);e||!this.indices?this.indices=[]:this.indices=Array.prototype.slice.call(this.indices)},convertToTypedArray:function(){var e=this.attributes;for(var t in e)e[t].value&&e[t].value.length>0?e[t].value=new Float32Array(e[t].value):e[t].value=null;this.indices&&this.indices.length>0&&(this.indices=this.vertexCount>65535?new Uint32Array(this.indices):new Uint16Array(this.indices)),this.dirty()}},Po={vec2:pe,vec3:Qe,vec4:Et,mat2:Fn,mat2d:kn,mat3:Dt,mat4:Ye,quat:Rt};var Oo=Po.vec3,No=[[0,0],[1,1]],Io=Vr.extend((function(){return{segmentScale:1,dynamic:!0,useNativeLine:!0,attributes:{position:new Vr.Attribute(\"position\",\"float\",3,\"POSITION\"),positionPrev:new Vr.Attribute(\"positionPrev\",\"float\",3),positionNext:new Vr.Attribute(\"positionNext\",\"float\",3),prevPositionPrev:new Vr.Attribute(\"prevPositionPrev\",\"float\",3),prevPosition:new Vr.Attribute(\"prevPosition\",\"float\",3),prevPositionNext:new Vr.Attribute(\"prevPositionNext\",\"float\",3),offset:new Vr.Attribute(\"offset\",\"float\",1),color:new Vr.Attribute(\"color\",\"float\",4,\"COLOR\")}}}),{resetOffset:function(){this._vertexOffset=0,this._triangleOffset=0,this._itemVertexOffsets=[]},setVertexCount:function(e){var t=this.attributes;this.vertexCount!==e&&(t.position.init(e),t.color.init(e),this.useNativeLine||(t.positionPrev.init(e),t.positionNext.init(e),t.offset.init(e)),e>65535?this.indices instanceof Uint16Array&&(this.indices=new Uint32Array(this.indices)):this.indices instanceof Uint32Array&&(this.indices=new Uint16Array(this.indices)))},setTriangleCount:function(e){this.triangleCount!==e&&(this.indices=0===e?null:this.vertexCount>65535?new Uint32Array(3*e):new Uint16Array(3*e))},_getCubicCurveApproxStep:function(e,t,r,i){return 1/(Oo.dist(e,t)+Oo.dist(r,t)+Oo.dist(i,r)+1)*this.segmentScale},getCubicCurveVertexCount:function(e,t,r,i){var n=this._getCubicCurveApproxStep(e,t,r,i),a=Math.ceil(1/n);return this.useNativeLine?2*a:2*a+2},getCubicCurveTriangleCount:function(e,t,r,i){var n=this._getCubicCurveApproxStep(e,t,r,i),a=Math.ceil(1/n);return this.useNativeLine?0:2*a},getLineVertexCount:function(){return this.getPolylineVertexCount(No)},getLineTriangleCount:function(){return this.getPolylineTriangleCount(No)},getPolylineVertexCount:function(e){var t;return t=\"number\"==typeof e?e:\"number\"!=typeof e[0]?e.length:e.length/3,this.useNativeLine?2*(t-1):2*(t-1)+2},getPolylineTriangleCount:function(e){var t;return t=\"number\"==typeof e?e:\"number\"!=typeof e[0]?e.length:e.length/3,this.useNativeLine?0:2*Math.max(t-1,0)},addCubicCurve:function(e,t,r,i,n,a){null==a&&(a=1);var o=e[0],s=e[1],l=e[2],h=t[0],u=t[1],c=t[2],d=r[0],f=r[1],p=r[2],m=i[0],g=i[1],_=i[2],v=this._getCubicCurveApproxStep(e,t,r,i),y=v*v,x=y*v,b=3*v,w=3*y,T=6*y,S=6*x,M=o-2*h+d,A=s-2*u+f,E=l-2*c+p,C=3*(h-d)-o+m,D=3*(u-f)-s+g,L=3*(c-p)-l+_,P=o,O=s,N=l,I=(h-o)*b+M*w+C*x,R=(u-s)*b+A*w+D*x,B=(c-l)*b+E*w+L*x,F=M*T+C*S,z=A*T+D*S,G=E*T+L*S,U=C*S,k=D*S,V=L*S,H=0,W=0,j=Math.ceil(1/v),X=new Float32Array(3*(j+1)),q=(X=[],0);for(W=0;W1&&(P=I>0?Math.min(P,m):Math.max(P,m),O=R>0?Math.min(O,g):Math.max(O,g),N=B>0?Math.min(N,_):Math.max(N,_));return this.addPolyline(X,n,a)},addLine:function(e,t,r,i){return this.addPolyline([e,t],r,i)},addPolyline:function(e,t,r,i,n){if(e.length){var a=\"number\"!=typeof e[0];if(null==n&&(n=a?e.length:e.length/3),!(n<2)){null==i&&(i=0),null==r&&(r=1),this._itemVertexOffsets.push(this._vertexOffset);var o,s,l=(a=\"number\"!=typeof e[0])?\"number\"!=typeof t[0]:t.length/4===n,h=this.attributes.position,u=this.attributes.positionPrev,c=this.attributes.positionNext,d=this.attributes.color,f=this.attributes.offset,p=this.indices,m=this._vertexOffset;r=Math.max(r,.01);for(var g=i;g1&&(h.copy(m,m-1),d.copy(m,m-1),m++):(g0&&(c.set(m-2,o),c.set(m-1,o)),h.set(m,o),h.set(m+1,o),d.set(m,s),d.set(m+1,s),f.set(m,r/2),f.set(m+1,-r/2),m+=2),this.useNativeLine)d.set(m,s),h.set(m,o),m++;else if(g>0){var y=3*this._triangleOffset;(p=this.indices)[y]=m-4,p[y+1]=m-3,p[y+2]=m-2,p[y+3]=m-3,p[y+4]=m-1,p[y+5]=m-2,this._triangleOffset+=2}}if(!this.useNativeLine){var x=this._vertexOffset,b=this._vertexOffset+2*n;u.copy(x,x+2),u.copy(x+1,x+3),c.copy(b-1,b-3),c.copy(b-2,b-4)}return this._vertexOffset=m,this._vertexOffset}}},setItemColor:function(e,t){for(var r=this._itemVertexOffsets[e],i=eo&&(n=this._x=0,a+=this._rowHeight+l,this._y=a,this._rowHeight=0),this._x+=t+l,this._rowHeight=Math.max(this._rowHeight,r),a+r+l>s)return null;e.x+=this.offsetX*this.dpr+n,e.y+=this.offsetY*this.dpr+a,this._zr.add(e);var h=[this.offsetX/this.width,this.offsetY/this.height];return[[n/o+h[0],a/s+h[1]],[(n+t)/o+h[0],(a+r)/s+h[1]]]},_fitElement:function(e,t,r){var i=e.getBoundingRect(),n=t/i.width,a=r/i.height;e.x=-i.x*n,e.y=-i.y*a,e.scaleX=n,e.scaleY=a,e.update()}},Fo.prototype={clear:function(){for(var e=0;e=e)){var n=(r+this._nodeWidth)*this._dpr,a=(i+this._nodeHeight)*this._dpr;try{this._zr.resize({width:n,height:a})}catch(e){this._canvas.width=n,this._canvas.height=a}var o=new Bo(this._zr,r,i,this._nodeWidth,this._nodeHeight,this._gap,this._dpr);return this._textureAtlasNodes.push(o),o}},add:function(e,t,r){if(this._coords[e.id])return this._coords[e.id];var i=this._getCurrentNode().add(e,t,r);if(!i){var n=this._expand();if(!n)return;i=n.add(e,t,r)}return this._coords[e.id]=i,i},getCoordsScale:function(){var e=this._dpr;return[this._nodeWidth/this._canvas.width*e,this._nodeHeight/this._canvas.height*e]},getCoords:function(e){return this._coords[e]},dispose:function(){this._zr.dispose()}};const zo=Fo;function Go(){}Go.prototype={constructor:Go,setScene:function(e){this._scene=e,this._skybox&&this._skybox.attachScene(this._scene)},initLight:function(e){this._lightRoot=e,this.mainLight=new Ka.DirectionalLight({shadowBias:.005}),this.ambientLight=new Ka.AmbientLight,e.add(this.mainLight),e.add(this.ambientLight)},dispose:function(){this._lightRoot&&(this._lightRoot.remove(this.mainLight),this._lightRoot.remove(this.ambientLight))},updateLight:function(e){var t=this.mainLight,r=this.ambientLight,i=e.getModel(\"light\"),n=i.getModel(\"main\"),a=i.getModel(\"ambient\");t.intensity=n.get(\"intensity\"),r.intensity=a.get(\"intensity\"),t.color=Ka.parseColor(n.get(\"color\")).slice(0,3),r.color=Ka.parseColor(a.get(\"color\")).slice(0,3);var o=n.get(\"alpha\")||0,s=n.get(\"beta\")||0;t.position.setArray(Ka.directionFromAlphaBeta(o,s)),t.lookAt(Ka.Vector3.ZERO),t.castShadow=n.get(\"shadow\"),t.shadowResolution=Ka.getShadowResolution(n.get(\"shadowQuality\"))},updateAmbientCubemap:function(e,t,r){var i=t.getModel(\"light.ambientCubemap\"),n=i.get(\"texture\");if(n){this._cubemapLightsCache=this._cubemapLightsCache||{};var a=this._cubemapLightsCache[n];if(!a){var o=this;a=this._cubemapLightsCache[n]=Ka.createAmbientCubemap(i.option,e,r,(function(){o._isSkyboxFromAmbientCubemap&&o._skybox.setEnvironmentMap(a.specular.cubemap),r.getZr().refresh()}))}this._lightRoot.add(a.diffuse),this._lightRoot.add(a.specular),this._currentCubemapLights=a}else this._currentCubemapLights&&(this._lightRoot.remove(this._currentCubemapLights.diffuse),this._lightRoot.remove(this._currentCubemapLights.specular),this._currentCubemapLights=null)},updateSkybox:function(e,t,r){var n=t.get(\"environment\"),a=this,o=(a._skybox=a._skybox||new ji,a._skybox);if(n&&\"none\"!==n)if(\"auto\"===n)if(this._isSkyboxFromAmbientCubemap=!0,this._currentCubemapLights){var s=this._currentCubemapLights.specular.cubemap;o.setEnvironmentMap(s),this._scene&&o.attachScene(this._scene),o.material.set(\"lod\",3)}else this._skybox&&this._skybox.detachScene();else if(\"object\"==typeof n&&n.colorStops||\"string\"==typeof n&&i.color.parse(n)){this._isSkyboxFromAmbientCubemap=!1;var l=new Ka.Texture2D({anisotropic:8,flipY:!1});o.setEnvironmentMap(l);var h=l.image=document.createElement(\"canvas\");h.width=h.height=16;var u=h.getContext(\"2d\"),c=new i.graphic.Rect({shape:{x:0,y:0,width:16,height:16},style:{fill:n}});i.innerDrawElementOnCanvas(u,c),o.attachScene(this._scene)}else this._isSkyboxFromAmbientCubemap=!1,l=Ka.loadTexture(n,r,{anisotropic:8,flipY:!1}),o.setEnvironmentMap(l),o.attachScene(this._scene);else this._skybox&&this._skybox.detachScene(this._scene),this._skybox=null;var d=t.coordinateSystem;if(this._skybox)if(!d||!d.viewGL||\"auto\"===n||n.match&&n.match(/.hdr$/))this._skybox.material.undefine(\"fragment\",\"SRGB_DECODE\");else{var f=d.viewGL.isLinearSpace()?\"define\":\"undefine\";this._skybox.material[f](\"fragment\",\"SRGB_DECODE\")}}};const Uo=Go;var ko=Po.vec3,Vo=Vr.extend((function(){return{segmentScale:1,useNativeLine:!0,attributes:{position:new Vr.Attribute(\"position\",\"float\",3,\"POSITION\"),normal:new Vr.Attribute(\"normal\",\"float\",3,\"NORMAL\"),color:new Vr.Attribute(\"color\",\"float\",4,\"COLOR\")}}}),{resetOffset:function(){this._vertexOffset=0,this._faceOffset=0},setQuadCount:function(e){var t=this.attributes,r=this.getQuadVertexCount()*e,i=this.getQuadTriangleCount()*e;this.vertexCount!==r&&(t.position.init(r),t.normal.init(r),t.color.init(r)),this.triangleCount!==i&&(this.indices=r>65535?new Uint32Array(3*i):new Uint16Array(3*i))},getQuadVertexCount:function(){return 4},getQuadTriangleCount:function(){return 2},addQuad:function(){var e=ko.create(),t=ko.create(),r=ko.create(),i=[0,3,1,3,2,1];return function(n,a){var o=this.attributes.position,s=this.attributes.normal,l=this.attributes.color;ko.sub(e,n[1],n[0]),ko.sub(t,n[2],n[1]),ko.cross(r,e,t),ko.normalize(r,r);for(var h=0;h<4;h++)o.set(this._vertexOffset+h,n[h]),l.set(this._vertexOffset+h,a),s.set(this._vertexOffset+h,r);var u=3*this._faceOffset;for(h=0;h<6;h++)this.indices[u+h]=i[h]+this._vertexOffset;this._vertexOffset+=4,this._faceOffset+=2}}()});i.util.defaults(Vo.prototype,Lo);const Ho=Vo;var Wo=Mn,jo={x:0,y:2,z:1};function Xo(e,t,r){this.rootNode=new Ka.Node;var i=new Ka.Mesh({geometry:new Ro({useNativeLine:!1}),material:t,castShadow:!1,ignorePicking:!0,$ignorePicking:!0,renderOrder:1}),n=new Ka.Mesh({geometry:new Ho,material:r,castShadow:!1,culling:!1,ignorePicking:!0,$ignorePicking:!0,renderOrder:0});this.rootNode.add(n),this.rootNode.add(i),this.faceInfo=e,this.plane=new Ka.Plane,this.linesMesh=i,this.quadsMesh=n}Xo.prototype.update=function(e,t,r){var i=e.coordinateSystem,n=[i.getAxis(this.faceInfo[0]),i.getAxis(this.faceInfo[1])],a=this.linesMesh.geometry,o=this.quadsMesh.geometry;a.convertToDynamicArray(!0),o.convertToDynamicArray(!0),this._updateSplitLines(a,n,e,r),this._udpateSplitAreas(o,n,e,r),a.convertToTypedArray(),o.convertToTypedArray();var s=i.getAxis(this.faceInfo[2]);!function(e,t,r,i){var n=[0,0,0],a=i<0?r.getExtentMin():r.getExtentMax();n[jo[r.dim]]=a,e.position.setArray(n),e.rotation.identity(),t.distance=-Math.abs(a),t.normal.set(0,0,0),\"x\"===r.dim?(e.rotation.rotateY(i*Math.PI/2),t.normal.x=-i):\"z\"===r.dim?(e.rotation.rotateX(-i*Math.PI/2),t.normal.y=-i):(i>0&&e.rotation.rotateY(Math.PI),t.normal.z=-i)}(this.rootNode,this.plane,s,this.faceInfo[3])},Xo.prototype._updateSplitLines=function(e,t,r,n){var a=n.getDevicePixelRatio();t.forEach((function(n,o){var s=n.model,l=t[1-o].getExtent();if(!n.scale.isBlank()){var h=s.getModel(\"splitLine\",r.getModel(\"splitLine\"));if(h.get(\"show\")){var u=h.getModel(\"lineStyle\"),c=u.get(\"color\"),d=Wo(u.get(\"opacity\"),1),f=Wo(u.get(\"width\"),1);c=i.util.isArray(c)?c:[c];for(var p=n.getTicksCoords({tickModel:h}),m=0,g=0;g65535?new Uint32Array(3*r):new Uint16Array(3*r))},setSpriteAlign:function(e,t,r,i,n){var a,o,s,l;switch(null==r&&(r=\"left\"),null==i&&(i=\"top\"),n=n||0,r){case\"left\":a=n,s=t[0]+n;break;case\"center\":case\"middle\":a=-t[0]/2,s=t[0]/2;break;case\"right\":a=-t[0]-n,s=-n}switch(i){case\"bottom\":o=n,l=t[1]+n;break;case\"middle\":o=-t[1]/2,l=t[1]/2;break;case\"top\":o=-t[1]-n,l=-n}var h=4*e,u=this.attributes.offset;u.set(h,[a,l]),u.set(h+1,[s,l]),u.set(h+2,[s,o]),u.set(h+3,[a,o])},addSprite:function(e,t,r,i,n,a){var o=this._vertexOffset;this.setSprite(this._vertexOffset/4,e,t,r,i,n,a);for(var s=0;s 0.0) {\\n currProj = clipNear(currProj, nextProj);\\n }\\n else if (prevProj.w > 0.0) {\\n currProj = clipNear(currProj, prevProj);\\n }\\n }\\n\\n vec2 prevScreen = (prevProj.xy / abs(prevProj.w) + 1.0) * 0.5 * viewport.zw;\\n vec2 currScreen = (currProj.xy / abs(currProj.w) + 1.0) * 0.5 * viewport.zw;\\n vec2 nextScreen = (nextProj.xy / abs(nextProj.w) + 1.0) * 0.5 * viewport.zw;\\n\\n vec2 dir;\\n float len = offset;\\n if (position == positionPrev) {\\n dir = normalize(nextScreen - currScreen);\\n }\\n else if (position == positionNext) {\\n dir = normalize(currScreen - prevScreen);\\n }\\n else {\\n vec2 dirA = normalize(currScreen - prevScreen);\\n vec2 dirB = normalize(nextScreen - currScreen);\\n\\n vec2 tanget = normalize(dirA + dirB);\\n\\n float miter = 1.0 / max(dot(tanget, dirA), 0.5);\\n len *= miter;\\n dir = tanget;\\n }\\n\\n dir = vec2(-dir.y, dir.x) * len;\\n currScreen += dir;\\n\\n currProj.xy = (currScreen / viewport.zw - 0.5) * 2.0 * abs(currProj.w);\\n@end\\n\\n\\n@export ecgl.meshLines3D.vertex\\n\\nattribute vec3 position: POSITION;\\nattribute vec3 positionPrev;\\nattribute vec3 positionNext;\\nattribute float offset;\\nattribute vec4 a_Color : COLOR;\\n\\n#ifdef VERTEX_ANIMATION\\nattribute vec3 prevPosition;\\nattribute vec3 prevPositionPrev;\\nattribute vec3 prevPositionNext;\\nuniform float percent : 1.0;\\n#endif\\n\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\nuniform vec4 viewport : VIEWPORT;\\nuniform float near : NEAR;\\n\\nvarying vec4 v_Color;\\n\\n@import ecgl.common.wireframe.vertexHeader\\n\\n@import ecgl.lines3D.clipNear\\n\\nvoid main()\\n{\\n @import ecgl.lines3D.expandLine\\n\\n gl_Position = currProj;\\n\\n v_Color = a_Color;\\n\\n @import ecgl.common.wireframe.vertexMain\\n}\\n@end\\n\\n\\n@export ecgl.meshLines3D.fragment\\n\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\n\\nvarying vec4 v_Color;\\n\\n@import ecgl.common.wireframe.fragmentHeader\\n\\n@import clay.util.srgb\\n\\nvoid main()\\n{\\n#ifdef SRGB_DECODE\\n gl_FragColor = sRGBToLinear(color * v_Color);\\n#else\\n gl_FragColor = color * v_Color;\\n#endif\\n\\n @import ecgl.common.wireframe.fragmentMain\\n}\\n\\n@end\";var ns=Mn;Ka.Shader.import(is);var as={x:0,y:2,z:1};const os=i.ComponentView.extend({type:\"grid3D\",__ecgl__:!0,init:function(e,t){var r=new Ka.Material({shader:Ka.createShader(\"ecgl.color\"),depthMask:!1,transparent:!0}),i=new Ka.Material({shader:Ka.createShader(\"ecgl.meshLines3D\"),depthMask:!1,transparent:!0});r.define(\"fragment\",\"DOUBLE_SIDED\"),r.define(\"both\",\"VERTEX_COLOR\"),this.groupGL=new Ka.Node,this._control=new Do({zr:t.getZr()}),this._control.init(),this._faces=[[\"y\",\"z\",\"x\",-1,\"left\"],[\"y\",\"z\",\"x\",1,\"right\"],[\"x\",\"y\",\"z\",-1,\"bottom\"],[\"x\",\"y\",\"z\",1,\"top\"],[\"x\",\"z\",\"y\",-1,\"far\"],[\"x\",\"z\",\"y\",1,\"near\"]].map((function(e){var t=new qo(e,i,r);return this.groupGL.add(t.rootNode),t}),this),this._axes=[\"x\",\"y\",\"z\"].map((function(e){var t=new rs(e,i);return this.groupGL.add(t.rootNode),t}),this);var n=t.getDevicePixelRatio();this._axisLabelSurface=new zo({width:256,height:256,devicePixelRatio:n}),this._axisLabelSurface.onupdate=function(){t.getZr().refresh()},this._axisPointerLineMesh=new Ka.Mesh({geometry:new Ro({useNativeLine:!1}),material:i,castShadow:!1,ignorePicking:!0,renderOrder:3}),this.groupGL.add(this._axisPointerLineMesh),this._axisPointerLabelsSurface=new zo({width:128,height:128,devicePixelRatio:n}),this._axisPointerLabelsMesh=new Qo({ignorePicking:!0,renderOrder:4,castShadow:!1}),this._axisPointerLabelsMesh.material.set(\"textureAtlas\",this._axisPointerLabelsSurface.getTexture()),this.groupGL.add(this._axisPointerLabelsMesh),this._lightRoot=new Ka.Node,this._sceneHelper=new Uo,this._sceneHelper.initLight(this._lightRoot)},render:function(e,t,r){this._model=e,this._api=r;var i=e.coordinateSystem;i.viewGL.add(this._lightRoot),e.get(\"show\")?i.viewGL.add(this.groupGL):i.viewGL.remove(this.groupGL);var n=this._control;n.setViewGL(i.viewGL);var a=e.getModel(\"viewControl\");n.setFromViewControlModel(a,0),this._axisLabelSurface.clear(),n.off(\"update\"),e.get(\"show\")&&(this._faces.forEach((function(i){i.update(e,t,r)}),this),this._axes.forEach((function(t){t.update(e,this._axisLabelSurface,r)}),this)),n.on(\"update\",this._onCameraChange.bind(this,e,r),this),this._sceneHelper.setScene(i.viewGL.scene),this._sceneHelper.updateLight(e),i.viewGL.setPostEffect(e.getModel(\"postEffect\"),r),i.viewGL.setTemporalSuperSampling(e.getModel(\"temporalSuperSampling\")),this._initMouseHandler(e)},afterRender:function(e,t,r,i){var n=i.renderer;this._sceneHelper.updateAmbientCubemap(n,e,r),this._sceneHelper.updateSkybox(n,e,r)},showAxisPointer:function(e,t,r,i){this._doShowAxisPointer(),this._updateAxisPointer(i.value)},hideAxisPointer:function(e,t,r,i){this._doHideAxisPointer()},_initMouseHandler:function(e){var t=e.coordinateSystem.viewGL;e.get(\"show\")&&e.get(\"axisPointer.show\")?t.on(\"mousemove\",this._updateAxisPointerOnMousePosition,this):t.off(\"mousemove\",this._updateAxisPointerOnMousePosition)},_updateAxisPointerOnMousePosition:function(e){if(!e.target){for(var t,r=this._model.coordinateSystem,i=r.viewGL,n=i.castRay(e.offsetX,e.offsetY,new Ka.Ray),a=0;ai[1]?0:1,o=this._faces[2*r+a],s=this._faces[2*r+1-a];o.rootNode.invisible=!0,s.rootNode.invisible=!1}},_updateAxisLinePosition:function(){var e=this._model.coordinateSystem,t=e.getAxis(\"x\"),r=e.getAxis(\"y\"),i=e.getAxis(\"z\"),n=i.getExtentMax(),a=i.getExtentMin(),o=t.getExtentMin(),s=t.getExtentMax(),l=r.getExtentMax(),h=r.getExtentMin(),u=this._axes[0].rootNode,c=this._axes[1].rootNode,d=this._axes[2].rootNode,f=this._faces,p=f[4].rootNode.invisible?h:l,m=f[2].rootNode.invisible?n:a,g=f[0].rootNode.invisible?o:s,_=f[2].rootNode.invisible?n:a,v=f[0].rootNode.invisible?s:o,y=f[4].rootNode.invisible?h:l;u.rotation.identity(),c.rotation.identity(),d.rotation.identity(),f[4].rootNode.invisible&&(this._axes[0].flipped=!0,u.rotation.rotateX(Math.PI)),f[0].rootNode.invisible&&(this._axes[1].flipped=!0,c.rotation.rotateZ(Math.PI)),f[4].rootNode.invisible&&(this._axes[2].flipped=!0,d.rotation.rotateY(Math.PI)),u.position.set(0,m,p),c.position.set(g,_,0),d.position.set(v,0,y),u.update(),c.update(),d.update(),this._updateAxisLabelAlign()},_updateAxisLabelAlign:function(){var e=this._control.getCamera(),t=[new Ka.Vector4,new Ka.Vector4],r=new Ka.Vector4;this.groupGL.getWorldPosition(r),r.w=1,r.transformMat4(e.viewMatrix).transformMat4(e.projectionMatrix),r.x/=r.w,r.y/=r.w,this._axes.forEach((function(i){for(var n=i.axisLineCoords,a=(i.labelsMesh.geometry,0);ar.y?\"bottom\":\"top\"):(s=\"middle\",o=u>r.x?\"left\":\"right\"),i.setSpriteAlign(o,s,this._api)}),this)},_doShowAxisPointer:function(){this._axisPointerLineMesh.invisible&&(this._axisPointerLineMesh.invisible=!1,this._axisPointerLabelsMesh.invisible=!1,this._api.getZr().refresh())},_doHideAxisPointer:function(){this._axisPointerLineMesh.invisible||(this._axisPointerLineMesh.invisible=!0,this._axisPointerLabelsMesh.invisible=!0,this._api.getZr().refresh())},_updateAxisPointer:function(e){var t=this._model.coordinateSystem,r=t.dataToPoint(e),i=this._axisPointerLineMesh.geometry,n=this._model.getModel(\"axisPointer\"),a=this._api.getDevicePixelRatio();function o(e){return Mn(e.model.get(\"axisPointer.show\"),n.get(\"show\"))}function s(e){var t=e.model.getModel(\"axisPointer\",n).getModel(\"lineStyle\"),r=Ka.parseColor(t.get(\"color\")),i=ns(t.get(\"width\"),1),a=ns(t.get(\"opacity\"),1);return r[3]*=a,{color:r,lineWidth:i}}i.convertToDynamicArray(!0);for(var l=0;lp&&(p=y,mp&&(p=x,_=r.x&&e<=r.x+r.width&&t>=r.y&&t<=r.y+r.height},e.prototype.clone=function(){return new e(this.x,this.y,this.width,this.height)},e.prototype.copy=function(t){e.copy(this,t)},e.prototype.plain=function(){return{x:this.x,y:this.y,width:this.width,height:this.height}},e.prototype.isFinite=function(){return isFinite(this.x)&&isFinite(this.y)&&isFinite(this.width)&&isFinite(this.height)},e.prototype.isZero=function(){return 0===this.width||0===this.height},e.create=function(t){return new e(t.x,t.y,t.width,t.height)},e.copy=function(e,t){e.x=t.x,e.y=t.y,e.width=t.width,e.height=t.height},e.applyTransform=function(t,r,i){if(i){if(i[1]<1e-5&&i[1]>-1e-5&&i[2]<1e-5&&i[2]>-1e-5){var n=i[0],a=i[3],o=i[4],s=i[5];return t.x=r.x*n+o,t.y=r.y*a+s,t.width=r.width*n,t.height=r.height*a,t.width<0&&(t.x+=t.width,t.width=-t.width),void(t.height<0&&(t.y+=t.height,t.height=-t.height))}gs.x=vs.x=r.x,gs.y=ys.y=r.y,_s.x=ys.x=r.x+r.width,_s.y=vs.y=r.y+r.height,gs.transform(i),ys.transform(i),_s.transform(i),vs.transform(i),t.x=ps(gs.x,_s.x,vs.x,ys.x),t.y=ps(gs.y,_s.y,vs.y,ys.y);var l=ms(gs.x,_s.x,vs.x,ys.x),h=ms(gs.y,_s.y,vs.y,ys.y);t.width=l-t.x,t.height=h-t.y}else t!==r&&e.copy(t,r)},e}();function Ts(e,t,r,i,n){var a=0,o=0;null==i&&(i=1/0),null==n&&(n=1/0);var s=0;t.eachChild((function(l,h){var u,c,d=l.getBoundingRect(),f=t.childAt(h+1),p=f&&f.getBoundingRect();if(\"horizontal\"===e){var m=d.width+(p?-p.x+d.x:0);(u=a+m)>i||l.newline?(a=0,u=m,o+=s+r,s=d.height):s=Math.max(s,d.height)}else{var g=d.height+(p?-p.y+d.y:0);(c=o+g)>n||l.newline?(a+=s+r,o=0,c=g,s=d.width):s=Math.max(s,d.width)}l.newline||(l.x=a,l.y=o,l.markRedraw(),\"horizontal\"===e?a=u+r:o=c+r)}))}function Ss(e,t,r){r=function(e){if(\"number\"==typeof e)return[e,e,e,e];var t=e.length;return 2===t?[e[0],e[1],e[0],e[1]]:3===t?[e[0],e[1],e[2],e[1]]:e}(r||0);var i=t.width,n=t.height,a=go(e.left,i),o=go(e.top,n),s=go(e.right,i),l=go(e.bottom,n),h=go(e.width,i),u=go(e.height,n),c=r[2]+r[0],d=r[1]+r[3],f=e.aspect;switch(isNaN(h)&&(h=i-s-d-a),isNaN(u)&&(u=n-l-c-o),null!=f&&(isNaN(h)&&isNaN(u)&&(f>i/n?h=.8*i:u=.8*n),isNaN(h)&&(h=f*u),isNaN(u)&&(u=h/f)),isNaN(a)&&(a=i-s-h-d),isNaN(o)&&(o=n-l-u-c),e.left||e.right){case\"center\":a=i/2-h/2-r[3];break;case\"right\":a=i-h-d}switch(e.top||e.bottom){case\"middle\":case\"center\":o=n/2-u/2-r[0];break;case\"bottom\":o=n-u-c}a=a||0,o=o||0,isNaN(h)&&(h=i-d-a-(s||0)),isNaN(u)&&(u=n-c-o-(l||0));var p=new ws(a+r[3],o+r[0],h,u);return p.margin=r,p}wa(Ts,\"vertical\"),wa(Ts,\"horizontal\");var Ms=function(){this._pool={},this._allocatedTextures=[]};Ms.prototype={constructor:Ms,get:function(e){var t=Cs(e);this._pool.hasOwnProperty(t)||(this._pool[t]=[]);var r=this._pool[t];if(!r.length){var i=new Dr(e);return this._allocatedTextures.push(i),i}return r.pop()},put:function(e){var t=Cs(e);this._pool.hasOwnProperty(t)||(this._pool[t]=[]),this._pool[t].push(e)},clear:function(e){for(var t=0;t 0.0) {\\n if (texture2D(alphaMap, v_Texcoord).a <= alphaCutoff) {\\n discard;\\n }\\n }\\n#ifdef USE_VSM\\n depth = depth * 0.5 + 0.5;\\n float moment1 = depth;\\n float moment2 = depth * depth;\\n #ifdef SUPPORT_STANDARD_DERIVATIVES\\n float dx = dFdx(depth);\\n float dy = dFdy(depth);\\n moment2 += 0.25*(dx*dx+dy*dy);\\n #endif\\n gl_FragColor = vec4(moment1, moment2, 0.0, 1.0);\\n#else\\n #ifdef SUPPORT_STANDARD_DERIVATIVES\\n float dx = dFdx(depth);\\n float dy = dFdy(depth);\\n depth += sqrt(dx*dx + dy*dy) * slopeScale + bias;\\n #else\\n depth += bias;\\n #endif\\n gl_FragColor = encodeFloat(depth * 0.5 + 0.5);\\n#endif\\n}\\n@end\\n@export clay.sm.debug_depth\\nuniform sampler2D depthMap;\\nvarying vec2 v_Texcoord;\\n@import clay.util.decode_float\\nvoid main() {\\n vec4 tex = texture2D(depthMap, v_Texcoord);\\n#ifdef USE_VSM\\n gl_FragColor = vec4(tex.rgb, 1.0);\\n#else\\n float depth = decodeFloat(tex);\\n gl_FragColor = vec4(depth, depth, depth, 1.0);\\n#endif\\n}\\n@end\\n@export clay.sm.distance.vertex\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\nuniform mat4 world : WORLD;\\nattribute vec3 position : POSITION;\\n@import clay.chunk.skinning_header\\nvarying vec3 v_WorldPosition;\\nvoid main (){\\n vec4 P = vec4(position, 1.0);\\n#ifdef SKINNING\\n @import clay.chunk.skin_matrix\\n P = skinMatrixWS * P;\\n#endif\\n#ifdef INSTANCING\\n @import clay.chunk.instancing_matrix\\n P = instanceMat * P;\\n#endif\\n gl_Position = worldViewProjection * P;\\n v_WorldPosition = (world * P).xyz;\\n}\\n@end\\n@export clay.sm.distance.fragment\\nuniform vec3 lightPosition;\\nuniform float range : 100;\\nvarying vec3 v_WorldPosition;\\n@import clay.util.encode_float\\nvoid main(){\\n float dist = distance(lightPosition, v_WorldPosition);\\n#ifdef USE_VSM\\n gl_FragColor = vec4(dist, dist * dist, 0.0, 0.0);\\n#else\\n dist = dist / range;\\n gl_FragColor = encodeFloat(dist);\\n#endif\\n}\\n@end\\n@export clay.plugin.shadow_map_common\\n@import clay.util.decode_float\\nfloat tapShadowMap(sampler2D map, vec2 uv, float z){\\n vec4 tex = texture2D(map, uv);\\n return step(z, decodeFloat(tex) * 2.0 - 1.0);\\n}\\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize, vec2 scale) {\\n float shadowContrib = tapShadowMap(map, uv, z);\\n vec2 offset = vec2(1.0 / textureSize) * scale;\\n#ifdef PCF_KERNEL_SIZE\\n for (int _idx_ = 0; _idx_ < PCF_KERNEL_SIZE; _idx_++) {{\\n shadowContrib += tapShadowMap(map, uv + offset * pcfKernel[_idx_], z);\\n }}\\n return shadowContrib / float(PCF_KERNEL_SIZE + 1);\\n#else\\n shadowContrib += tapShadowMap(map, uv+vec2(offset.x, 0.0), z);\\n shadowContrib += tapShadowMap(map, uv+vec2(offset.x, offset.y), z);\\n shadowContrib += tapShadowMap(map, uv+vec2(-offset.x, offset.y), z);\\n shadowContrib += tapShadowMap(map, uv+vec2(0.0, offset.y), z);\\n shadowContrib += tapShadowMap(map, uv+vec2(-offset.x, 0.0), z);\\n shadowContrib += tapShadowMap(map, uv+vec2(-offset.x, -offset.y), z);\\n shadowContrib += tapShadowMap(map, uv+vec2(offset.x, -offset.y), z);\\n shadowContrib += tapShadowMap(map, uv+vec2(0.0, -offset.y), z);\\n return shadowContrib / 9.0;\\n#endif\\n}\\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize) {\\n return pcf(map, uv, z, textureSize, vec2(1.0));\\n}\\nfloat chebyshevUpperBound(vec2 moments, float z){\\n float p = 0.0;\\n z = z * 0.5 + 0.5;\\n if (z <= moments.x) {\\n p = 1.0;\\n }\\n float variance = moments.y - moments.x * moments.x;\\n variance = max(variance, 0.0000001);\\n float mD = moments.x - z;\\n float pMax = variance / (variance + mD * mD);\\n pMax = clamp((pMax-0.4)/(1.0-0.4), 0.0, 1.0);\\n return max(p, pMax);\\n}\\nfloat computeShadowContrib(\\n sampler2D map, mat4 lightVPM, vec3 position, float textureSize, vec2 scale, vec2 offset\\n) {\\n vec4 posInLightSpace = lightVPM * vec4(position, 1.0);\\n posInLightSpace.xyz /= posInLightSpace.w;\\n float z = posInLightSpace.z;\\n if(all(greaterThan(posInLightSpace.xyz, vec3(-0.99, -0.99, -1.0))) &&\\n all(lessThan(posInLightSpace.xyz, vec3(0.99, 0.99, 1.0)))){\\n vec2 uv = (posInLightSpace.xy+1.0) / 2.0;\\n #ifdef USE_VSM\\n vec2 moments = texture2D(map, uv * scale + offset).xy;\\n return chebyshevUpperBound(moments, z);\\n #else\\n return pcf(map, uv * scale + offset, z, textureSize, scale);\\n #endif\\n }\\n return 1.0;\\n}\\nfloat computeShadowContrib(sampler2D map, mat4 lightVPM, vec3 position, float textureSize) {\\n return computeShadowContrib(map, lightVPM, position, textureSize, vec2(1.0), vec2(0.0));\\n}\\nfloat computeShadowContribOmni(samplerCube map, vec3 direction, float range)\\n{\\n float dist = length(direction);\\n vec4 shadowTex = textureCube(map, direction);\\n#ifdef USE_VSM\\n vec2 moments = shadowTex.xy;\\n float variance = moments.y - moments.x * moments.x;\\n float mD = moments.x - dist;\\n float p = variance / (variance + mD * mD);\\n if(moments.x + 0.001 < dist){\\n return clamp(p, 0.0, 1.0);\\n }else{\\n return 1.0;\\n }\\n#else\\n return step(dist, (decodeFloat(shadowTex) + 0.0002) * range);\\n#endif\\n}\\n@end\\n@export clay.plugin.compute_shadow_map\\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT) || defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT) || defined(POINT_LIGHT_SHADOWMAP_COUNT)\\n#ifdef SPOT_LIGHT_SHADOWMAP_COUNT\\nuniform sampler2D spotLightShadowMaps[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\\nuniform mat4 spotLightMatrices[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\\nuniform float spotLightShadowMapSizes[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\\n#endif\\n#ifdef DIRECTIONAL_LIGHT_SHADOWMAP_COUNT\\n#if defined(SHADOW_CASCADE)\\nuniform sampler2D directionalLightShadowMaps[1]:unconfigurable;\\nuniform mat4 directionalLightMatrices[SHADOW_CASCADE]:unconfigurable;\\nuniform float directionalLightShadowMapSizes[1]:unconfigurable;\\nuniform float shadowCascadeClipsNear[SHADOW_CASCADE]:unconfigurable;\\nuniform float shadowCascadeClipsFar[SHADOW_CASCADE]:unconfigurable;\\n#else\\nuniform sampler2D directionalLightShadowMaps[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\\nuniform mat4 directionalLightMatrices[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\\nuniform float directionalLightShadowMapSizes[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\\n#endif\\n#endif\\n#ifdef POINT_LIGHT_SHADOWMAP_COUNT\\nuniform samplerCube pointLightShadowMaps[POINT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\\n#endif\\nuniform bool shadowEnabled : true;\\n#ifdef PCF_KERNEL_SIZE\\nuniform vec2 pcfKernel[PCF_KERNEL_SIZE];\\n#endif\\n@import clay.plugin.shadow_map_common\\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\\nvoid computeShadowOfSpotLights(vec3 position, inout float shadowContribs[SPOT_LIGHT_COUNT] ) {\\n float shadowContrib;\\n for(int _idx_ = 0; _idx_ < SPOT_LIGHT_SHADOWMAP_COUNT; _idx_++) {{\\n shadowContrib = computeShadowContrib(\\n spotLightShadowMaps[_idx_], spotLightMatrices[_idx_], position,\\n spotLightShadowMapSizes[_idx_]\\n );\\n shadowContribs[_idx_] = shadowContrib;\\n }}\\n for(int _idx_ = SPOT_LIGHT_SHADOWMAP_COUNT; _idx_ < SPOT_LIGHT_COUNT; _idx_++){{\\n shadowContribs[_idx_] = 1.0;\\n }}\\n}\\n#endif\\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\\n#ifdef SHADOW_CASCADE\\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_COUNT]){\\n float depth = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far)\\n / (gl_DepthRange.far - gl_DepthRange.near);\\n float shadowContrib;\\n shadowContribs[0] = 1.0;\\n for (int _idx_ = 0; _idx_ < SHADOW_CASCADE; _idx_++) {{\\n if (\\n depth >= shadowCascadeClipsNear[_idx_] &&\\n depth <= shadowCascadeClipsFar[_idx_]\\n ) {\\n shadowContrib = computeShadowContrib(\\n directionalLightShadowMaps[0], directionalLightMatrices[_idx_], position,\\n directionalLightShadowMapSizes[0],\\n vec2(1.0 / float(SHADOW_CASCADE), 1.0),\\n vec2(float(_idx_) / float(SHADOW_CASCADE), 0.0)\\n );\\n shadowContribs[0] = shadowContrib;\\n }\\n }}\\n for(int _idx_ = DIRECTIONAL_LIGHT_SHADOWMAP_COUNT; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++) {{\\n shadowContribs[_idx_] = 1.0;\\n }}\\n}\\n#else\\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_COUNT]){\\n float shadowContrib;\\n for(int _idx_ = 0; _idx_ < DIRECTIONAL_LIGHT_SHADOWMAP_COUNT; _idx_++) {{\\n shadowContrib = computeShadowContrib(\\n directionalLightShadowMaps[_idx_], directionalLightMatrices[_idx_], position,\\n directionalLightShadowMapSizes[_idx_]\\n );\\n shadowContribs[_idx_] = shadowContrib;\\n }}\\n for(int _idx_ = DIRECTIONAL_LIGHT_SHADOWMAP_COUNT; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++) {{\\n shadowContribs[_idx_] = 1.0;\\n }}\\n}\\n#endif\\n#endif\\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\\nvoid computeShadowOfPointLights(vec3 position, inout float shadowContribs[POINT_LIGHT_COUNT] ){\\n vec3 lightPosition;\\n vec3 direction;\\n for(int _idx_ = 0; _idx_ < POINT_LIGHT_SHADOWMAP_COUNT; _idx_++) {{\\n lightPosition = pointLightPosition[_idx_];\\n direction = position - lightPosition;\\n shadowContribs[_idx_] = computeShadowContribOmni(pointLightShadowMaps[_idx_], direction, pointLightRange[_idx_]);\\n }}\\n for(int _idx_ = POINT_LIGHT_SHADOWMAP_COUNT; _idx_ < POINT_LIGHT_COUNT; _idx_++) {{\\n shadowContribs[_idx_] = 1.0;\\n }}\\n}\\n#endif\\n#endif\\n@end\");var Ns,Is,Rs,Bs,Fs,zs,Gs,Us=m.extend((function(){return{softShadow:Us.PCF,shadowBlur:1,lightFrustumBias:\"auto\",kernelPCF:new Float32Array([1,0,1,1,-1,1,0,1,-1,0,-1,-1,1,-1,0,-1]),precision:\"highp\",_lastRenderNotCastShadow:!1,_frameBuffer:new zi,_textures:{},_shadowMapNumber:{POINT_LIGHT:0,DIRECTIONAL_LIGHT:0,SPOT_LIGHT:0},_depthMaterials:{},_distanceMaterials:{},_receivers:[],_lightsCastShadow:[],_lightCameras:{},_lightMaterials:{},_texturePool:new Ds}}),(function(){this._gaussianPassH=new pn({fragment:Xe.source(\"clay.compositor.gaussian_blur\")}),this._gaussianPassV=new pn({fragment:Xe.source(\"clay.compositor.gaussian_blur\")}),this._gaussianPassH.setUniform(\"blurSize\",this.shadowBlur),this._gaussianPassH.setUniform(\"blurDir\",0),this._gaussianPassV.setUniform(\"blurSize\",this.shadowBlur),this._gaussianPassV.setUniform(\"blurDir\",1),this._outputDepthPass=new pn({fragment:Xe.source(\"clay.sm.debug_depth\")})}),{render:function(e,t,r,i){r||(r=t.getMainCamera()),this.trigger(\"beforerender\",this,e,t,r),this._renderShadowPass(e,t,r,i),this.trigger(\"afterrender\",this,e,t,r)},renderDebug:function(e,t){e.saveClear();var r=e.viewport,i=0,n=t||r.width/4,a=n;for(var o in this.softShadow===Us.VSM?this._outputDepthPass.material.define(\"fragment\",\"USE_VSM\"):this._outputDepthPass.material.undefine(\"fragment\",\"USE_VSM\"),this._textures){var s=this._textures[o];e.setViewport(i,0,n*s.width/s.height,a),this._outputDepthPass.setUniform(\"depthMap\",s),this._outputDepthPass.render(e),i+=n*s.width/s.height}e.setViewport(r),e.restoreClear()},_updateReceivers:function(e,t){if(t.receiveShadow?(this._receivers.push(t),t.material.set(\"shadowEnabled\",1),t.material.set(\"pcfKernel\",this.kernelPCF)):t.material.set(\"shadowEnabled\",0),this.softShadow===Us.VSM)t.material.define(\"fragment\",\"USE_VSM\"),t.material.undefine(\"fragment\",\"PCF_KERNEL_SIZE\");else{t.material.undefine(\"fragment\",\"USE_VSM\");var r=this.kernelPCF;r&&r.length?t.material.define(\"fragment\",\"PCF_KERNEL_SIZE\",r.length/2):t.material.undefine(\"fragment\",\"PCF_KERNEL_SIZE\")}},_update:function(e,t){var r=this;t.traverse((function(t){t.isRenderable()&&r._updateReceivers(e,t)}));for(var i=0;i4){console.warn(\"Support at most 4 cascade\");continue}p.shadowCascade>1&&(o=p),this.renderDirectionalLightShadow(e,t,r,p,c,u,h)}else\"SPOT_LIGHT\"===p.type?this.renderSpotLightShadow(e,t,p,l,s):\"POINT_LIGHT\"===p.type&&this.renderPointLightShadow(e,t,p,d);this._shadowMapNumber[p.type]++}for(var m in this._shadowMapNumber){var g=this._shadowMapNumber[m],_=m+\"_SHADOWMAP_COUNT\";for(f=0;f0?v.define(\"fragment\",_,g):v.isDefined(\"fragment\",_)&&v.undefine(\"fragment\",_))}for(f=0;f0){var x=h.map(S);if(y.directionalLightShadowMaps={value:h,type:\"tv\"},y.directionalLightMatrices={value:u,type:\"m4v\"},y.directionalLightShadowMapSizes={value:x,type:\"1fv\"},o){var b=c.slice(),w=c.slice();b.pop(),w.shift(),b.reverse(),w.reverse(),u.reverse(),y.shadowCascadeClipsNear={value:b,type:\"1fv\"},y.shadowCascadeClipsFar={value:w,type:\"1fv\"}}}if(s.length>0){var T=s.map(S);(y=t.shadowUniforms).spotLightShadowMaps={value:s,type:\"tv\"},y.spotLightMatrices={value:l,type:\"m4v\"},y.spotLightShadowMapSizes={value:T,type:\"1fv\"}}d.length>0&&(y.pointLightShadowMaps={value:d,type:\"tv\"})}function S(e){return e.height}},renderDirectionalLightShadow:(Ns=new si,Is=new Ht,Rs=new ir,Bs=new Ht,Fs=new Ht,zs=new Ht,Gs=new Ht,function(e,t,r,i,n,a,o){var s=this._getDepthMaterial(i),l={getMaterial:function(e){return e.shadowDepthMaterial||s},isMaterialChanged:Os,getUniform:Ps,ifRender:function(e){return e.castShadow},sortCompare:ut.opaqueSortCompare};if(!t.viewBoundingBoxLastFrame.isFinite()){var h=t.getBoundingBox();t.viewBoundingBoxLastFrame.copy(h).applyTransform(r.viewMatrix)}var u=Math.min(-t.viewBoundingBoxLastFrame.min.z,r.far),c=Math.max(-t.viewBoundingBoxLastFrame.max.z,r.near),d=this._getDirectionalLightCamera(i,t,r),f=zs.array;Gs.copy(d.projectionMatrix),Ye.invert(Fs.array,d.worldTransform.array),Ye.multiply(Fs.array,Fs.array,r.worldTransform.array),Ye.multiply(f,Gs.array,Fs.array);for(var p=[],m=r instanceof Ei,g=(r.near+r.far)/(r.near-r.far),_=2*r.near*r.far/(r.near-r.far),v=0;v<=i.shadowCascade;v++){var y=c*Math.pow(u/c,v/i.shadowCascade),x=c+(u-c)*v/i.shadowCascade,b=y*i.cascadeSplitLogFactor+x*(1-i.cascadeSplitLogFactor);p.push(b),n.push(-(-b*g+_)/-b)}var w=this._getTexture(i,i.shadowCascade);o.push(w);var T=e.viewport,S=e.gl;for(this._frameBuffer.attach(w),this._frameBuffer.bind(e),S.clear(S.COLOR_BUFFER_BIT|S.DEPTH_BUFFER_BIT),v=0;vd?s>f?p[n>0?\"px\":\"nx\"]=!0:p[o>0?\"pz\":\"nz\"]=!0:d>f?p[a>0?\"py\":\"ny\"]=!0:p[o>0?\"pz\":\"nz\"]=!0}for(r=0;r0&&(this.outputs[e].keepLastFrame?(this._prevOutputTextures[e]&&this._compositor.releaseTexture(this._prevOutputTextures[e]),this._prevOutputTextures[e]=this._outputTextures[e]):this._compositor.releaseTexture(this._outputTextures[e]))}}),Hs=m.extend((function(){return{nodes:[]}}),{dirty:function(){this._dirty=!0},addNode:function(e){this.nodes.indexOf(e)>=0||(this.nodes.push(e),this._dirty=!0)},removeNode:function(e){\"string\"==typeof e&&(e=this.getNodeByName(e));var t=this.nodes.indexOf(e);t>=0&&(this.nodes.splice(t,1),this._dirty=!0)},getNodeByName:function(e){for(var t=0;t=r.COLOR_ATTACHMENT0&&u<=r.COLOR_ATTACHMENT0+8&&h.push(u);l.drawBuffersEXT(h)}e.saveClear(),e.clearBit=16640,t=e.render(this.scene,this.camera,!this.autoUpdateScene,this.preZ),e.restoreClear(),i.unbind(e)}else t=e.render(this.scene,this.camera,!this.autoUpdateScene,this.preZ);this.trigger(\"afterrender\",t),this._rendering=!1,this._rendered=!0}}),Xs=Vs.extend((function(){return{texture:null,outputs:{color:{}}}}),(function(){}),{getOutput:function(e,t){return this.texture},beforeFrame:function(){},afterFrame:function(){}}),qs=Vs.extend((function(){return{name:\"\",inputs:{},outputs:null,shader:\"\",inputLinks:{},outputLinks:{},pass:null,_prevOutputTextures:{},_outputTextures:{},_outputReferences:{},_rendering:!1,_rendered:!1,_compositor:null}}),(function(){var e=new pn({fragment:this.shader});this.pass=e}),{render:function(e,t){this.trigger(\"beforerender\",e),this._rendering=!0;var r=e.gl;for(var i in this.inputLinks){var n=(c=this.inputLinks[i]).node.getOutput(e,c.pin);this.pass.setUniform(i,n)}if(this.outputs){this.pass.outputs={};var a={};for(var o in this.outputs){var s=this.updateParameter(o,e);isNaN(s.width)&&this.updateParameter(o,e);var l=this.outputs[o],h=this._compositor.allocateTexture(s);this._outputTextures[o]=h,\"string\"==typeof(u=l.attachment||r.COLOR_ATTACHMENT0)&&(u=r[u]),a[u]=h}for(var u in this._compositor.getFrameBuffer().bind(e),a)this._compositor.getFrameBuffer().attach(a[u],u);this.pass.render(e),this._compositor.getFrameBuffer().updateMipmap(e)}else this.pass.outputs=null,this._compositor.getFrameBuffer().unbind(e),this.pass.render(e,t);for(var i in this.inputLinks){var c;(c=this.inputLinks[i]).node.removeReference(c.pin)}this._rendering=!1,this._rendered=!0,this.trigger(\"afterrender\",e)},updateParameter:function(e,t){var r,i,n=this.outputs[e],a=n.parameters,o=n._parametersCopy;if(o||(o=n._parametersCopy={}),a)for(var s in a)\"width\"!==s&&\"height\"!==s&&(o[s]=a[s]);return r=\"function\"==typeof a.width?a.width.call(this,t):a.width,i=\"function\"==typeof a.height?a.height.call(this,t):a.height,r=Math.ceil(r),i=Math.ceil(i),o.width===r&&o.height===i||this._outputTextures[e]&&this._outputTextures[e].dispose(t),o.width=r,o.height=i,o},setParameter:function(e,t){this.pass.setUniform(e,t)},getParameter:function(e){return this.pass.getUniform(e)},setParameters:function(e){for(var t in e)this.setParameter(t,e[t])},define:function(e,t){this.pass.material.define(\"fragment\",e,t)},undefine:function(e){this.pass.material.undefine(\"fragment\",e)},removeReference:function(e){this._outputReferences[e]--,0===this._outputReferences[e]&&(this.outputs[e].keepLastFrame?(this._prevOutputTextures[e]&&this._compositor.releaseTexture(this._prevOutputTextures[e]),this._prevOutputTextures[e]=this._outputTextures[e]):this._compositor.releaseTexture(this._outputTextures[e]))},clear:function(){Vs.prototype.clear.call(this),this.pass.material.disableTexturesAll()}}),Zs=\"@export clay.compositor.kernel.gaussian_9\\nfloat gaussianKernel[9];\\ngaussianKernel[0] = 0.07;\\ngaussianKernel[1] = 0.09;\\ngaussianKernel[2] = 0.12;\\ngaussianKernel[3] = 0.14;\\ngaussianKernel[4] = 0.16;\\ngaussianKernel[5] = 0.14;\\ngaussianKernel[6] = 0.12;\\ngaussianKernel[7] = 0.09;\\ngaussianKernel[8] = 0.07;\\n@end\\n@export clay.compositor.kernel.gaussian_13\\nfloat gaussianKernel[13];\\ngaussianKernel[0] = 0.02;\\ngaussianKernel[1] = 0.03;\\ngaussianKernel[2] = 0.06;\\ngaussianKernel[3] = 0.08;\\ngaussianKernel[4] = 0.11;\\ngaussianKernel[5] = 0.13;\\ngaussianKernel[6] = 0.14;\\ngaussianKernel[7] = 0.13;\\ngaussianKernel[8] = 0.11;\\ngaussianKernel[9] = 0.08;\\ngaussianKernel[10] = 0.06;\\ngaussianKernel[11] = 0.03;\\ngaussianKernel[12] = 0.02;\\n@end\\n@export clay.compositor.gaussian_blur\\n#define SHADER_NAME gaussian_blur\\nuniform sampler2D texture;varying vec2 v_Texcoord;\\nuniform float blurSize : 2.0;\\nuniform vec2 textureSize : [512.0, 512.0];\\nuniform float blurDir : 0.0;\\n@import clay.util.rgbm\\n@import clay.util.clamp_sample\\nvoid main (void)\\n{\\n @import clay.compositor.kernel.gaussian_9\\n vec2 off = blurSize / textureSize;\\n off *= vec2(1.0 - blurDir, blurDir);\\n vec4 sum = vec4(0.0);\\n float weightAll = 0.0;\\n for (int i = 0; i < 9; i++) {\\n float w = gaussianKernel[i];\\n vec4 texel = decodeHDR(clampSample(texture, v_Texcoord + float(i - 4) * off));\\n sum += texel * w;\\n weightAll += w;\\n }\\n gl_FragColor = encodeHDR(sum / max(weightAll, 0.01));\\n}\\n@end\\n\",Ys=\"\\n@export clay.compositor.lut\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nuniform sampler2D lookup;\\nvoid main()\\n{\\n vec4 tex = texture2D(texture, v_Texcoord);\\n float blueColor = tex.b * 63.0;\\n vec2 quad1;\\n quad1.y = floor(floor(blueColor) / 8.0);\\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\\n vec2 quad2;\\n quad2.y = floor(ceil(blueColor) / 8.0);\\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\\n vec2 texPos1;\\n texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.r);\\n texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.g);\\n vec2 texPos2;\\n texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.r);\\n texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.g);\\n vec4 newColor1 = texture2D(lookup, texPos1);\\n vec4 newColor2 = texture2D(lookup, texPos2);\\n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\\n gl_FragColor = vec4(newColor.rgb, tex.w);\\n}\\n@end\",Ks=\"@export clay.compositor.output\\n#define OUTPUT_ALPHA\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\n@import clay.util.rgbm\\nvoid main()\\n{\\n vec4 tex = decodeHDR(texture2D(texture, v_Texcoord));\\n gl_FragColor.rgb = tex.rgb;\\n#ifdef OUTPUT_ALPHA\\n gl_FragColor.a = tex.a;\\n#else\\n gl_FragColor.a = 1.0;\\n#endif\\n gl_FragColor = encodeHDR(gl_FragColor);\\n#ifdef PREMULTIPLY_ALPHA\\n gl_FragColor.rgb *= gl_FragColor.a;\\n#endif\\n}\\n@end\",Qs=\"@export clay.compositor.bright\\nuniform sampler2D texture;\\nuniform float threshold : 1;\\nuniform float scale : 1.0;\\nuniform vec2 textureSize: [512, 512];\\nvarying vec2 v_Texcoord;\\nconst vec3 lumWeight = vec3(0.2125, 0.7154, 0.0721);\\n@import clay.util.rgbm\\nvec4 median(vec4 a, vec4 b, vec4 c)\\n{\\n return a + b + c - min(min(a, b), c) - max(max(a, b), c);\\n}\\nvoid main()\\n{\\n vec4 texel = decodeHDR(texture2D(texture, v_Texcoord));\\n#ifdef ANTI_FLICKER\\n vec3 d = 1.0 / textureSize.xyx * vec3(1.0, 1.0, 0.0);\\n vec4 s1 = decodeHDR(texture2D(texture, v_Texcoord - d.xz));\\n vec4 s2 = decodeHDR(texture2D(texture, v_Texcoord + d.xz));\\n vec4 s3 = decodeHDR(texture2D(texture, v_Texcoord - d.zy));\\n vec4 s4 = decodeHDR(texture2D(texture, v_Texcoord + d.zy));\\n texel = median(median(texel, s1, s2), s3, s4);\\n#endif\\n float lum = dot(texel.rgb , lumWeight);\\n vec4 color;\\n if (lum > threshold && texel.a > 0.0)\\n {\\n color = vec4(texel.rgb * scale, texel.a * scale);\\n }\\n else\\n {\\n color = vec4(0.0);\\n }\\n gl_FragColor = encodeHDR(color);\\n}\\n@end\\n\",Js=\"@export clay.compositor.downsample\\nuniform sampler2D texture;\\nuniform vec2 textureSize : [512, 512];\\nvarying vec2 v_Texcoord;\\n@import clay.util.rgbm\\nfloat brightness(vec3 c)\\n{\\n return max(max(c.r, c.g), c.b);\\n}\\n@import clay.util.clamp_sample\\nvoid main()\\n{\\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\\n#ifdef ANTI_FLICKER\\n vec3 s1 = decodeHDR(clampSample(texture, v_Texcoord + d.xy)).rgb;\\n vec3 s2 = decodeHDR(clampSample(texture, v_Texcoord + d.zy)).rgb;\\n vec3 s3 = decodeHDR(clampSample(texture, v_Texcoord + d.xw)).rgb;\\n vec3 s4 = decodeHDR(clampSample(texture, v_Texcoord + d.zw)).rgb;\\n float s1w = 1.0 / (brightness(s1) + 1.0);\\n float s2w = 1.0 / (brightness(s2) + 1.0);\\n float s3w = 1.0 / (brightness(s3) + 1.0);\\n float s4w = 1.0 / (brightness(s4) + 1.0);\\n float oneDivideSum = 1.0 / (s1w + s2w + s3w + s4w);\\n vec4 color = vec4(\\n (s1 * s1w + s2 * s2w + s3 * s3w + s4 * s4w) * oneDivideSum,\\n 1.0\\n );\\n#else\\n vec4 color = decodeHDR(clampSample(texture, v_Texcoord + d.xy));\\n color += decodeHDR(clampSample(texture, v_Texcoord + d.zy));\\n color += decodeHDR(clampSample(texture, v_Texcoord + d.xw));\\n color += decodeHDR(clampSample(texture, v_Texcoord + d.zw));\\n color *= 0.25;\\n#endif\\n gl_FragColor = encodeHDR(color);\\n}\\n@end\",$s=\"\\n@export clay.compositor.upsample\\n#define HIGH_QUALITY\\nuniform sampler2D texture;\\nuniform vec2 textureSize : [512, 512];\\nuniform float sampleScale: 0.5;\\nvarying vec2 v_Texcoord;\\n@import clay.util.rgbm\\n@import clay.util.clamp_sample\\nvoid main()\\n{\\n#ifdef HIGH_QUALITY\\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\\n vec4 s;\\n s = decodeHDR(clampSample(texture, v_Texcoord - d.xy));\\n s += decodeHDR(clampSample(texture, v_Texcoord - d.wy)) * 2.0;\\n s += decodeHDR(clampSample(texture, v_Texcoord - d.zy));\\n s += decodeHDR(clampSample(texture, v_Texcoord + d.zw)) * 2.0;\\n s += decodeHDR(clampSample(texture, v_Texcoord )) * 4.0;\\n s += decodeHDR(clampSample(texture, v_Texcoord + d.xw)) * 2.0;\\n s += decodeHDR(clampSample(texture, v_Texcoord + d.zy));\\n s += decodeHDR(clampSample(texture, v_Texcoord + d.wy)) * 2.0;\\n s += decodeHDR(clampSample(texture, v_Texcoord + d.xy));\\n gl_FragColor = encodeHDR(s / 16.0);\\n#else\\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\\n vec4 s;\\n s = decodeHDR(clampSample(texture, v_Texcoord + d.xy));\\n s += decodeHDR(clampSample(texture, v_Texcoord + d.zy));\\n s += decodeHDR(clampSample(texture, v_Texcoord + d.xw));\\n s += decodeHDR(clampSample(texture, v_Texcoord + d.zw));\\n gl_FragColor = encodeHDR(s / 4.0);\\n#endif\\n}\\n@end\",el=\"@export clay.compositor.hdr.composite\\n#define TONEMAPPING\\nuniform sampler2D texture;\\n#ifdef BLOOM_ENABLED\\nuniform sampler2D bloom;\\n#endif\\n#ifdef LENSFLARE_ENABLED\\nuniform sampler2D lensflare;\\nuniform sampler2D lensdirt;\\n#endif\\n#ifdef LUM_ENABLED\\nuniform sampler2D lum;\\n#endif\\n#ifdef LUT_ENABLED\\nuniform sampler2D lut;\\n#endif\\n#ifdef COLOR_CORRECTION\\nuniform float brightness : 0.0;\\nuniform float contrast : 1.0;\\nuniform float saturation : 1.0;\\n#endif\\n#ifdef VIGNETTE\\nuniform float vignetteDarkness: 1.0;\\nuniform float vignetteOffset: 1.0;\\n#endif\\nuniform float exposure : 1.0;\\nuniform float bloomIntensity : 0.25;\\nuniform float lensflareIntensity : 1;\\nvarying vec2 v_Texcoord;\\n@import clay.util.srgb\\nvec3 ACESToneMapping(vec3 color)\\n{\\n const float A = 2.51;\\n const float B = 0.03;\\n const float C = 2.43;\\n const float D = 0.59;\\n const float E = 0.14;\\n return (color * (A * color + B)) / (color * (C * color + D) + E);\\n}\\nfloat eyeAdaption(float fLum)\\n{\\n return mix(0.2, fLum, 0.5);\\n}\\n#ifdef LUT_ENABLED\\nvec3 lutTransform(vec3 color) {\\n float blueColor = color.b * 63.0;\\n vec2 quad1;\\n quad1.y = floor(floor(blueColor) / 8.0);\\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\\n vec2 quad2;\\n quad2.y = floor(ceil(blueColor) / 8.0);\\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\\n vec2 texPos1;\\n texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);\\n texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);\\n vec2 texPos2;\\n texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);\\n texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);\\n vec4 newColor1 = texture2D(lut, texPos1);\\n vec4 newColor2 = texture2D(lut, texPos2);\\n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\\n return newColor.rgb;\\n}\\n#endif\\n@import clay.util.rgbm\\nvoid main()\\n{\\n vec4 texel = vec4(0.0);\\n vec4 originalTexel = vec4(0.0);\\n#ifdef TEXTURE_ENABLED\\n texel = decodeHDR(texture2D(texture, v_Texcoord));\\n originalTexel = texel;\\n#endif\\n#ifdef BLOOM_ENABLED\\n vec4 bloomTexel = decodeHDR(texture2D(bloom, v_Texcoord));\\n texel.rgb += bloomTexel.rgb * bloomIntensity;\\n texel.a += bloomTexel.a * bloomIntensity;\\n#endif\\n#ifdef LENSFLARE_ENABLED\\n texel += decodeHDR(texture2D(lensflare, v_Texcoord)) * texture2D(lensdirt, v_Texcoord) * lensflareIntensity;\\n#endif\\n texel.a = min(texel.a, 1.0);\\n#ifdef LUM_ENABLED\\n float fLum = texture2D(lum, vec2(0.5, 0.5)).r;\\n float adaptedLumDest = 3.0 / (max(0.1, 1.0 + 10.0*eyeAdaption(fLum)));\\n float exposureBias = adaptedLumDest * exposure;\\n#else\\n float exposureBias = exposure;\\n#endif\\n#ifdef TONEMAPPING\\n texel.rgb *= exposureBias;\\n texel.rgb = ACESToneMapping(texel.rgb);\\n#endif\\n texel = linearTosRGB(texel);\\n#ifdef LUT_ENABLED\\n texel.rgb = lutTransform(clamp(texel.rgb,vec3(0.0),vec3(1.0)));\\n#endif\\n#ifdef COLOR_CORRECTION\\n texel.rgb = clamp(texel.rgb + vec3(brightness), 0.0, 1.0);\\n texel.rgb = clamp((texel.rgb - vec3(0.5))*contrast+vec3(0.5), 0.0, 1.0);\\n float lum = dot(texel.rgb, vec3(0.2125, 0.7154, 0.0721));\\n texel.rgb = mix(vec3(lum), texel.rgb, saturation);\\n#endif\\n#ifdef VIGNETTE\\n vec2 uv = (v_Texcoord - vec2(0.5)) * vec2(vignetteOffset);\\n texel.rgb = mix(texel.rgb, vec3(1.0 - vignetteDarkness), dot(uv, uv));\\n#endif\\n gl_FragColor = encodeHDR(texel);\\n#ifdef DEBUG\\n #if DEBUG == 1\\n gl_FragColor = encodeHDR(decodeHDR(texture2D(texture, v_Texcoord)));\\n #elif DEBUG == 2\\n gl_FragColor = encodeHDR(decodeHDR(texture2D(bloom, v_Texcoord)) * bloomIntensity);\\n #elif DEBUG == 3\\n gl_FragColor = encodeHDR(decodeHDR(texture2D(lensflare, v_Texcoord) * lensflareIntensity));\\n #endif\\n#endif\\n if (originalTexel.a <= 0.01 && gl_FragColor.a > 1e-5) {\\n gl_FragColor.a = dot(gl_FragColor.rgb, vec3(0.2125, 0.7154, 0.0721));\\n }\\n#ifdef PREMULTIPLY_ALPHA\\n gl_FragColor.rgb *= gl_FragColor.a;\\n#endif\\n}\\n@end\",tl=\"@export clay.compositor.blend\\n#define SHADER_NAME blend\\n#ifdef TEXTURE1_ENABLED\\nuniform sampler2D texture1;\\nuniform float weight1 : 1.0;\\n#endif\\n#ifdef TEXTURE2_ENABLED\\nuniform sampler2D texture2;\\nuniform float weight2 : 1.0;\\n#endif\\n#ifdef TEXTURE3_ENABLED\\nuniform sampler2D texture3;\\nuniform float weight3 : 1.0;\\n#endif\\n#ifdef TEXTURE4_ENABLED\\nuniform sampler2D texture4;\\nuniform float weight4 : 1.0;\\n#endif\\n#ifdef TEXTURE5_ENABLED\\nuniform sampler2D texture5;\\nuniform float weight5 : 1.0;\\n#endif\\n#ifdef TEXTURE6_ENABLED\\nuniform sampler2D texture6;\\nuniform float weight6 : 1.0;\\n#endif\\nvarying vec2 v_Texcoord;\\n@import clay.util.rgbm\\nvoid main()\\n{\\n vec4 tex = vec4(0.0);\\n#ifdef TEXTURE1_ENABLED\\n tex += decodeHDR(texture2D(texture1, v_Texcoord)) * weight1;\\n#endif\\n#ifdef TEXTURE2_ENABLED\\n tex += decodeHDR(texture2D(texture2, v_Texcoord)) * weight2;\\n#endif\\n#ifdef TEXTURE3_ENABLED\\n tex += decodeHDR(texture2D(texture3, v_Texcoord)) * weight3;\\n#endif\\n#ifdef TEXTURE4_ENABLED\\n tex += decodeHDR(texture2D(texture4, v_Texcoord)) * weight4;\\n#endif\\n#ifdef TEXTURE5_ENABLED\\n tex += decodeHDR(texture2D(texture5, v_Texcoord)) * weight5;\\n#endif\\n#ifdef TEXTURE6_ENABLED\\n tex += decodeHDR(texture2D(texture6, v_Texcoord)) * weight6;\\n#endif\\n gl_FragColor = encodeHDR(tex);\\n}\\n@end\",rl=\"@export clay.compositor.fxaa\\nuniform sampler2D texture;\\nuniform vec4 viewport : VIEWPORT;\\nvarying vec2 v_Texcoord;\\n#define FXAA_REDUCE_MIN (1.0/128.0)\\n#define FXAA_REDUCE_MUL (1.0/8.0)\\n#define FXAA_SPAN_MAX 8.0\\n@import clay.util.rgbm\\nvoid main()\\n{\\n vec2 resolution = 1.0 / viewport.zw;\\n vec3 rgbNW = decodeHDR( texture2D( texture, ( gl_FragCoord.xy + vec2( -1.0, -1.0 ) ) * resolution ) ).xyz;\\n vec3 rgbNE = decodeHDR( texture2D( texture, ( gl_FragCoord.xy + vec2( 1.0, -1.0 ) ) * resolution ) ).xyz;\\n vec3 rgbSW = decodeHDR( texture2D( texture, ( gl_FragCoord.xy + vec2( -1.0, 1.0 ) ) * resolution ) ).xyz;\\n vec3 rgbSE = decodeHDR( texture2D( texture, ( gl_FragCoord.xy + vec2( 1.0, 1.0 ) ) * resolution ) ).xyz;\\n vec4 rgbaM = decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution ) );\\n vec3 rgbM = rgbaM.xyz;\\n float opacity = rgbaM.w;\\n vec3 luma = vec3( 0.299, 0.587, 0.114 );\\n float lumaNW = dot( rgbNW, luma );\\n float lumaNE = dot( rgbNE, luma );\\n float lumaSW = dot( rgbSW, luma );\\n float lumaSE = dot( rgbSE, luma );\\n float lumaM = dot( rgbM, luma );\\n float lumaMin = min( lumaM, min( min( lumaNW, lumaNE ), min( lumaSW, lumaSE ) ) );\\n float lumaMax = max( lumaM, max( max( lumaNW, lumaNE) , max( lumaSW, lumaSE ) ) );\\n vec2 dir;\\n dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\\n dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\\n float dirReduce = max( ( lumaNW + lumaNE + lumaSW + lumaSE ) * ( 0.25 * FXAA_REDUCE_MUL ), FXAA_REDUCE_MIN );\\n float rcpDirMin = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + dirReduce );\\n dir = min( vec2( FXAA_SPAN_MAX, FXAA_SPAN_MAX),\\n max( vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),\\n dir * rcpDirMin)) * resolution;\\n vec3 rgbA = decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution + dir * ( 1.0 / 3.0 - 0.5 ) ) ).xyz;\\n rgbA += decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution + dir * ( 2.0 / 3.0 - 0.5 ) ) ).xyz;\\n rgbA *= 0.5;\\n vec3 rgbB = decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution + dir * -0.5 ) ).xyz;\\n rgbB += decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution + dir * 0.5 ) ).xyz;\\n rgbB *= 0.25;\\n rgbB += rgbA * 0.5;\\n float lumaB = dot( rgbB, luma );\\n if ( ( lumaB < lumaMin ) || ( lumaB > lumaMax ) )\\n {\\n gl_FragColor = vec4( rgbA, opacity );\\n }\\n else {\\n gl_FragColor = vec4( rgbB, opacity );\\n }\\n}\\n@end\";!function(e){e.import(\"@export clay.compositor.coloradjust\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nuniform float brightness : 0.0;\\nuniform float contrast : 1.0;\\nuniform float exposure : 0.0;\\nuniform float gamma : 1.0;\\nuniform float saturation : 1.0;\\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\\nvoid main()\\n{\\n vec4 tex = texture2D( texture, v_Texcoord);\\n vec3 color = clamp(tex.rgb + vec3(brightness), 0.0, 1.0);\\n color = clamp( (color-vec3(0.5))*contrast+vec3(0.5), 0.0, 1.0);\\n color = clamp( color * pow(2.0, exposure), 0.0, 1.0);\\n color = clamp( pow(color, vec3(gamma)), 0.0, 1.0);\\n float luminance = dot( color, w );\\n color = mix(vec3(luminance), color, saturation);\\n gl_FragColor = vec4(color, tex.a);\\n}\\n@end\\n@export clay.compositor.brightness\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nuniform float brightness : 0.0;\\nvoid main()\\n{\\n vec4 tex = texture2D( texture, v_Texcoord);\\n vec3 color = tex.rgb + vec3(brightness);\\n gl_FragColor = vec4(color, tex.a);\\n}\\n@end\\n@export clay.compositor.contrast\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nuniform float contrast : 1.0;\\nvoid main()\\n{\\n vec4 tex = texture2D( texture, v_Texcoord);\\n vec3 color = (tex.rgb-vec3(0.5))*contrast+vec3(0.5);\\n gl_FragColor = vec4(color, tex.a);\\n}\\n@end\\n@export clay.compositor.exposure\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nuniform float exposure : 0.0;\\nvoid main()\\n{\\n vec4 tex = texture2D(texture, v_Texcoord);\\n vec3 color = tex.rgb * pow(2.0, exposure);\\n gl_FragColor = vec4(color, tex.a);\\n}\\n@end\\n@export clay.compositor.gamma\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nuniform float gamma : 1.0;\\nvoid main()\\n{\\n vec4 tex = texture2D(texture, v_Texcoord);\\n vec3 color = pow(tex.rgb, vec3(gamma));\\n gl_FragColor = vec4(color, tex.a);\\n}\\n@end\\n@export clay.compositor.saturation\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nuniform float saturation : 1.0;\\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\\nvoid main()\\n{\\n vec4 tex = texture2D(texture, v_Texcoord);\\n vec3 color = tex.rgb;\\n float luminance = dot(color, w);\\n color = mix(vec3(luminance), color, saturation);\\n gl_FragColor = vec4(color, tex.a);\\n}\\n@end\"),e.import(Zs),e.import(\"@export clay.compositor.hdr.log_lum\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\\n@import clay.util.rgbm\\nvoid main()\\n{\\n vec4 tex = decodeHDR(texture2D(texture, v_Texcoord));\\n float luminance = dot(tex.rgb, w);\\n luminance = log(luminance + 0.001);\\n gl_FragColor = encodeHDR(vec4(vec3(luminance), 1.0));\\n}\\n@end\\n@export clay.compositor.hdr.lum_adaption\\nvarying vec2 v_Texcoord;\\nuniform sampler2D adaptedLum;\\nuniform sampler2D currentLum;\\nuniform float frameTime : 0.02;\\n@import clay.util.rgbm\\nvoid main()\\n{\\n float fAdaptedLum = decodeHDR(texture2D(adaptedLum, vec2(0.5, 0.5))).r;\\n float fCurrentLum = exp(encodeHDR(texture2D(currentLum, vec2(0.5, 0.5))).r);\\n fAdaptedLum += (fCurrentLum - fAdaptedLum) * (1.0 - pow(0.98, 30.0 * frameTime));\\n gl_FragColor = encodeHDR(vec4(vec3(fAdaptedLum), 1.0));\\n}\\n@end\\n@export clay.compositor.lum\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\\nvoid main()\\n{\\n vec4 tex = texture2D( texture, v_Texcoord );\\n float luminance = dot(tex.rgb, w);\\n gl_FragColor = vec4(vec3(luminance), 1.0);\\n}\\n@end\"),e.import(Ys),e.import(\"@export clay.compositor.vignette\\n#define OUTPUT_ALPHA\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nuniform float darkness: 1;\\nuniform float offset: 1;\\n@import clay.util.rgbm\\nvoid main()\\n{\\n vec4 texel = decodeHDR(texture2D(texture, v_Texcoord));\\n gl_FragColor.rgb = texel.rgb;\\n vec2 uv = (v_Texcoord - vec2(0.5)) * vec2(offset);\\n gl_FragColor = encodeHDR(vec4(mix(texel.rgb, vec3(1.0 - darkness), dot(uv, uv)), texel.a));\\n}\\n@end\"),e.import(Ks),e.import(Qs),e.import(Js),e.import($s),e.import(el),e.import(\"@export clay.compositor.lensflare\\n#define SAMPLE_NUMBER 8\\nuniform sampler2D texture;\\nuniform sampler2D lenscolor;\\nuniform vec2 textureSize : [512, 512];\\nuniform float dispersal : 0.3;\\nuniform float haloWidth : 0.4;\\nuniform float distortion : 1.0;\\nvarying vec2 v_Texcoord;\\n@import clay.util.rgbm\\nvec4 textureDistorted(\\n in vec2 texcoord,\\n in vec2 direction,\\n in vec3 distortion\\n) {\\n return vec4(\\n decodeHDR(texture2D(texture, texcoord + direction * distortion.r)).r,\\n decodeHDR(texture2D(texture, texcoord + direction * distortion.g)).g,\\n decodeHDR(texture2D(texture, texcoord + direction * distortion.b)).b,\\n 1.0\\n );\\n}\\nvoid main()\\n{\\n vec2 texcoord = -v_Texcoord + vec2(1.0); vec2 textureOffset = 1.0 / textureSize;\\n vec2 ghostVec = (vec2(0.5) - texcoord) * dispersal;\\n vec2 haloVec = normalize(ghostVec) * haloWidth;\\n vec3 distortion = vec3(-textureOffset.x * distortion, 0.0, textureOffset.x * distortion);\\n vec4 result = vec4(0.0);\\n for (int i = 0; i < SAMPLE_NUMBER; i++)\\n {\\n vec2 offset = fract(texcoord + ghostVec * float(i));\\n float weight = length(vec2(0.5) - offset) / length(vec2(0.5));\\n weight = pow(1.0 - weight, 10.0);\\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\\n }\\n result *= texture2D(lenscolor, vec2(length(vec2(0.5) - texcoord)) / length(vec2(0.5)));\\n float weight = length(vec2(0.5) - fract(texcoord + haloVec)) / length(vec2(0.5));\\n weight = pow(1.0 - weight, 10.0);\\n vec2 offset = fract(texcoord + haloVec);\\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\\n gl_FragColor = result;\\n}\\n@end\"),e.import(tl),e.import(rl)}(Xe);var il=/^#source\\((.*?)\\)/;function nl(e,t,r){var i,n,a,o,s=e.type||\"filter\";if(\"filter\"===s){var l=e.shader.trim(),h=il.exec(l);if(h?i=Xe.source(h[1].trim()):\"#\"===l.charAt(0)&&(i=t.shaders[l.substr(1)]),i||(i=l),!i)return}if(e.inputs)for(var u in n={},e.inputs)\"string\"==typeof e.inputs[u]?n[u]=e.inputs[u]:n[u]={node:e.inputs[u].node,pin:e.inputs[u].pin};if(e.outputs)for(var u in a={},e.outputs){var c=e.outputs[u];a[u]={},null!=c.attachment&&(a[u].attachment=c.attachment),null!=c.keepLastFrame&&(a[u].keepLastFrame=c.keepLastFrame),null!=c.outputLastFrame&&(a[u].outputLastFrame=c.outputLastFrame),c.parameters&&(a[u].parameters=sl(c.parameters))}if(o=\"scene\"===s?new js({name:e.name,scene:r.scene,camera:r.camera,outputs:a}):\"texture\"===s?new Xs({name:e.name,outputs:a}):new qs({name:e.name,shader:i,inputs:n,outputs:a})){if(e.parameters)for(var u in e.parameters)\"string\"==typeof(d=e.parameters[u])?\"#\"===(d=d.trim()).charAt(0)?d=t.textures[d.substr(1)]:o.on(\"beforerender\",ll(u,hl(d))):\"function\"==typeof d&&o.on(\"beforerender\",d),o.setParameter(u,d);if(e.defines&&o.pass)for(var u in e.defines){var d=e.defines[u];o.pass.material.define(\"fragment\",u,d)}}return o}function al(e,t){return e}function ol(e,t){return t}function sl(e){var t={};if(!e)return t;[\"type\",\"minFilter\",\"magFilter\",\"wrapS\",\"wrapT\",\"flipY\",\"useMipmap\"].forEach((function(r){var i=e[r];null!=i&&(\"string\"==typeof i&&(i=wr[i]),t[r]=i)}));var r=e.scale||1;return[\"width\",\"height\"].forEach((function(i){if(null!=e[i]){var n=e[i];\"string\"==typeof n?(n=n.trim(),t[i]=(a=hl(n),o=(o=r)||1,function(e){var t=e.getDevicePixelRatio(),r=e.getWidth()*o,i=e.getHeight()*o;return a(r,i,t)})):t[i]=n}var a,o})),t.width||(t.width=al),t.height||(t.height=ol),null!=e.useMipmap&&(t.useMipmap=e.useMipmap),t}function ll(e,t){return function(r){var i=r.getDevicePixelRatio(),n=r.getWidth(),a=r.getHeight(),o=t(n,a,i);this.setParameter(e,o)}}function hl(e){var t=/^expr\\((.*)\\)$/.exec(e);if(t)try{var r=new Function(\"width\",\"height\",\"dpr\",\"return \"+t[1]);return r(1,1),r}catch(e){throw new Error(\"Invalid expression.\")}}const ul=function(e,t){for(var r=0,i=1/t,n=e;n>0;)r+=i*(n%t),n=Math.floor(n/t),i/=t;return r};function cl(e){for(var t=new Uint8Array(e*e*4),r=0,i=new vt,n=0;n 0.99999) {\\n gl_FragColor = vec4(1.0);\\n return;\\n }\\n mat3 kernelBasis;\\n#endif\\n\\n float z = depthTexel.r * 2.0 - 1.0;\\n\\n vec4 projectedPos = vec4(v_Texcoord * 2.0 - 1.0, z, 1.0);\\n vec4 p4 = projectionInv * projectedPos;\\n\\n vec3 position = p4.xyz / p4.w;\\n\\n float ao = ssaoEstimator(position, kernelBasis);\\n ao = clamp(1.0 - (1.0 - ao) * intensity, 0.0, 1.0);\\n gl_FragColor = vec4(vec3(ao), 1.0);\\n}\\n\\n@end\\n\\n\\n@export ecgl.ssao.blur\\n#define SHADER_NAME SSAO_BLUR\\n\\nuniform sampler2D ssaoTexture;\\n\\n#ifdef NORMALTEX_ENABLED\\nuniform sampler2D normalTex;\\n#endif\\n\\nvarying vec2 v_Texcoord;\\n\\nuniform vec2 textureSize;\\nuniform float blurSize : 1.0;\\n\\nuniform int direction: 0.0;\\n\\n#ifdef DEPTHTEX_ENABLED\\nuniform sampler2D depthTex;\\nuniform mat4 projection;\\nuniform float depthRange : 0.5;\\n\\nfloat getLinearDepth(vec2 coord)\\n{\\n float depth = texture2D(depthTex, coord).r * 2.0 - 1.0;\\n return projection[3][2] / (depth * projection[2][3] - projection[2][2]);\\n}\\n#endif\\n\\nvoid main()\\n{\\n float kernel[5];\\n kernel[0] = 0.122581;\\n kernel[1] = 0.233062;\\n kernel[2] = 0.288713;\\n kernel[3] = 0.233062;\\n kernel[4] = 0.122581;\\n\\n vec2 off = vec2(0.0);\\n if (direction == 0) {\\n off[0] = blurSize / textureSize.x;\\n }\\n else {\\n off[1] = blurSize / textureSize.y;\\n }\\n\\n vec2 coord = v_Texcoord;\\n\\n float sum = 0.0;\\n float weightAll = 0.0;\\n\\n#ifdef NORMALTEX_ENABLED\\n vec3 centerNormal = texture2D(normalTex, v_Texcoord).rgb * 2.0 - 1.0;\\n#endif\\n#if defined(DEPTHTEX_ENABLED)\\n float centerDepth = getLinearDepth(v_Texcoord);\\n#endif\\n\\n for (int i = 0; i < 5; i++) {\\n vec2 coord = clamp(v_Texcoord + vec2(float(i) - 2.0) * off, vec2(0.0), vec2(1.0));\\n\\n float w = kernel[i];\\n#ifdef NORMALTEX_ENABLED\\n vec3 normal = texture2D(normalTex, coord).rgb * 2.0 - 1.0;\\n w *= clamp(dot(normal, centerNormal), 0.0, 1.0);\\n#endif\\n#ifdef DEPTHTEX_ENABLED\\n float d = getLinearDepth(coord);\\n w *= (1.0 - smoothstep(abs(centerDepth - d) / depthRange, 0.0, 1.0));\\n#endif\\n\\n weightAll += w;\\n sum += texture2D(ssaoTexture, coord).r * w;\\n }\\n\\n gl_FragColor = vec4(vec3(sum / weightAll), 1.0);\\n}\\n\\n@end\\n\"),pl.prototype.setDepthTexture=function(e){this._depthTex=e},pl.prototype.setNormalTexture=function(e){this._normalTex=e,this._ssaoPass.material[e?\"enableTexture\":\"disableTexture\"](\"normalTex\"),this.setKernelSize(this._kernelSize)},pl.prototype.update=function(e,t,r){var i=e.getWidth(),n=e.getHeight(),a=this._ssaoPass,o=this._blurPass;a.setUniform(\"kernel\",this._kernels[r%this._kernels.length]),a.setUniform(\"depthTex\",this._depthTex),null!=this._normalTex&&a.setUniform(\"normalTex\",this._normalTex),a.setUniform(\"depthTexSize\",[this._depthTex.width,this._depthTex.height]);var s=new Ht;Ht.transpose(s,t.worldTransform),a.setUniform(\"projection\",t.projectionMatrix.array),a.setUniform(\"projectionInv\",t.invProjectionMatrix.array),a.setUniform(\"viewInverseTranspose\",s.array);var l=this._ssaoTexture,h=this._blurTexture,u=this._blurTexture2;l.width=i/2,l.height=n/2,h.width=i,h.height=n,u.width=i,u.height=n,this._framebuffer.attach(l),this._framebuffer.bind(e),e.gl.clearColor(1,1,1,1),e.gl.clear(e.gl.COLOR_BUFFER_BIT),a.render(e),o.setUniform(\"textureSize\",[i/2,n/2]),o.setUniform(\"projection\",t.projectionMatrix.array),this._framebuffer.attach(h),o.setUniform(\"direction\",0),o.setUniform(\"ssaoTexture\",l),o.render(e),this._framebuffer.attach(u),o.setUniform(\"textureSize\",[i,n]),o.setUniform(\"direction\",1),o.setUniform(\"ssaoTexture\",h),o.render(e),this._framebuffer.unbind(e);var c=e.clearColor;e.gl.clearColor(c[0],c[1],c[2],c[3])},pl.prototype.getTargetTexture=function(){return this._blurTexture2},pl.prototype.setParameter=function(e,t){\"noiseTexSize\"===e?this.setNoiseSize(t):\"kernelSize\"===e?this.setKernelSize(t):\"intensity\"===e?this._ssaoPass.material.set(\"intensity\",t):this._ssaoPass.setUniform(e,t)},pl.prototype.setKernelSize=function(e){this._kernelSize=e,this._ssaoPass.material.define(\"fragment\",\"KERNEL_SIZE\",e),this._kernels=this._kernels||[];for(var t=0;t<30;t++)this._kernels[t]=fl(e,t*e,!!this._normalTex)},pl.prototype.setNoiseSize=function(e){var t=this._ssaoPass.getUniform(\"noiseTex\");t?(t.data=cl(e),t.width=t.height=e,t.dirty()):(t=dl(e),this._ssaoPass.setUniform(\"noiseTex\",dl(e))),this._ssaoPass.setUniform(\"noiseTexSize\",[e,e])},pl.prototype.dispose=function(e){this._blurTexture.dispose(e),this._ssaoTexture.dispose(e),this._blurTexture2.dispose(e)};const ml=pl;function gl(e){e=e||{},this._ssrPass=new pn({fragment:Xe.source(\"ecgl.ssr.main\"),clearColor:[0,0,0,0]}),this._blurPass1=new pn({fragment:Xe.source(\"ecgl.ssr.blur\"),clearColor:[0,0,0,0]}),this._blurPass2=new pn({fragment:Xe.source(\"ecgl.ssr.blur\"),clearColor:[0,0,0,0]}),this._blendPass=new pn({fragment:Xe.source(\"clay.compositor.blend\")}),this._blendPass.material.disableTexturesAll(),this._blendPass.material.enableTexture([\"texture1\",\"texture2\"]),this._ssrPass.setUniform(\"gBufferTexture1\",e.normalTexture),this._ssrPass.setUniform(\"gBufferTexture2\",e.depthTexture),this._blurPass1.setUniform(\"gBufferTexture1\",e.normalTexture),this._blurPass1.setUniform(\"gBufferTexture2\",e.depthTexture),this._blurPass2.setUniform(\"gBufferTexture1\",e.normalTexture),this._blurPass2.setUniform(\"gBufferTexture2\",e.depthTexture),this._blurPass2.material.define(\"fragment\",\"VERTICAL\"),this._blurPass2.material.define(\"fragment\",\"BLEND\"),this._ssrTexture=new Dr({type:wr.HALF_FLOAT}),this._texture2=new Dr({type:wr.HALF_FLOAT}),this._texture3=new Dr({type:wr.HALF_FLOAT}),this._prevTexture=new Dr({type:wr.HALF_FLOAT}),this._currentTexture=new Dr({type:wr.HALF_FLOAT}),this._frameBuffer=new zi({depthBuffer:!1}),this._normalDistribution=null,this._totalSamples=256,this._samplePerFrame=4,this._ssrPass.material.define(\"fragment\",\"SAMPLE_PER_FRAME\",this._samplePerFrame),this._ssrPass.material.define(\"fragment\",\"TOTAL_SAMPLES\",this._totalSamples),this._downScale=1}Xe.import(\"@export ecgl.ssr.main\\n\\n#define SHADER_NAME SSR\\n#define MAX_ITERATION 20;\\n#define SAMPLE_PER_FRAME 5;\\n#define TOTAL_SAMPLES 128;\\n\\nuniform sampler2D sourceTexture;\\nuniform sampler2D gBufferTexture1;\\nuniform sampler2D gBufferTexture2;\\nuniform sampler2D gBufferTexture3;\\nuniform samplerCube specularCubemap;\\nuniform float specularIntensity: 1;\\n\\nuniform mat4 projection;\\nuniform mat4 projectionInv;\\nuniform mat4 toViewSpace;\\nuniform mat4 toWorldSpace;\\n\\nuniform float maxRayDistance: 200;\\n\\nuniform float pixelStride: 16;\\nuniform float pixelStrideZCutoff: 50; \\nuniform float screenEdgeFadeStart: 0.9; \\nuniform float eyeFadeStart : 0.2; uniform float eyeFadeEnd: 0.8; \\nuniform float minGlossiness: 0.2; uniform float zThicknessThreshold: 1;\\n\\nuniform float nearZ;\\nuniform vec2 viewportSize : VIEWPORT_SIZE;\\n\\nuniform float jitterOffset: 0;\\n\\nvarying vec2 v_Texcoord;\\n\\n#ifdef DEPTH_DECODE\\n@import clay.util.decode_float\\n#endif\\n\\n#ifdef PHYSICALLY_CORRECT\\nuniform sampler2D normalDistribution;\\nuniform float sampleOffset: 0;\\nuniform vec2 normalDistributionSize;\\n\\nvec3 transformNormal(vec3 H, vec3 N) {\\n vec3 upVector = N.y > 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\\n vec3 tangentX = normalize(cross(N, upVector));\\n vec3 tangentZ = cross(N, tangentX);\\n return normalize(tangentX * H.x + N * H.y + tangentZ * H.z);\\n}\\nvec3 importanceSampleNormalGGX(float i, float roughness, vec3 N) {\\n float p = fract((i + sampleOffset) / float(TOTAL_SAMPLES));\\n vec3 H = texture2D(normalDistribution,vec2(roughness, p)).rgb;\\n return transformNormal(H, N);\\n}\\nfloat G_Smith(float g, float ndv, float ndl) {\\n float roughness = 1.0 - g;\\n float k = roughness * roughness / 2.0;\\n float G1V = ndv / (ndv * (1.0 - k) + k);\\n float G1L = ndl / (ndl * (1.0 - k) + k);\\n return G1L * G1V;\\n}\\nvec3 F_Schlick(float ndv, vec3 spec) {\\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\\n}\\n#endif\\n\\nfloat fetchDepth(sampler2D depthTexture, vec2 uv)\\n{\\n vec4 depthTexel = texture2D(depthTexture, uv);\\n return depthTexel.r * 2.0 - 1.0;\\n}\\n\\nfloat linearDepth(float depth)\\n{\\n if (projection[3][3] == 0.0) {\\n return projection[3][2] / (depth * projection[2][3] - projection[2][2]);\\n }\\n else {\\n return (depth - projection[3][2]) / projection[2][2];\\n }\\n}\\n\\nbool rayIntersectDepth(float rayZNear, float rayZFar, vec2 hitPixel)\\n{\\n if (rayZFar > rayZNear)\\n {\\n float t = rayZFar; rayZFar = rayZNear; rayZNear = t;\\n }\\n float cameraZ = linearDepth(fetchDepth(gBufferTexture2, hitPixel));\\n return rayZFar <= cameraZ && rayZNear >= cameraZ - zThicknessThreshold;\\n}\\n\\n\\nbool traceScreenSpaceRay(\\n vec3 rayOrigin, vec3 rayDir, float jitter,\\n out vec2 hitPixel, out vec3 hitPoint, out float iterationCount\\n)\\n{\\n float rayLength = ((rayOrigin.z + rayDir.z * maxRayDistance) > -nearZ)\\n ? (-nearZ - rayOrigin.z) / rayDir.z : maxRayDistance;\\n\\n vec3 rayEnd = rayOrigin + rayDir * rayLength;\\n\\n vec4 H0 = projection * vec4(rayOrigin, 1.0);\\n vec4 H1 = projection * vec4(rayEnd, 1.0);\\n\\n float k0 = 1.0 / H0.w, k1 = 1.0 / H1.w;\\n\\n vec3 Q0 = rayOrigin * k0, Q1 = rayEnd * k1;\\n\\n vec2 P0 = (H0.xy * k0 * 0.5 + 0.5) * viewportSize;\\n vec2 P1 = (H1.xy * k1 * 0.5 + 0.5) * viewportSize;\\n\\n P1 += dot(P1 - P0, P1 - P0) < 0.0001 ? 0.01 : 0.0;\\n vec2 delta = P1 - P0;\\n\\n bool permute = false;\\n if (abs(delta.x) < abs(delta.y)) {\\n permute = true;\\n delta = delta.yx;\\n P0 = P0.yx;\\n P1 = P1.yx;\\n }\\n float stepDir = sign(delta.x);\\n float invdx = stepDir / delta.x;\\n\\n vec3 dQ = (Q1 - Q0) * invdx;\\n float dk = (k1 - k0) * invdx;\\n\\n vec2 dP = vec2(stepDir, delta.y * invdx);\\n\\n float strideScaler = 1.0 - min(1.0, -rayOrigin.z / pixelStrideZCutoff);\\n float pixStride = 1.0 + strideScaler * pixelStride;\\n\\n dP *= pixStride; dQ *= pixStride; dk *= pixStride;\\n\\n vec4 pqk = vec4(P0, Q0.z, k0);\\n vec4 dPQK = vec4(dP, dQ.z, dk);\\n\\n pqk += dPQK * jitter;\\n float rayZFar = (dPQK.z * 0.5 + pqk.z) / (dPQK.w * 0.5 + pqk.w);\\n float rayZNear;\\n\\n bool intersect = false;\\n\\n vec2 texelSize = 1.0 / viewportSize;\\n\\n iterationCount = 0.0;\\n\\n for (int i = 0; i < MAX_ITERATION; i++)\\n {\\n pqk += dPQK;\\n\\n rayZNear = rayZFar;\\n rayZFar = (dPQK.z * 0.5 + pqk.z) / (dPQK.w * 0.5 + pqk.w);\\n\\n hitPixel = permute ? pqk.yx : pqk.xy;\\n hitPixel *= texelSize;\\n\\n intersect = rayIntersectDepth(rayZNear, rayZFar, hitPixel);\\n\\n iterationCount += 1.0;\\n\\n dPQK *= 1.2;\\n\\n if (intersect) {\\n break;\\n }\\n }\\n\\n Q0.xy += dQ.xy * iterationCount;\\n Q0.z = pqk.z;\\n hitPoint = Q0 / pqk.w;\\n\\n return intersect;\\n}\\n\\nfloat calculateAlpha(\\n float iterationCount, float reflectivity,\\n vec2 hitPixel, vec3 hitPoint, float dist, vec3 rayDir\\n)\\n{\\n float alpha = clamp(reflectivity, 0.0, 1.0);\\n alpha *= 1.0 - (iterationCount / float(MAX_ITERATION));\\n vec2 hitPixelNDC = hitPixel * 2.0 - 1.0;\\n float maxDimension = min(1.0, max(abs(hitPixelNDC.x), abs(hitPixelNDC.y)));\\n alpha *= 1.0 - max(0.0, maxDimension - screenEdgeFadeStart) / (1.0 - screenEdgeFadeStart);\\n\\n float _eyeFadeStart = eyeFadeStart;\\n float _eyeFadeEnd = eyeFadeEnd;\\n if (_eyeFadeStart > _eyeFadeEnd) {\\n float tmp = _eyeFadeEnd;\\n _eyeFadeEnd = _eyeFadeStart;\\n _eyeFadeStart = tmp;\\n }\\n\\n float eyeDir = clamp(rayDir.z, _eyeFadeStart, _eyeFadeEnd);\\n alpha *= 1.0 - (eyeDir - _eyeFadeStart) / (_eyeFadeEnd - _eyeFadeStart);\\n\\n alpha *= 1.0 - clamp(dist / maxRayDistance, 0.0, 1.0);\\n\\n return alpha;\\n}\\n\\n@import clay.util.rand\\n\\n@import clay.util.rgbm\\n\\nvoid main()\\n{\\n vec4 normalAndGloss = texture2D(gBufferTexture1, v_Texcoord);\\n\\n if (dot(normalAndGloss.rgb, vec3(1.0)) == 0.0) {\\n discard;\\n }\\n\\n float g = normalAndGloss.a;\\n#if !defined(PHYSICALLY_CORRECT)\\n if (g <= minGlossiness) {\\n discard;\\n }\\n#endif\\n\\n float reflectivity = (g - minGlossiness) / (1.0 - minGlossiness);\\n\\n vec3 N = normalize(normalAndGloss.rgb * 2.0 - 1.0);\\n N = normalize((toViewSpace * vec4(N, 0.0)).xyz);\\n\\n vec4 projectedPos = vec4(v_Texcoord * 2.0 - 1.0, fetchDepth(gBufferTexture2, v_Texcoord), 1.0);\\n vec4 pos = projectionInv * projectedPos;\\n vec3 rayOrigin = pos.xyz / pos.w;\\n vec3 V = -normalize(rayOrigin);\\n\\n float ndv = clamp(dot(N, V), 0.0, 1.0);\\n float iterationCount;\\n float jitter = rand(fract(v_Texcoord + jitterOffset));\\n\\n#ifdef PHYSICALLY_CORRECT\\n vec4 color = vec4(vec3(0.0), 1.0);\\n vec4 albedoMetalness = texture2D(gBufferTexture3, v_Texcoord);\\n vec3 albedo = albedoMetalness.rgb;\\n float m = albedoMetalness.a;\\n vec3 diffuseColor = albedo * (1.0 - m);\\n vec3 spec = mix(vec3(0.04), albedo, m);\\n\\n float jitter2 = rand(fract(v_Texcoord)) * float(TOTAL_SAMPLES);\\n\\n for (int i = 0; i < SAMPLE_PER_FRAME; i++) {\\n vec3 H = importanceSampleNormalGGX(float(i) + jitter2, 1.0 - g, N);\\n vec3 rayDir = normalize(reflect(-V, H));\\n#else\\n vec3 rayDir = normalize(reflect(-V, N));\\n#endif\\n vec2 hitPixel;\\n vec3 hitPoint;\\n\\n bool intersect = traceScreenSpaceRay(rayOrigin, rayDir, jitter, hitPixel, hitPoint, iterationCount);\\n\\n float dist = distance(rayOrigin, hitPoint);\\n\\n vec3 hitNormal = texture2D(gBufferTexture1, hitPixel).rgb * 2.0 - 1.0;\\n hitNormal = normalize((toViewSpace * vec4(hitNormal, 0.0)).xyz);\\n#ifdef PHYSICALLY_CORRECT\\n float ndl = clamp(dot(N, rayDir), 0.0, 1.0);\\n float vdh = clamp(dot(V, H), 0.0, 1.0);\\n float ndh = clamp(dot(N, H), 0.0, 1.0);\\n vec3 litTexel = vec3(0.0);\\n if (dot(hitNormal, rayDir) < 0.0 && intersect) {\\n litTexel = texture2D(sourceTexture, hitPixel).rgb;\\n litTexel *= pow(clamp(1.0 - dist / 200.0, 0.0, 1.0), 3.0);\\n\\n }\\n else {\\n #ifdef SPECULARCUBEMAP_ENABLED\\n vec3 rayDirW = normalize(toWorldSpace * vec4(rayDir, 0.0)).rgb;\\n litTexel = RGBMDecode(textureCubeLodEXT(specularCubemap, rayDirW, 0.0), 8.12).rgb * specularIntensity;\\n#endif\\n }\\n color.rgb += ndl * litTexel * (\\n F_Schlick(ndl, spec) * G_Smith(g, ndv, ndl) * vdh / (ndh * ndv + 0.001)\\n );\\n }\\n color.rgb /= float(SAMPLE_PER_FRAME);\\n#else\\n #if !defined(SPECULARCUBEMAP_ENABLED)\\n if (dot(hitNormal, rayDir) >= 0.0) {\\n discard;\\n }\\n if (!intersect) {\\n discard;\\n }\\n#endif\\n float alpha = clamp(calculateAlpha(iterationCount, reflectivity, hitPixel, hitPoint, dist, rayDir), 0.0, 1.0);\\n vec4 color = texture2D(sourceTexture, hitPixel);\\n color.rgb *= alpha;\\n\\n#ifdef SPECULARCUBEMAP_ENABLED\\n vec3 rayDirW = normalize(toWorldSpace * vec4(rayDir, 0.0)).rgb;\\n alpha = alpha * (intersect ? 1.0 : 0.0);\\n float bias = (1.0 -g) * 5.0;\\n color.rgb += (1.0 - alpha)\\n * RGBMDecode(textureCubeLodEXT(specularCubemap, rayDirW, bias), 8.12).rgb\\n * specularIntensity;\\n#endif\\n\\n#endif\\n\\n gl_FragColor = encodeHDR(color);\\n}\\n@end\\n\\n@export ecgl.ssr.blur\\n\\nuniform sampler2D texture;\\nuniform sampler2D gBufferTexture1;\\nuniform sampler2D gBufferTexture2;\\nuniform mat4 projection;\\nuniform float depthRange : 0.05;\\n\\nvarying vec2 v_Texcoord;\\n\\nuniform vec2 textureSize;\\nuniform float blurSize : 1.0;\\n\\n#ifdef BLEND\\n #ifdef SSAOTEX_ENABLED\\nuniform sampler2D ssaoTex;\\n #endif\\nuniform sampler2D sourceTexture;\\n#endif\\n\\nfloat getLinearDepth(vec2 coord)\\n{\\n float depth = texture2D(gBufferTexture2, coord).r * 2.0 - 1.0;\\n return projection[3][2] / (depth * projection[2][3] - projection[2][2]);\\n}\\n\\n@import clay.util.rgbm\\n\\n\\nvoid main()\\n{\\n @import clay.compositor.kernel.gaussian_9\\n\\n vec4 centerNTexel = texture2D(gBufferTexture1, v_Texcoord);\\n float g = centerNTexel.a;\\n float maxBlurSize = clamp(1.0 - g, 0.0, 1.0) * blurSize;\\n#ifdef VERTICAL\\n vec2 off = vec2(0.0, maxBlurSize / textureSize.y);\\n#else\\n vec2 off = vec2(maxBlurSize / textureSize.x, 0.0);\\n#endif\\n\\n vec2 coord = v_Texcoord;\\n\\n vec4 sum = vec4(0.0);\\n float weightAll = 0.0;\\n\\n vec3 cN = centerNTexel.rgb * 2.0 - 1.0;\\n float cD = getLinearDepth(v_Texcoord);\\n for (int i = 0; i < 9; i++) {\\n vec2 coord = clamp((float(i) - 4.0) * off + v_Texcoord, vec2(0.0), vec2(1.0));\\n float w = gaussianKernel[i]\\n * clamp(dot(cN, texture2D(gBufferTexture1, coord).rgb * 2.0 - 1.0), 0.0, 1.0);\\n float d = getLinearDepth(coord);\\n w *= (1.0 - smoothstep(abs(cD - d) / depthRange, 0.0, 1.0));\\n\\n weightAll += w;\\n sum += decodeHDR(texture2D(texture, coord)) * w;\\n }\\n\\n#ifdef BLEND\\n float aoFactor = 1.0;\\n #ifdef SSAOTEX_ENABLED\\n aoFactor = texture2D(ssaoTex, v_Texcoord).r;\\n #endif\\n gl_FragColor = encodeHDR(\\n sum / weightAll * aoFactor + decodeHDR(texture2D(sourceTexture, v_Texcoord))\\n );\\n#else\\n gl_FragColor = encodeHDR(sum / weightAll);\\n#endif\\n}\\n\\n@end\"),gl.prototype.setAmbientCubemap=function(e,t){this._ssrPass.material.set(\"specularCubemap\",e),this._ssrPass.material.set(\"specularIntensity\",t);var r=e&&t;this._ssrPass.material[r?\"enableTexture\":\"disableTexture\"](\"specularCubemap\")},gl.prototype.update=function(e,t,r,i){var n=e.getWidth(),a=e.getHeight(),o=this._ssrTexture,s=this._texture2,l=this._texture3;o.width=this._prevTexture.width=this._currentTexture.width=n/this._downScale,o.height=this._prevTexture.height=this._currentTexture.height=a/this._downScale,s.width=l.width=n,s.height=l.height=a;var h=this._frameBuffer,u=this._ssrPass,c=this._blurPass1,d=this._blurPass2,f=this._blendPass,p=new Ht,m=new Ht;Ht.transpose(p,t.worldTransform),Ht.transpose(m,t.viewMatrix),u.setUniform(\"sourceTexture\",r),u.setUniform(\"projection\",t.projectionMatrix.array),u.setUniform(\"projectionInv\",t.invProjectionMatrix.array),u.setUniform(\"toViewSpace\",p.array),u.setUniform(\"toWorldSpace\",m.array),u.setUniform(\"nearZ\",t.near);var g=i/this._totalSamples*this._samplePerFrame;if(u.setUniform(\"jitterOffset\",g),u.setUniform(\"sampleOffset\",i*this._samplePerFrame),c.setUniform(\"textureSize\",[o.width,o.height]),d.setUniform(\"textureSize\",[n,a]),d.setUniform(\"sourceTexture\",r),c.setUniform(\"projection\",t.projectionMatrix.array),d.setUniform(\"projection\",t.projectionMatrix.array),h.attach(o),h.bind(e),u.render(e),this._physicallyCorrect&&(h.attach(this._currentTexture),f.setUniform(\"texture1\",this._prevTexture),f.setUniform(\"texture2\",o),f.material.set({weight1:i>=1?.95:0,weight2:i>=1?.05:1}),f.render(e)),h.attach(s),c.setUniform(\"texture\",this._physicallyCorrect?this._currentTexture:o),c.render(e),h.attach(l),d.setUniform(\"texture\",s),d.render(e),h.unbind(e),this._physicallyCorrect){var _=this._prevTexture;this._prevTexture=this._currentTexture,this._currentTexture=_}},gl.prototype.getTargetTexture=function(){return this._texture3},gl.prototype.setParameter=function(e,t){\"maxIteration\"===e?this._ssrPass.material.define(\"fragment\",\"MAX_ITERATION\",t):this._ssrPass.setUniform(e,t)},gl.prototype.setPhysicallyCorrect=function(e){e?(this._normalDistribution||(this._normalDistribution=_n.generateNormalDistribution(64,this._totalSamples)),this._ssrPass.material.define(\"fragment\",\"PHYSICALLY_CORRECT\"),this._ssrPass.material.set(\"normalDistribution\",this._normalDistribution),this._ssrPass.material.set(\"normalDistributionSize\",[64,this._totalSamples])):this._ssrPass.material.undefine(\"fragment\",\"PHYSICALLY_CORRECT\"),this._physicallyCorrect=e},gl.prototype.setSSAOTexture=function(e){var t=this._blurPass2;e?(t.material.enableTexture(\"ssaoTex\"),t.material.set(\"ssaoTex\",e)):t.material.disableTexture(\"ssaoTex\")},gl.prototype.isFinished=function(e){return!this._physicallyCorrect||e>this._totalSamples/this._samplePerFrame},gl.prototype.dispose=function(e){this._ssrTexture.dispose(e),this._texture2.dispose(e),this._texture3.dispose(e),this._prevTexture.dispose(e),this._currentTexture.dispose(e),this._frameBuffer.dispose(e)};const _l=gl,vl=[0,0,-.321585265978,-.154972575841,.458126042375,.188473391593,.842080129861,.527766490688,.147304551086,-.659453822776,-.331943915203,-.940619700594,.0479226680259,.54812163202,.701581552186,-.709825561388,-.295436780218,.940589268233,-.901489676764,.237713156085,.973570876096,-.109899459384,-.866792314779,-.451805525005,.330975007087,.800048655954,-.344275183665,.381779221166,-.386139432542,-.437418421534,-.576478634965,-.0148463392551,.385798197415,-.262426961053,-.666302061145,.682427250835,-.628010632582,-.732836215494,.10163141741,-.987658134403,.711995289051,-.320024291314,.0296005138058,.950296523438,.0130612307608,-.351024443122,-.879596633704,-.10478487883,.435712737232,.504254490347,.779203817497,.206477676721,.388264289969,-.896736162545,-.153106280781,-.629203242522,-.245517550697,.657969239148,.126830499058,.26862328493,-.634888119007,-.302301223431,.617074219636,.779817204925];function yl(e,t,r,i,n){var a=e.gl;t.setUniform(a,\"1i\",r,n),a.activeTexture(a.TEXTURE0+n),i.isRenderable()?i.bind(e):i.unbind(e)}function xl(e,t,r,i,n){var a,o,s,l,h=e.gl;return function(n,u,c){if(!l||l.material!==n.material){var d=n.material,f=n.__program,p=d.get(\"roughness\");null==p&&(p=1);var m=d.get(\"normalMap\")||t,g=d.get(\"roughnessMap\"),_=d.get(\"bumpMap\"),v=d.get(\"uvRepeat\"),y=d.get(\"uvOffset\"),x=d.get(\"detailUvRepeat\"),b=d.get(\"detailUvOffset\"),w=!!_&&d.isTextureEnabled(\"bumpMap\"),T=!!g&&d.isTextureEnabled(\"roughnessMap\"),S=d.isDefined(\"fragment\",\"DOUBLE_SIDED\");_=_||r,g=g||i,c!==u?(u.set(\"normalMap\",m),u.set(\"bumpMap\",_),u.set(\"roughnessMap\",g),u.set(\"useBumpMap\",w),u.set(\"useRoughnessMap\",T),u.set(\"doubleSide\",S),null!=v&&u.set(\"uvRepeat\",v),null!=y&&u.set(\"uvOffset\",y),null!=x&&u.set(\"detailUvRepeat\",x),null!=b&&u.set(\"detailUvOffset\",b),u.set(\"roughness\",p)):(f.setUniform(h,\"1f\",\"roughness\",p),a!==m&&yl(e,f,\"normalMap\",m,0),o!==_&&_&&yl(e,f,\"bumpMap\",_,1),s!==g&&g&&yl(e,f,\"roughnessMap\",g,2),null!=v&&f.setUniform(h,\"2f\",\"uvRepeat\",v),null!=y&&f.setUniform(h,\"2f\",\"uvOffset\",y),null!=x&&f.setUniform(h,\"2f\",\"detailUvRepeat\",x),null!=b&&f.setUniform(h,\"2f\",\"detailUvOffset\",b),f.setUniform(h,\"1i\",\"useBumpMap\",+w),f.setUniform(h,\"1i\",\"useRoughnessMap\",+T),f.setUniform(h,\"1i\",\"doubleSide\",+S)),a=m,o=_,s=g,l=n}}}function bl(e){e=e||{},this._depthTex=new Dr({format:wr.DEPTH_COMPONENT,type:wr.UNSIGNED_INT}),this._normalTex=new Dr({type:wr.HALF_FLOAT}),this._framebuffer=new zi,this._framebuffer.attach(this._normalTex),this._framebuffer.attach(this._depthTex,zi.DEPTH_ATTACHMENT),this._normalMaterial=new le({shader:new Xe(Xe.source(\"ecgl.normal.vertex\"),Xe.source(\"ecgl.normal.fragment\"))}),this._normalMaterial.enableTexture([\"normalMap\",\"bumpMap\",\"roughnessMap\"]),this._defaultNormalMap=an.createBlank(\"#000\"),this._defaultBumpMap=an.createBlank(\"#000\"),this._defaultRoughessMap=an.createBlank(\"#000\"),this._debugPass=new pn({fragment:Xe.source(\"clay.compositor.output\")}),this._debugPass.setUniform(\"texture\",this._normalTex),this._debugPass.material.undefine(\"fragment\",\"OUTPUT_ALPHA\")}Xe.import(\"@export ecgl.normal.vertex\\n\\n@import ecgl.common.transformUniforms\\n\\n@import ecgl.common.uv.header\\n\\n@import ecgl.common.attributes\\n\\nvarying vec3 v_Normal;\\nvarying vec3 v_WorldPosition;\\n\\n@import ecgl.common.normalMap.vertexHeader\\n\\n@import ecgl.common.vertexAnimation.header\\n\\nvoid main()\\n{\\n\\n @import ecgl.common.vertexAnimation.main\\n\\n @import ecgl.common.uv.main\\n\\n v_Normal = normalize((worldInverseTranspose * vec4(normal, 0.0)).xyz);\\n v_WorldPosition = (world * vec4(pos, 1.0)).xyz;\\n\\n @import ecgl.common.normalMap.vertexMain\\n\\n gl_Position = worldViewProjection * vec4(pos, 1.0);\\n\\n}\\n\\n\\n@end\\n\\n\\n@export ecgl.normal.fragment\\n\\n#define ROUGHNESS_CHANEL 0\\n\\nuniform bool useBumpMap;\\nuniform bool useRoughnessMap;\\nuniform bool doubleSide;\\nuniform float roughness;\\n\\n@import ecgl.common.uv.fragmentHeader\\n\\nvarying vec3 v_Normal;\\nvarying vec3 v_WorldPosition;\\n\\nuniform mat4 viewInverse : VIEWINVERSE;\\n\\n@import ecgl.common.normalMap.fragmentHeader\\n@import ecgl.common.bumpMap.header\\n\\nuniform sampler2D roughnessMap;\\n\\nvoid main()\\n{\\n vec3 N = v_Normal;\\n \\n bool flipNormal = false;\\n if (doubleSide) {\\n vec3 eyePos = viewInverse[3].xyz;\\n vec3 V = normalize(eyePos - v_WorldPosition);\\n\\n if (dot(N, V) < 0.0) {\\n flipNormal = true;\\n }\\n }\\n\\n @import ecgl.common.normalMap.fragmentMain\\n\\n if (useBumpMap) {\\n N = bumpNormal(v_WorldPosition, v_Normal, N);\\n }\\n\\n float g = 1.0 - roughness;\\n\\n if (useRoughnessMap) {\\n float g2 = 1.0 - texture2D(roughnessMap, v_DetailTexcoord)[ROUGHNESS_CHANEL];\\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\\n }\\n\\n if (flipNormal) {\\n N = -N;\\n }\\n\\n gl_FragColor.rgb = (N.xyz + 1.0) * 0.5;\\n gl_FragColor.a = g;\\n}\\n@end\"),bl.prototype.getDepthTexture=function(){return this._depthTex},bl.prototype.getNormalTexture=function(){return this._normalTex},bl.prototype.update=function(e,t,r){var i=e.getWidth(),n=e.getHeight(),a=this._depthTex,o=this._normalTex,s=this._normalMaterial;a.width=i,a.height=n,o.width=i,o.height=n;var l=t.getRenderList(r).opaque;this._framebuffer.bind(e),e.gl.clearColor(0,0,0,0),e.gl.clear(e.gl.COLOR_BUFFER_BIT|e.gl.DEPTH_BUFFER_BIT),e.gl.disable(e.gl.BLEND),e.renderPass(l,r,{getMaterial:function(){return s},ifRender:function(e){return e.renderNormal},beforeRender:xl(e,this._defaultNormalMap,this._defaultBumpMap,this._defaultRoughessMap,this._normalMaterial),sort:e.opaqueSortCompare}),this._framebuffer.unbind(e)},bl.prototype.renderDebug=function(e){this._debugPass.render(e)},bl.prototype.dispose=function(e){this._depthTex.dispose(e),this._normalTex.dispose(e)};const wl=bl;function Tl(e){e=e||{},this._edgePass=new pn({fragment:Xe.source(\"ecgl.edge\")}),this._edgePass.setUniform(\"normalTexture\",e.normalTexture),this._edgePass.setUniform(\"depthTexture\",e.depthTexture),this._targetTexture=new Dr({type:wr.HALF_FLOAT}),this._frameBuffer=new zi,this._frameBuffer.attach(this._targetTexture)}Tl.prototype.update=function(e,t,r,i){var n=e.getWidth(),a=e.getHeight(),o=this._targetTexture;o.width=n,o.height=a;var s=this._frameBuffer;s.bind(e),this._edgePass.setUniform(\"projectionInv\",t.invProjectionMatrix.array),this._edgePass.setUniform(\"textureSize\",[n,a]),this._edgePass.setUniform(\"texture\",r),this._edgePass.render(e),s.unbind(e)},Tl.prototype.getTargetTexture=function(){return this._targetTexture},Tl.prototype.setParameter=function(e,t){this._edgePass.setUniform(e,t)},Tl.prototype.dispose=function(e){this._targetTexture.dispose(e),this._frameBuffer.dispose(e)};const Sl=Tl,Ml={type:\"compositor\",nodes:[{name:\"source\",type:\"texture\",outputs:{color:{}}},{name:\"source_half\",shader:\"#source(clay.compositor.downsample)\",inputs:{texture:\"source\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 2)\",height:\"expr(height * 1.0 / 2)\",type:\"HALF_FLOAT\"}}},parameters:{textureSize:\"expr( [width * 1.0, height * 1.0] )\"}},{name:\"bright\",shader:\"#source(clay.compositor.bright)\",inputs:{texture:\"source_half\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 2)\",height:\"expr(height * 1.0 / 2)\",type:\"HALF_FLOAT\"}}},parameters:{threshold:2,scale:4,textureSize:\"expr([width * 1.0 / 2, height / 2])\"}},{name:\"bright_downsample_4\",shader:\"#source(clay.compositor.downsample)\",inputs:{texture:\"bright\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 4)\",height:\"expr(height * 1.0 / 4)\",type:\"HALF_FLOAT\"}}},parameters:{textureSize:\"expr( [width * 1.0 / 2, height / 2] )\"}},{name:\"bright_downsample_8\",shader:\"#source(clay.compositor.downsample)\",inputs:{texture:\"bright_downsample_4\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 8)\",height:\"expr(height * 1.0 / 8)\",type:\"HALF_FLOAT\"}}},parameters:{textureSize:\"expr( [width * 1.0 / 4, height / 4] )\"}},{name:\"bright_downsample_16\",shader:\"#source(clay.compositor.downsample)\",inputs:{texture:\"bright_downsample_8\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 16)\",height:\"expr(height * 1.0 / 16)\",type:\"HALF_FLOAT\"}}},parameters:{textureSize:\"expr( [width * 1.0 / 8, height / 8] )\"}},{name:\"bright_downsample_32\",shader:\"#source(clay.compositor.downsample)\",inputs:{texture:\"bright_downsample_16\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 32)\",height:\"expr(height * 1.0 / 32)\",type:\"HALF_FLOAT\"}}},parameters:{textureSize:\"expr( [width * 1.0 / 16, height / 16] )\"}},{name:\"bright_upsample_16_blur_h\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_downsample_32\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 16)\",height:\"expr(height * 1.0 / 16)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:0,textureSize:\"expr( [width * 1.0 / 32, height / 32] )\"}},{name:\"bright_upsample_16_blur_v\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_upsample_16_blur_h\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 16)\",height:\"expr(height * 1.0 / 16)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:1,textureSize:\"expr( [width * 1.0 / 16, height * 1.0 / 16] )\"}},{name:\"bright_upsample_8_blur_h\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_downsample_16\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 8)\",height:\"expr(height * 1.0 / 8)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:0,textureSize:\"expr( [width * 1.0 / 16, height * 1.0 / 16] )\"}},{name:\"bright_upsample_8_blur_v\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_upsample_8_blur_h\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 8)\",height:\"expr(height * 1.0 / 8)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:1,textureSize:\"expr( [width * 1.0 / 8, height * 1.0 / 8] )\"}},{name:\"bright_upsample_8_blend\",shader:\"#source(clay.compositor.blend)\",inputs:{texture1:\"bright_upsample_8_blur_v\",texture2:\"bright_upsample_16_blur_v\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 8)\",height:\"expr(height * 1.0 / 8)\",type:\"HALF_FLOAT\"}}},parameters:{weight1:.3,weight2:.7}},{name:\"bright_upsample_4_blur_h\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_downsample_8\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 4)\",height:\"expr(height * 1.0 / 4)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:0,textureSize:\"expr( [width * 1.0 / 8, height * 1.0 / 8] )\"}},{name:\"bright_upsample_4_blur_v\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_upsample_4_blur_h\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 4)\",height:\"expr(height * 1.0 / 4)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:1,textureSize:\"expr( [width * 1.0 / 4, height * 1.0 / 4] )\"}},{name:\"bright_upsample_4_blend\",shader:\"#source(clay.compositor.blend)\",inputs:{texture1:\"bright_upsample_4_blur_v\",texture2:\"bright_upsample_8_blend\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 4)\",height:\"expr(height * 1.0 / 4)\",type:\"HALF_FLOAT\"}}},parameters:{weight1:.3,weight2:.7}},{name:\"bright_upsample_2_blur_h\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_downsample_4\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 2)\",height:\"expr(height * 1.0 / 2)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:0,textureSize:\"expr( [width * 1.0 / 4, height * 1.0 / 4] )\"}},{name:\"bright_upsample_2_blur_v\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_upsample_2_blur_h\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 2)\",height:\"expr(height * 1.0 / 2)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:1,textureSize:\"expr( [width * 1.0 / 2, height * 1.0 / 2] )\"}},{name:\"bright_upsample_2_blend\",shader:\"#source(clay.compositor.blend)\",inputs:{texture1:\"bright_upsample_2_blur_v\",texture2:\"bright_upsample_4_blend\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 2)\",height:\"expr(height * 1.0 / 2)\",type:\"HALF_FLOAT\"}}},parameters:{weight1:.3,weight2:.7}},{name:\"bright_upsample_full_blur_h\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright\"},outputs:{color:{parameters:{width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:0,textureSize:\"expr( [width * 1.0 / 2, height * 1.0 / 2] )\"}},{name:\"bright_upsample_full_blur_v\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_upsample_full_blur_h\"},outputs:{color:{parameters:{width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:1,textureSize:\"expr( [width * 1.0, height * 1.0] )\"}},{name:\"bloom_composite\",shader:\"#source(clay.compositor.blend)\",inputs:{texture1:\"bright_upsample_full_blur_v\",texture2:\"bright_upsample_2_blend\"},outputs:{color:{parameters:{width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\",type:\"HALF_FLOAT\"}}},parameters:{weight1:.3,weight2:.7}},{name:\"coc\",shader:\"#source(ecgl.dof.coc)\",outputs:{color:{parameters:{minFilter:\"NEAREST\",magFilter:\"NEAREST\",width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\"}}},parameters:{focalDist:50,focalRange:30}},{name:\"dof_far_blur\",shader:\"#source(ecgl.dof.diskBlur)\",inputs:{texture:\"source\",coc:\"coc\"},outputs:{color:{parameters:{width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\",type:\"HALF_FLOAT\"}}},parameters:{textureSize:\"expr( [width * 1.0, height * 1.0] )\"}},{name:\"dof_near_blur\",shader:\"#source(ecgl.dof.diskBlur)\",inputs:{texture:\"source\",coc:\"coc\"},outputs:{color:{parameters:{width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\",type:\"HALF_FLOAT\"}}},parameters:{textureSize:\"expr( [width * 1.0, height * 1.0] )\"},defines:{BLUR_NEARFIELD:null}},{name:\"dof_coc_blur\",shader:\"#source(ecgl.dof.diskBlur)\",inputs:{texture:\"coc\"},outputs:{color:{parameters:{minFilter:\"NEAREST\",magFilter:\"NEAREST\",width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\"}}},parameters:{textureSize:\"expr( [width * 1.0, height * 1.0] )\"},defines:{BLUR_COC:null}},{name:\"dof_composite\",shader:\"#source(ecgl.dof.composite)\",inputs:{original:\"source\",blurred:\"dof_far_blur\",nearfield:\"dof_near_blur\",coc:\"coc\",nearcoc:\"dof_coc_blur\"},outputs:{color:{parameters:{width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\",type:\"HALF_FLOAT\"}}}},{name:\"composite\",shader:\"#source(clay.compositor.hdr.composite)\",inputs:{texture:\"source\",bloom:\"bloom_composite\"},outputs:{color:{parameters:{width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\"}}},defines:{}},{name:\"FXAA\",shader:\"#source(clay.compositor.fxaa)\",inputs:{texture:\"composite\"}}]};function Al(e,t){return{color:{parameters:{width:e,height:t}}}}Xe.import(Zs),Xe.import(Ys),Xe.import(Ks),Xe.import(Qs),Xe.import(Js),Xe.import($s),Xe.import(el),Xe.import(tl),Xe.import(rl),Xe.import(\"@export ecgl.dof.coc\\n\\nuniform sampler2D depth;\\n\\nuniform float zNear: 0.1;\\nuniform float zFar: 2000;\\n\\nuniform float focalDistance: 3;\\nuniform float focalRange: 1;\\nuniform float focalLength: 30;\\nuniform float fstop: 2.8;\\n\\nvarying vec2 v_Texcoord;\\n\\n@import clay.util.encode_float\\n\\nvoid main()\\n{\\n float z = texture2D(depth, v_Texcoord).r * 2.0 - 1.0;\\n\\n float dist = 2.0 * zNear * zFar / (zFar + zNear - z * (zFar - zNear));\\n\\n float aperture = focalLength / fstop;\\n\\n float coc;\\n\\n float uppper = focalDistance + focalRange;\\n float lower = focalDistance - focalRange;\\n if (dist <= uppper && dist >= lower) {\\n coc = 0.5;\\n }\\n else {\\n float focalAdjusted = dist > uppper ? uppper : lower;\\n\\n coc = abs(aperture * (focalLength * (dist - focalAdjusted)) / (dist * (focalAdjusted - focalLength)));\\n coc = clamp(coc, 0.0, 2.0) / 2.00001;\\n\\n if (dist < lower) {\\n coc = -coc;\\n }\\n coc = coc * 0.5 + 0.5;\\n }\\n\\n gl_FragColor = encodeFloat(coc);\\n}\\n@end\\n\\n\\n@export ecgl.dof.composite\\n\\n#define DEBUG 0\\n\\nuniform sampler2D original;\\nuniform sampler2D blurred;\\nuniform sampler2D nearfield;\\nuniform sampler2D coc;\\nuniform sampler2D nearcoc;\\nvarying vec2 v_Texcoord;\\n\\n@import clay.util.rgbm\\n@import clay.util.float\\n\\nvoid main()\\n{\\n vec4 blurredColor = texture2D(blurred, v_Texcoord);\\n vec4 originalColor = texture2D(original, v_Texcoord);\\n\\n float fCoc = decodeFloat(texture2D(coc, v_Texcoord));\\n\\n fCoc = abs(fCoc * 2.0 - 1.0);\\n\\n float weight = smoothstep(0.0, 1.0, fCoc);\\n \\n#ifdef NEARFIELD_ENABLED\\n vec4 nearfieldColor = texture2D(nearfield, v_Texcoord);\\n float fNearCoc = decodeFloat(texture2D(nearcoc, v_Texcoord));\\n fNearCoc = abs(fNearCoc * 2.0 - 1.0);\\n\\n gl_FragColor = encodeHDR(\\n mix(\\n nearfieldColor, mix(originalColor, blurredColor, weight),\\n pow(1.0 - fNearCoc, 4.0)\\n )\\n );\\n#else\\n gl_FragColor = encodeHDR(mix(originalColor, blurredColor, weight));\\n#endif\\n\\n}\\n\\n@end\\n\\n\\n\\n@export ecgl.dof.diskBlur\\n\\n#define POISSON_KERNEL_SIZE 16;\\n\\nuniform sampler2D texture;\\nuniform sampler2D coc;\\nvarying vec2 v_Texcoord;\\n\\nuniform float blurRadius : 10.0;\\nuniform vec2 textureSize : [512.0, 512.0];\\n\\nuniform vec2 poissonKernel[POISSON_KERNEL_SIZE];\\n\\nuniform float percent;\\n\\nfloat nrand(const in vec2 n) {\\n return fract(sin(dot(n.xy ,vec2(12.9898,78.233))) * 43758.5453);\\n}\\n\\n@import clay.util.rgbm\\n@import clay.util.float\\n\\n\\nvoid main()\\n{\\n vec2 offset = blurRadius / textureSize;\\n\\n float rnd = 6.28318 * nrand(v_Texcoord + 0.07 * percent );\\n float cosa = cos(rnd);\\n float sina = sin(rnd);\\n vec4 basis = vec4(cosa, -sina, sina, cosa);\\n\\n#if !defined(BLUR_NEARFIELD) && !defined(BLUR_COC)\\n offset *= abs(decodeFloat(texture2D(coc, v_Texcoord)) * 2.0 - 1.0);\\n#endif\\n\\n#ifdef BLUR_COC\\n float cocSum = 0.0;\\n#else\\n vec4 color = vec4(0.0);\\n#endif\\n\\n\\n float weightSum = 0.0;\\n\\n for (int i = 0; i < POISSON_KERNEL_SIZE; i++) {\\n vec2 ofs = poissonKernel[i];\\n\\n ofs = vec2(dot(ofs, basis.xy), dot(ofs, basis.zw));\\n\\n vec2 uv = v_Texcoord + ofs * offset;\\n vec4 texel = texture2D(texture, uv);\\n\\n float w = 1.0;\\n#ifdef BLUR_COC\\n float fCoc = decodeFloat(texel) * 2.0 - 1.0;\\n cocSum += clamp(fCoc, -1.0, 0.0) * w;\\n#else\\n texel = texel;\\n #if !defined(BLUR_NEARFIELD)\\n float fCoc = decodeFloat(texture2D(coc, uv)) * 2.0 - 1.0;\\n w *= abs(fCoc);\\n #endif\\n texel.rgb *= texel.a;\\n color += texel * w;\\n#endif\\n\\n weightSum += w;\\n }\\n\\n#ifdef BLUR_COC\\n gl_FragColor = encodeFloat(clamp(cocSum / weightSum, -1.0, 0.0) * 0.5 + 0.5);\\n#else\\n color /= weightSum;\\n color.rgb /= (color.a + 0.0001);\\n gl_FragColor = color;\\n#endif\\n}\\n\\n@end\"),Xe.import(\"@export ecgl.edge\\n\\nuniform sampler2D texture;\\n\\nuniform sampler2D normalTexture;\\nuniform sampler2D depthTexture;\\n\\nuniform mat4 projectionInv;\\n\\nuniform vec2 textureSize;\\n\\nuniform vec4 edgeColor: [0,0,0,0.8];\\n\\nvarying vec2 v_Texcoord;\\n\\nvec3 packColor(vec2 coord) {\\n float z = texture2D(depthTexture, coord).r * 2.0 - 1.0;\\n vec4 p = vec4(v_Texcoord * 2.0 - 1.0, z, 1.0);\\n vec4 p4 = projectionInv * p;\\n\\n return vec3(\\n texture2D(normalTexture, coord).rg,\\n -p4.z / p4.w / 5.0\\n );\\n}\\n\\nvoid main() {\\n vec2 cc = v_Texcoord;\\n vec3 center = packColor(cc);\\n\\n float size = clamp(1.0 - (center.z - 10.0) / 100.0, 0.0, 1.0) * 0.5;\\n float dx = size / textureSize.x;\\n float dy = size / textureSize.y;\\n\\n vec2 coord;\\n vec3 topLeft = packColor(cc+vec2(-dx, -dy));\\n vec3 top = packColor(cc+vec2(0.0, -dy));\\n vec3 topRight = packColor(cc+vec2(dx, -dy));\\n vec3 left = packColor(cc+vec2(-dx, 0.0));\\n vec3 right = packColor(cc+vec2(dx, 0.0));\\n vec3 bottomLeft = packColor(cc+vec2(-dx, dy));\\n vec3 bottom = packColor(cc+vec2(0.0, dy));\\n vec3 bottomRight = packColor(cc+vec2(dx, dy));\\n\\n vec3 v = -topLeft-2.0*top-topRight+bottomLeft+2.0*bottom+bottomRight;\\n vec3 h = -bottomLeft-2.0*left-topLeft+bottomRight+2.0*right+topRight;\\n\\n float edge = sqrt(dot(h, h) + dot(v, v));\\n\\n edge = smoothstep(0.8, 1.0, edge);\\n\\n gl_FragColor = mix(texture2D(texture, v_Texcoord), vec4(edgeColor.rgb, 1.0), edgeColor.a * edge);\\n}\\n@end\");var El=[\"composite\",\"FXAA\"];function Cl(){this._width,this._height,this._dpr,this._sourceTexture=new Dr({type:wr.HALF_FLOAT}),this._depthTexture=new Dr({format:wr.DEPTH_COMPONENT,type:wr.UNSIGNED_INT}),this._framebuffer=new zi,this._framebuffer.attach(this._sourceTexture),this._framebuffer.attach(this._depthTexture,zi.DEPTH_ATTACHMENT),this._normalPass=new wl,this._compositor=function(e,t){var r=new Ws;t=t||{};var i={textures:{},parameters:{}};for(var n in e.parameters){var a=e.parameters[n];i.parameters[n]=sl(a)}return function(e,t,r,i){if(e.textures){var n={},a=0,o=!1,s=r.textureRootPath;f.each(e.textures,(function(e,t){var r,l=e.path,h=sl(e.parameters);if(Array.isArray(l)&&6===l.length)s&&(l=l.map((function(e){return f.relative2absolute(e,s)}))),r=new Ai(h);else{if(\"string\"!=typeof l)return;s&&(l=f.relative2absolute(l,s)),r=new Dr(h)}r.load(l),a++,r.once(\"success\",(function(){n[t]=r,0==--a&&(i(n),o=!0)}))})),0!==a||o||i(n)}else i({})}(e,0,t,(function(n){i.textures=n,function(n,a){for(var o=0;o=this._haltonSequence.length},render:function(e,t,r){var i=this._blendPass;0===this._frame?(i.setUniform(\"weight1\",0),i.setUniform(\"weight2\",1)):(i.setUniform(\"weight1\",.9),i.setUniform(\"weight2\",.1)),i.setUniform(\"texture1\",this._prevFrameTex),i.setUniform(\"texture2\",t||this._sourceTex),this._blendFb.attach(this._outputTex),this._blendFb.bind(e),i.render(e),this._blendFb.unbind(e),r||(this._outputPass.setUniform(\"texture\",this._outputTex),this._outputPass.render(e));var n=this._prevFrameTex;this._prevFrameTex=this._outputTex,this._outputTex=n,this._frame++},dispose:function(e){this._sourceFb.dispose(e),this._blendFb.dispose(e),this._prevFrameTex.dispose(e),this._outputTex.dispose(e),this._sourceTex.dispose(e),this._outputPass.dispose(e),this._blendPass.dispose(e)}};const Pl=Ll;function Ol(e){e=e||\"perspective\",this.layer=null,this.scene=new vi,this.rootNode=this.scene,this.viewport={x:0,y:0,width:0,height:0},this.setProjection(e),this._compositor=new Dl,this._temporalSS=new Pl,this._shadowMapPass=new ks;for(var t=[],r=0,i=0;i<30;i++){for(var n=[],a=0;a<6;a++)n.push(4*ul(r,2)-2),n.push(4*ul(r,3)-2),r++;t.push(n)}this._pcfKernels=t,this.scene.on(\"beforerender\",(function(e,t,r){this.needsTemporalSS()&&this._temporalSS.jitterProjection(e,r)}),this)}Ol.prototype.setProjection=function(e){var t=this.camera;t&&t.update(),\"perspective\"===e?this.camera instanceof Ei||(this.camera=new Ei,t&&this.camera.setLocalTransform(t.localTransform)):this.camera instanceof un||(this.camera=new un,t&&this.camera.setLocalTransform(t.localTransform)),this.camera.near=.1,this.camera.far=2e3},Ol.prototype.setViewport=function(e,t,r,i,n){this.camera instanceof Ei&&(this.camera.aspect=r/i),n=n||1,this.viewport.x=e,this.viewport.y=t,this.viewport.width=r,this.viewport.height=i,this.viewport.devicePixelRatio=n,this._compositor.resize(r*n,i*n),this._temporalSS.resize(r*n,i*n)},Ol.prototype.containPoint=function(e,t){var r=this.viewport;return t=this.layer.renderer.getHeight()-t,e>=r.x&&t>=r.y&&e<=r.x+r.width&&t<=r.y+r.height};var Nl=new _e;Ol.prototype.castRay=function(e,t,r){var i=this.layer.renderer,n=i.viewport;return i.viewport=this.viewport,i.screenToNDC(e,t,Nl),this.camera.castRay(Nl,r),i.viewport=n,r},Ol.prototype.prepareRender=function(){this.scene.update(),this.camera.update(),this.scene.updateLights();var e=this.scene.updateRenderList(this.camera);this._needsSortProgressively=!1;for(var t=0;t30},Ol.prototype._doRender=function(e,t,r){var i=this.scene,n=this.camera;r=r||0,this._updateTransparent(e,i,n,r),t||(this._shadowMapPass.kernelPCF=this._pcfKernels[0],this._shadowMapPass.render(e,i,n,!0)),this._updateShadowPCFKernel(r);var a,o=e.clearColor;e.gl.clearColor(o[0],o[1],o[2],o[3]),this._enablePostEffect&&(this.needsTemporalSS()&&this._temporalSS.jitterProjection(e,n),this._compositor.updateNormal(e,i,n,this._temporalSS.getFrame())),this._updateSSAO(e,i,n,this._temporalSS.getFrame()),this._enablePostEffect?((a=this._compositor.getSourceFrameBuffer()).bind(e),e.gl.clear(e.gl.DEPTH_BUFFER_BIT|e.gl.COLOR_BUFFER_BIT),e.render(i,n,!0,!0),a.unbind(e),this.needsTemporalSS()&&t?(this._compositor.composite(e,i,n,this._temporalSS.getSourceFrameBuffer(),this._temporalSS.getFrame()),e.setViewport(this.viewport),this._temporalSS.render(e)):(e.setViewport(this.viewport),this._compositor.composite(e,i,n,null,0))):this.needsTemporalSS()&&t?((a=this._temporalSS.getSourceFrameBuffer()).bind(e),e.saveClear(),e.clearBit=e.gl.DEPTH_BUFFER_BIT|e.gl.COLOR_BUFFER_BIT,e.render(i,n,!0,!0),e.restoreClear(),a.unbind(e),e.setViewport(this.viewport),this._temporalSS.render(e)):(e.setViewport(this.viewport),e.render(i,n,!0,!0))},Ol.prototype._updateTransparent=function(e,t,r,i){for(var n=new vt,a=new Ht,o=r.getWorldPosition(),s=t.getRenderList(r).transparent,l=0;lthis.camera.far||e80*r){i=a=e[0],n=o=e[1];for(var p=r;pa&&(a=s),l>o&&(o=l);h=Math.max(a-i,o-n)}return nh(d,f,r,i,n,h),f}function rh(e,t,r,i,n){var a,o;if(n===Th(e,t,r,i)>0)for(a=t;a=t;a-=i)o=xh(a,e[a],e[a+1],o);return o&&gh(o,o.next)&&(bh(o),o=o.next),o}function ih(e,t){if(!e)return e;t||(t=e);var r,i=e;do{if(r=!1,i.steiner||!gh(i,i.next)&&0!==mh(i.prev,i,i.next))i=i.next;else{if(bh(i),(i=t=i.prev)===i.next)return null;r=!0}}while(r||i!==t);return t}function nh(e,t,r,i,n,a,o){if(e){!o&&a&&function(e,t,r,i){var n=e;do{null===n.z&&(n.z=ch(n.x,n.y,t,r,i)),n.prevZ=n.prev,n.nextZ=n.next,n=n.next}while(n!==e);n.prevZ.nextZ=null,n.prevZ=null,function(e){var t,r,i,n,a,o,s,l,h=1;do{for(r=e,e=null,a=null,o=0;r;){for(o++,i=r,s=0,t=0;t0||l>0&&i;)0!==s&&(0===l||!i||r.z<=i.z)?(n=r,r=r.nextZ,s--):(n=i,i=i.nextZ,l--),a?a.nextZ=n:e=n,n.prevZ=a,a=n;r=i}a.nextZ=null,h*=2}while(o>1)}(n)}(e,i,n,a);for(var s,l,h=e;e.prev!==e.next;)if(s=e.prev,l=e.next,a?oh(e,i,n,a):ah(e))t.push(s.i/r),t.push(e.i/r),t.push(l.i/r),bh(e),e=l.next,h=l.next;else if((e=l)===h){o?1===o?nh(e=sh(e,t,r),t,r,i,n,a,2):2===o&&lh(e,t,r,i,n,a):nh(ih(e),t,r,i,n,a,1);break}}}function ah(e){var t=e.prev,r=e,i=e.next;if(mh(t,r,i)>=0)return!1;for(var n=e.next.next;n!==e.prev;){if(fh(t.x,t.y,r.x,r.y,i.x,i.y,n.x,n.y)&&mh(n.prev,n,n.next)>=0)return!1;n=n.next}return!0}function oh(e,t,r,i){var n=e.prev,a=e,o=e.next;if(mh(n,a,o)>=0)return!1;for(var s=n.xa.x?n.x>o.x?n.x:o.x:a.x>o.x?a.x:o.x,u=n.y>a.y?n.y>o.y?n.y:o.y:a.y>o.y?a.y:o.y,c=ch(s,l,t,r,i),d=ch(h,u,t,r,i),f=e.nextZ;f&&f.z<=d;){if(f!==e.prev&&f!==e.next&&fh(n.x,n.y,a.x,a.y,o.x,o.y,f.x,f.y)&&mh(f.prev,f,f.next)>=0)return!1;f=f.nextZ}for(f=e.prevZ;f&&f.z>=c;){if(f!==e.prev&&f!==e.next&&fh(n.x,n.y,a.x,a.y,o.x,o.y,f.x,f.y)&&mh(f.prev,f,f.next)>=0)return!1;f=f.prevZ}return!0}function sh(e,t,r){var i=e;do{var n=i.prev,a=i.next.next;!gh(n,a)&&_h(n,i,i.next,a)&&vh(n,a)&&vh(a,n)&&(t.push(n.i/r),t.push(i.i/r),t.push(a.i/r),bh(i),bh(i.next),i=e=a),i=i.next}while(i!==e);return i}function lh(e,t,r,i,n,a){var o=e;do{for(var s=o.next.next;s!==o.prev;){if(o.i!==s.i&&ph(o,s)){var l=yh(o,s);return o=ih(o,o.next),l=ih(l,l.next),nh(o,t,r,i,n,a),void nh(l,t,r,i,n,a)}s=s.next}o=o.next}while(o!==e)}function hh(e,t){return e.x-t.x}function uh(e,t){if(t=function(e,t){var r,i=t,n=e.x,a=e.y,o=-1/0;do{if(a<=i.y&&a>=i.next.y&&i.next.y!==i.y){var s=i.x+(a-i.y)*(i.next.x-i.x)/(i.next.y-i.y);if(s<=n&&s>o){if(o=s,s===n){if(a===i.y)return i;if(a===i.next.y)return i.next}r=i.x=i.x&&i.x>=u&&n!==i.x&&fh(ar.x)&&vh(i,e)&&(r=i,d=l),i=i.next;return r}(e,t)){var r=yh(t,e);ih(r,r.next)}}function ch(e,t,r,i,n){return(e=1431655765&((e=858993459&((e=252645135&((e=16711935&((e=32767*(e-r)/n)|e<<8))|e<<4))|e<<2))|e<<1))|(t=1431655765&((t=858993459&((t=252645135&((t=16711935&((t=32767*(t-i)/n)|t<<8))|t<<4))|t<<2))|t<<1))<<1}function dh(e){var t=e,r=e;do{t.x=0&&(e-o)*(i-s)-(r-o)*(t-s)>=0&&(r-o)*(a-s)-(n-o)*(i-s)>=0}function ph(e,t){return e.next.i!==t.i&&e.prev.i!==t.i&&!function(e,t){var r=e;do{if(r.i!==e.i&&r.next.i!==e.i&&r.i!==t.i&&r.next.i!==t.i&&_h(r,r.next,e,t))return!0;r=r.next}while(r!==e);return!1}(e,t)&&vh(e,t)&&vh(t,e)&&function(e,t){var r=e,i=!1,n=(e.x+t.x)/2,a=(e.y+t.y)/2;do{r.y>a!=r.next.y>a&&r.next.y!==r.y&&n<(r.next.x-r.x)*(a-r.y)/(r.next.y-r.y)+r.x&&(i=!i),r=r.next}while(r!==e);return i}(e,t)}function mh(e,t,r){return(t.y-e.y)*(r.x-t.x)-(t.x-e.x)*(r.y-t.y)}function gh(e,t){return e.x===t.x&&e.y===t.y}function _h(e,t,r,i){return!!(gh(e,t)&&gh(r,i)||gh(e,i)&&gh(r,t))||mh(e,t,r)>0!=mh(e,t,i)>0&&mh(r,i,e)>0!=mh(r,i,t)>0}function vh(e,t){return mh(e.prev,e,e.next)<0?mh(e,t,e.next)>=0&&mh(e,e.prev,t)>=0:mh(e,t,e.prev)<0||mh(e,e.next,t)<0}function yh(e,t){var r=new wh(e.i,e.x,e.y),i=new wh(t.i,t.x,t.y),n=e.next,a=t.prev;return e.next=t,t.prev=e,r.next=n,n.prev=r,i.next=r,r.prev=i,a.next=i,i.prev=a,i}function xh(e,t,r,i){var n=new wh(e,t,r);return i?(n.next=i.next,n.prev=i,i.next.prev=n,i.next=n):(n.prev=n,n.next=n),n}function bh(e){e.next.prev=e.prev,e.prev.next=e.next,e.prevZ&&(e.prevZ.nextZ=e.nextZ),e.nextZ&&(e.nextZ.prevZ=e.prevZ)}function wh(e,t,r){this.i=e,this.x=t,this.y=r,this.prev=null,this.next=null,this.z=null,this.prevZ=null,this.nextZ=null,this.steiner=!1}function Th(e,t,r,i){for(var n=0,a=t,o=r-i;ah&&s.push({pivot:Math.floor((l+h)/2),left:h,right:l}),h=a[o].pivot+1,(l=a[o].right)>h&&s.push({pivot:Math.floor((l+h)/2),left:h,right:l})}a=this._parts=s}else for(o=0;o=2e4},doSortTriangles:function(e,t){var r=this.indices;if(0===t){var i=this.attributes.position;e=e.array,this._triangleZList&&this._triangleZList.length===this.triangleCount||(this._triangleZList=new Float32Array(this.triangleCount),this._sortedTriangleIndices=new Uint32Array(this.triangleCount),this._indicesTmp=new r.constructor(r.length),this._triangleZListTmp=new Float32Array(this.triangleCount));for(var n,a=0,o=0;o0,r={},n=0;n65535?new Uint32Array(3*o):new Uint16Array(3*o),d.material.shader!==t&&d.material.attachShader(t,!0),Ka.setMaterialFromModel(t.__shading,d.material,e,r),s>0&&(this._linesMesh.geometry.resetOffset(),this._linesMesh.geometry.setVertexCount(s),this._linesMesh.geometry.setTriangleCount(l)),this._dataIndexOfVertex=new Uint32Array(a),this._vertexRangeOfDataIndex=new Uint32Array(2*(n-i))},_updateRegionMesh:function(e,t,r,i){for(var n=e.getData(),a=0,o=0,s=!1,l=this._polygonMesh,h=this._linesMesh,u=r;u0;w&&(b*=t.getDevicePixelRatio(),this._updateLinesGeometry(h.geometry,e,u,v,b,e.coordinateSystem.transform)),h.invisible=!w,h.material.set({color:g})}(l=this._polygonMesh).material.transparent=s,l.material.depthMask=!s,l.geometry.updateBoundingBox(),l.frontFace=this.extrudeY?Ka.Mesh.CCW:Ka.Mesh.CW,l.material.get(\"normalMap\")&&l.geometry.generateTangents(),l.seriesIndex=e.seriesIndex,l.on(\"mousemove\",this._onmousemove,this),l.on(\"mouseout\",this._onmouseout,this)},_updateDebugWireframe:function(e){var t=e.getModel(\"debug.wireframe\");if(t.get(\"show\")){var r=Ka.parseColor(t.get(\"lineStyle.color\")||\"rgba(0,0,0,0.5)\"),i=Mn(t.get(\"lineStyle.width\"),1),n=this._polygonMesh;n.geometry.generateBarycentric(),n.material.define(\"both\",\"WIREFRAME_TRIANGLE\"),n.material.set(\"wireframeLineColor\",r),n.material.set(\"wireframeLineWidth\",i)}},_onmousemove:function(e){var t=this._dataIndexOfVertex[e.triangle[0]];null==t&&(t=-1),t!==this._lastHoverDataIndex&&(this.downplay(this._lastHoverDataIndex),this.highlight(t),this._labelsBuilder.updateLabels([t])),this._lastHoverDataIndex=t,this._polygonMesh.dataIndex=t},_onmouseout:function(e){e.target&&(this.downplay(this._lastHoverDataIndex),this._lastHoverDataIndex=-1,this._polygonMesh.dataIndex=-1),this._labelsBuilder.updateLabels([])},_updateGroundPlane:function(e,t,r){var i=e.getModel(\"groundPlane\",e);if(this._groundMesh.invisible=!i.get(\"show\",!0),!this._groundMesh.invisible){var n=e.get(\"shading\"),a=this._groundMaterials[n];a||(a=this._groundMaterials.lambert),Ka.setMaterialFromModel(n,a,i,r),a.get(\"normalMap\")&&this._groundMesh.geometry.generateTangents(),this._groundMesh.material=a,this._groundMesh.material.set(\"color\",Ka.parseColor(i.get(\"color\"))),this._groundMesh.scale.set(t.size[0],t.size[2],1)}},_triangulation:function(e,t,r){this._triangulationResults=[];for(var i=[1/0,1/0,1/0],n=[-1/0,-1/0,-1/0],a=e.coordinateSystem,o=t;o1?i:0,L[U][g]=A.points[V+2],l.set(n+U,L[U]),s?(I[0]=(A.points[V]*_[0]-v[0])/x,I[1]=(A.points[V+2]*_[g]-v[g])/x):(I[0]=(k?R:R+G)/x,I[1]=(L[U][m]*_[m]-v[m])/x),u.set(n+U,I)}for(zh.sub(P,L[1],L[0]),zh.sub(O,L[3],L[0]),zh.cross(N,P,O),zh.normalize(N,N),U=0;U<4;U++)h.set(n+U,N),f&&c.set(n+U,o);for(U=0;U<6;U++)p[3*a+U]=D[U]+n;n+=4,a+=2,R+=G}}return t.dirty(),{vertexOffset:n,triangleOffset:a}},_getRegionLinesInfo:function(e,t,r){var i=0,n=0;return t.getRegionModel(e).getModel(\"itemStyle\").get(\"borderWidth\")>0&&t.getRegionPolygonCoords(e).forEach((function(e){var t=e.exterior,a=e.interiors;i+=r.getPolylineVertexCount(t),n+=r.getPolylineTriangleCount(t);for(var o=0;othis._endIndex)){t-=this._startIndex;for(var i=this._vertexRangeOfDataIndex[2*t];i0},_displacementChanged:!0,_displacementScale:0,updateDisplacementHash:function(){var e=this.getDisplacementTexture(),t=this.getDisplacemenScale();this._displacementChanged=this._displacementTexture!==e||this._displacementScale!==t,this._displacementTexture=e,this._displacementScale=t},isDisplacementChanged:function(){return this._displacementChanged}});i.util.merge(ru.prototype,uo),i.util.merge(ru.prototype,co),i.util.merge(ru.prototype,fo),i.util.merge(ru.prototype,Kl);const iu=ru;var nu=Math.PI,au=Math.sin,ou=Math.cos,su=Math.tan,lu=Math.asin,hu=Math.atan2,uu=nu/180,cu=23.4397*uu;function du(e,t){return hu(au(e)*ou(cu)-su(t)*au(cu),ou(e))}function fu(e,t,r){return hu(au(e),ou(e)*au(t)-su(r)*ou(t))}function pu(e,t,r){return lu(au(t)*au(r)+ou(t)*ou(r)*ou(e))}var mu={};mu.getPosition=function(e,t,r){var i=uu*-r,n=uu*t,a=function(e){return function(e){return e.valueOf()/864e5-.5+2440588}(e)-2451545}(e),o=function(e){var t,r,i=(r=function(e){return uu*(357.5291+.98560028*e)}(e))+uu*(1.9148*au(r)+.02*au(2*r)+3e-4*au(3*r))+102.9372*uu+nu;return{dec:(t=i,lu(au(0)*ou(cu)+ou(0)*au(cu)*au(t))),ra:du(i,0)}}(a),s=function(e,t){return uu*(280.16+360.9856235*e)-t}(a,i)-o.ra;return{azimuth:fu(s,n,o.dec),altitude:pu(s,n,o.dec)}};const gu=mu;Ka.Shader.import(Va),Ka.Shader.import(\"@export ecgl.atmosphere.vertex\\nattribute vec3 position: POSITION;\\nattribute vec3 normal : NORMAL;\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\nuniform mat4 normalMatrix : WORLDINVERSETRANSPOSE;\\n\\nvarying vec3 v_Normal;\\n\\nvoid main() {\\n v_Normal = normalize((normalMatrix * vec4(normal, 0.0)).xyz);\\n gl_Position = worldViewProjection * vec4(position, 1.0);\\n}\\n@end\\n\\n\\n@export ecgl.atmosphere.fragment\\nuniform mat4 viewTranspose: VIEWTRANSPOSE;\\nuniform float glowPower;\\nuniform vec3 glowColor;\\n\\nvarying vec3 v_Normal;\\n\\nvoid main() {\\n float intensity = pow(1.0 - dot(v_Normal, (viewTranspose * vec4(0.0, 0.0, 1.0, 0.0)).xyz), glowPower);\\n gl_FragColor = vec4(glowColor, intensity * intensity);\\n}\\n@end\");const _u=i.ComponentView.extend({type:\"globe\",__ecgl__:!0,_displacementScale:0,init:function(e,t){this.groupGL=new Ka.Node,this._sphereGeometry=new Ka.SphereGeometry({widthSegments:200,heightSegments:100,dynamic:!0}),this._overlayGeometry=new Ka.SphereGeometry({widthSegments:80,heightSegments:40}),this._planeGeometry=new Ka.PlaneGeometry,this._earthMesh=new Ka.Mesh({renderNormal:!0}),this._atmosphereMesh=new Ka.Mesh,this._atmosphereGeometry=new Ka.SphereGeometry({widthSegments:80,heightSegments:40}),this._atmosphereMaterial=new Ka.Material({shader:new Ka.Shader(Ka.Shader.source(\"ecgl.atmosphere.vertex\"),Ka.Shader.source(\"ecgl.atmosphere.fragment\")),transparent:!0}),this._atmosphereMesh.geometry=this._atmosphereGeometry,this._atmosphereMesh.material=this._atmosphereMaterial,this._atmosphereMesh.frontFace=Ka.Mesh.CW,this._lightRoot=new Ka.Node,this._sceneHelper=new Uo,this._sceneHelper.initLight(this._lightRoot),this.groupGL.add(this._atmosphereMesh),this.groupGL.add(this._earthMesh),this._control=new Do({zr:t.getZr()}),this._control.init(),this._layerMeshes={}},render:function(e,t,r){var i=e.coordinateSystem,n=e.get(\"shading\");i.viewGL.add(this._lightRoot),e.get(\"show\")?i.viewGL.add(this.groupGL):i.viewGL.remove(this.groupGL),this._sceneHelper.setScene(i.viewGL.scene),i.viewGL.setPostEffect(e.getModel(\"postEffect\"),r),i.viewGL.setTemporalSuperSampling(e.getModel(\"temporalSuperSampling\"));var a=this._earthMesh;a.geometry=this._sphereGeometry;var o=\"ecgl.\"+n;a.material&&a.material.shader.name===o||(a.material=Ka.createMaterial(o)),Ka.setMaterialFromModel(n,a.material,e,r),[\"roughnessMap\",\"metalnessMap\",\"detailMap\",\"normalMap\"].forEach((function(e){var t=a.material.get(e);t&&(t.flipY=!1)})),a.material.set(\"color\",Ka.parseColor(e.get(\"baseColor\")));var s=.99*i.radius;if(a.scale.set(s,s,s),e.get(\"atmosphere.show\")){a.material.define(\"both\",\"ATMOSPHERE_ENABLED\"),this._atmosphereMesh.invisible=!1,this._atmosphereMaterial.setUniforms({glowPower:e.get(\"atmosphere.glowPower\")||6,glowColor:e.get(\"atmosphere.color\")||\"#ffffff\"}),a.material.setUniforms({glowPower:e.get(\"atmosphere.innerGlowPower\")||2,glowColor:e.get(\"atmosphere.color\")||\"#ffffff\"});var l=e.get(\"atmosphere.offset\")||5;this._atmosphereMesh.scale.set(s+l,s+l,s+l)}else a.material.undefine(\"both\",\"ATMOSPHERE_ENABLED\"),this._atmosphereMesh.invisible=!0;var h=a.material.setTextureImage(\"diffuseMap\",e.get(\"baseTexture\"),r,{flipY:!1,anisotropic:8});h&&h.surface&&h.surface.attachToMesh(a);var u=a.material.setTextureImage(\"bumpMap\",e.get(\"heightTexture\"),r,{flipY:!1,anisotropic:8});u&&u.surface&&u.surface.attachToMesh(a),a.material[e.get(\"postEffect.enable\")?\"define\":\"undefine\"](\"fragment\",\"SRGB_DECODE\"),this._updateLight(e,r),this._displaceVertices(e,r),this._updateViewControl(e,r),this._updateLayers(e,r)},afterRender:function(e,t,r,i){var n=i.renderer;this._sceneHelper.updateAmbientCubemap(n,e,r),this._sceneHelper.updateSkybox(n,e,r)},_updateLayers:function(e,t){var r=e.coordinateSystem,n=e.get(\"layers\"),a=r.radius,o=[],s=[],l=[],h=[];i.util.each(n,(function(e){var n=new i.Model(e),u=n.get(\"type\"),c=Ka.loadTexture(n.get(\"texture\"),t,{flipY:!1,anisotropic:8});if(c.surface&&c.surface.attachToMesh(this._earthMesh),\"blend\"===u){var d=n.get(\"blendTo\"),f=Mn(n.get(\"intensity\"),1);\"emission\"===d?(l.push(c),h.push(f)):(o.push(c),s.push(f))}else{var p=n.get(\"id\"),m=this._layerMeshes[p];m||(m=this._layerMeshes[p]=new Ka.Mesh({geometry:this._overlayGeometry,castShadow:!1,ignorePicking:!0})),\"lambert\"===n.get(\"shading\")?(m.material=m.__lambertMaterial||new Ka.Material({autoUpdateTextureStatus:!1,shader:Ka.createShader(\"ecgl.lambert\"),transparent:!0,depthMask:!1}),m.__lambertMaterial=m.material):(m.material=m.__colorMaterial||new Ka.Material({autoUpdateTextureStatus:!1,shader:Ka.createShader(\"ecgl.color\"),transparent:!0,depthMask:!1}),m.__colorMaterial=m.material),m.material.enableTexture(\"diffuseMap\");var g=n.get(\"distance\"),_=a+(null==g?r.radius/100:g);m.scale.set(_,_,_),a=_;var v=this._blankTexture||(this._blankTexture=Ka.createBlankTexture(\"rgba(255, 255, 255, 0)\"));m.material.set(\"diffuseMap\",v),Ka.loadTexture(n.get(\"texture\"),t,{flipY:!1,anisotropic:8},(function(e){e.surface&&e.surface.attachToMesh(m),m.material.set(\"diffuseMap\",e),t.getZr().refresh()})),n.get(\"show\")?this.groupGL.add(m):this.groupGL.remove(m)}}),this);var u=this._earthMesh.material;u.define(\"fragment\",\"LAYER_DIFFUSEMAP_COUNT\",o.length),u.define(\"fragment\",\"LAYER_EMISSIVEMAP_COUNT\",l.length),u.set(\"layerDiffuseMap\",o),u.set(\"layerDiffuseIntensity\",s),u.set(\"layerEmissiveMap\",l),u.set(\"layerEmissionIntensity\",h);var c=e.getModel(\"debug.wireframe\");if(c.get(\"show\")){u.define(\"both\",\"WIREFRAME_TRIANGLE\");var d=Ka.parseColor(c.get(\"lineStyle.color\")||\"rgba(0,0,0,0.5)\"),f=Mn(c.get(\"lineStyle.width\"),1);u.set(\"wireframeLineWidth\",f),u.set(\"wireframeLineColor\",d)}else u.undefine(\"both\",\"WIREFRAME_TRIANGLE\")},_updateViewControl:function(e,t){var r=e.coordinateSystem,i=e.getModel(\"viewControl\"),n=(r.viewGL.camera,this),a=this._control;a.setViewGL(r.viewGL);var o,s,l=i.get(\"targetCoord\");null!=l&&(s=l[0]+90,o=l[1]),a.setFromViewControlModel(i,{baseDistance:r.radius,alpha:o,beta:s}),a.off(\"update\"),a.on(\"update\",(function(){t.dispatchAction({type:\"globeChangeCamera\",alpha:a.getAlpha(),beta:a.getBeta(),distance:a.getDistance()-r.radius,center:a.getCenter(),from:n.uid,globeId:e.id})}))},_displaceVertices:function(e,t){var r=e.get(\"displacementQuality\"),i=e.get(\"debug.wireframe.show\"),n=e.coordinateSystem;if(e.isDisplacementChanged()||r!==this._displacementQuality||i!==this._showDebugWireframe){this._displacementQuality=r,this._showDebugWireframe=i;var a=this._sphereGeometry,o={low:100,medium:200,high:400,ultra:800}[r]||200,s=o/2;(a.widthSegments!==o||i)&&(a.widthSegments=o,a.heightSegments=s,a.build()),this._doDisplaceVertices(a,n),i&&a.generateBarycentric()}},_doDisplaceVertices:function(e,t){var r=e.attributes.position.value,i=e.attributes.texcoord0.value,n=e.__originalPosition;n&&n.length===r.length||((n=new Float32Array(r.length)).set(r),e.__originalPosition=n);for(var a=t.displacementWidth,o=t.displacementHeight,s=t.displacementData,l=0;l50&&(a=1e3);var o=[];Ou.perspective(o,Iu,this.width/this.height,1,a),this.viewGL.camera.projectionMatrix.setArray(o),this.viewGL.camera.decomposeProjectionMatrix(),o=Ou.identity([]);var s=this.dataToPoint(this.center);Ou.scale(o,o,[1,-1,1]),Ou.translate(o,o,[0,0,-e]),Ou.rotateX(o,o,t),Ou.rotateZ(o,o,-this.bearing/180*Math.PI),Ou.translate(o,o,[-s[0]*this.getScale()*Bu,-s[1]*this.getScale()*Bu,0]),this.viewGL.camera.viewMatrix.array=o;var l=[];Ou.invert(l,o),this.viewGL.camera.worldTransform.array=l,this.viewGL.camera.decomposeWorldTransform();var h,u=Nu*this.getScale();if(this.altitudeExtent&&!isNaN(this.boxHeight)){var c=this.altitudeExtent[1]-this.altitudeExtent[0];h=this.boxHeight/c*this.getScale()/Math.pow(2,this._initialZoom-this.zoomOffset)}else h=u/(2*Math.PI*6378e3*Math.abs(Math.cos(this.center[1]*(Math.PI/180))))*this.altitudeScale*Bu;this.viewGL.rootNode.scale.set(this.getScale()*Bu,this.getScale()*Bu,h)}},getScale:function(){return Math.pow(2,this.zoom-this.zoomOffset)},projectOnTile:function(e,t){return this.projectOnTileWithScale(e,this.getScale()*Nu,t)},projectOnTileWithScale:function(e,t,r){var i=e[0],n=e[1]*Ru/180,a=t*(i*Ru/180+Ru)/(2*Ru),o=t*(Ru-Math.log(Math.tan(Ru/4+.5*n)))/(2*Ru);return(r=r||[])[0]=a,r[1]=o,r},unprojectFromTile:function(e,t){return this.unprojectOnTileWithScale(e,this.getScale()*Nu,t)},unprojectOnTileWithScale:function(e,t,r){var i=e[0],n=e[1],a=i/t*(2*Ru)-Ru,o=2*(Math.atan(Math.exp(Ru-n/t*(2*Ru)))-Ru/4);return(r=r||[])[0]=180*a/Ru,r[1]=180*o/Ru,r},dataToPoint:function(e,t){return(t=this.projectOnTileWithScale(e,Nu,t))[0]-=this._origin[0],t[1]-=this._origin[1],t[2]=isNaN(e[2])?0:e[2],isNaN(e[2])||(t[2]=e[2],this.altitudeExtent&&(t[2]-=this.altitudeExtent[0])),t}};const zu=Fu;function Gu(){zu.apply(this,arguments)}function Uu(e,t,r){function i(e,t){var r=t.getWidth(),i=t.getHeight(),n=t.getDevicePixelRatio();this.viewGL.setViewport(0,0,r,i,n),this.width=r,this.height=i,this.altitudeScale=e.get(\"altitudeScale\"),this.boxHeight=e.get(\"boxHeight\")}function n(e,t){if(\"auto\"!==this.model.get(\"boxHeight\")){var r=[1/0,-1/0];e.eachSeries((function(e){if(e.coordinateSystem===this){var t=e.getData(),i=e.coordDimToDataDim(\"alt\")[0];if(i){var n=t.getDataExtent(i,!0);r[0]=Math.min(r[0],n[0]),r[1]=Math.max(r[1],n[1])}}}),this),r&&isFinite(r[1]-r[0])&&(this.altitudeExtent=r)}}return{dimensions:t.prototype.dimensions,create:function(a,o){var s=[];return a.eachComponent(e,(function(e){var r=e.__viewGL;r||(r=e.__viewGL=new Il).setRootNode(new Ka.Node);var a=new t;a.viewGL=e.__viewGL,a.resize=i,a.resize(e,o),s.push(a),e.coordinateSystem=a,a.model=e,a.update=n})),a.eachSeries((function(t){if(t.get(\"coordinateSystem\")===e){var r=t.getReferringComponents(e).models[0];if(r||(r=a.getComponent(e)),!r)throw new Error(e+' \"'+Mn(t.get(e+\"Index\"),t.get(e+\"Id\"),0)+'\" not found');t.coordinateSystem=r.coordinateSystem}})),r&&r(s,a,o),s}}}Gu.prototype=new zu,Gu.prototype.constructor=Gu,Gu.prototype.type=\"mapbox3D\";const ku=Uu(\"mapbox3D\",Gu,(function(e){e.forEach((function(e){e.setCameraOption(e.model.getMapboxCameraOption())}))}));(0,i.use)((function(e){e.registerComponentModel(Au),e.registerComponentView(Pu),e.registerCoordinateSystem(\"mapbox3D\",ku),e.registerAction({type:\"mapbox3DChangeCamera\",event:\"mapbox3dcamerachanged\",update:\"mapbox3D:updateCamera\"},(function(e,t){t.eachComponent({mainType:\"mapbox3D\",query:e},(function(t){t.setMapboxCameraOption(e)}))}))}));var Vu=[\"zoom\",\"center\",\"pitch\",\"bearing\"],Hu=i.ComponentModel.extend({type:\"maptalks3D\",layoutMode:\"box\",coordinateSystem:null,defaultOption:{zlevel:-10,urlTemplate:\"http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png\",attribution:'© OpenStreetMap contributors, © CARTO',center:[0,0],zoom:0,pitch:0,bearing:0,light:{main:{alpha:20,beta:30}},altitudeScale:1,boxHeight:\"auto\"},getMaptalksCameraOption:function(){var e=this;return Vu.reduce((function(t,r){return t[r]=e.get(r),t}),{})},setMaptalksCameraOption:function(e){null!=e&&Vu.forEach((function(t){null!=e[t]&&(this.option[t]=e[t])}),this)},getMaptalks:function(){return this._maptalks},setMaptalks:function(e){this._maptalks=e}});i.util.merge(Hu.prototype,co),i.util.merge(Hu.prototype,fo);const Wu=Hu;function ju(e,t,r,i){if(this.id=e,this.zr=t,this.dom=document.createElement(\"div\"),this.dom.style.cssText=\"position:absolute;left:0;right:0;top:0;bottom:0;\",!maptalks)throw new Error(\"Maptalks library must be included. See https://maptalks.org\");this._maptalks=new maptalks.Map(this.dom,{center:r,zoom:i,doubleClickZoom:!1,fog:!1}),this._initEvents()}ju.prototype.setUnpainted=function(){},ju.prototype.resize=function(){this._maptalks.checkSize()},ju.prototype.getMaptalks=function(){return this._maptalks},ju.prototype.clear=function(){},ju.prototype.refresh=function(){this._maptalks.checkSize()};var Xu=[\"mousedown\",\"mouseup\",\"click\",\"dblclick\",\"mousemove\",\"mousewheel\",\"DOMMouseScroll\",\"touchstart\",\"touchend\",\"touchmove\",\"touchcancel\"];ju.prototype._initEvents=function(){var e=this.dom;this._handlers=this._handlers||{contextmenu:function(e){return e.preventDefault(),!1}},Xu.forEach((function(t){this._handlers[t]=function(r){var i={};for(var n in r)i[n]=r[n];i.bubbles=!1;var a=new r.constructor(r.type,i);\"mousewheel\"===t||\"DOMMouseScroll\"===t?e.dispatchEvent(a):e.firstElementChild.dispatchEvent(a)},this.zr.dom.addEventListener(t,this._handlers[t])}),this),this.zr.dom.addEventListener(\"contextmenu\",this._handlers.contextmenu)},ju.prototype.dispose=function(){Xu.forEach((function(e){this.zr.dom.removeEventListener(e,this._handlers[e])}),this),this._maptalks.remove()};const qu=ju;Ka.Shader.import(Lu);const Zu=i.ComponentView.extend({type:\"maptalks3D\",__ecgl__:!0,init:function(e,t){this._groundMesh=new Ka.Mesh({geometry:new Ka.PlaneGeometry,material:new Ka.Material({shader:new Ka.Shader({vertex:Ka.Shader.source(\"ecgl.displayShadow.vertex\"),fragment:Ka.Shader.source(\"ecgl.displayShadow.fragment\")}),depthMask:!1}),renderOrder:-100,culling:!1,castShadow:!1,$ignorePicking:!0,renderNormal:!0})},_initMaptalksLayer:function(e,t){var r=t.getZr();this._zrLayer=new qu(\"maptalks3D\",r,e.get(\"center\"),e.get(\"zoom\")),r.painter.insertLayer(-1e3,this._zrLayer),this._lightRoot=new Ka.Node,this._sceneHelper=new Uo(this._lightRoot),this._sceneHelper.initLight(this._lightRoot);var i=this._zrLayer.getMaptalks(),n=this._dispatchInteractAction.bind(this,t,i);[\"zoomend\",\"zooming\",\"zoomstart\",\"dragrotating\",\"pitch\",\"pitchend\",\"movestart\",\"moving\",\"moveend\",\"resize\",\"touchstart\",\"touchmove\",\"touchend\",\"animating\"].forEach((function(e){i.on(e,n)}))},render:function(e,t,r){this._zrLayer||this._initMaptalksLayer(e,r);var i=this._zrLayer.getMaptalks(),n=e.get(\"urlTemplate\"),a=i.getBaseLayer();n!==this._oldUrlTemplate&&(a?a.setOptions({urlTemplate:n,attribution:e.get(\"attribution\")}):(a=new maptalks.TileLayer(\"maptalks-echarts-gl-baselayer\",{urlTemplate:n,subdomains:[\"a\",\"b\",\"c\"],attribution:e.get(\"attribution\")}),i.setBaseLayer(a))),this._oldUrlTemplate=n,i.setCenter(e.get(\"center\")),i.setZoom(e.get(\"zoom\"),{animation:!1}),i.setPitch(e.get(\"pitch\")),i.setBearing(e.get(\"bearing\")),e.setMaptalks(i);var o=e.coordinateSystem;o.viewGL.scene.add(this._lightRoot),o.viewGL.add(this._groundMesh),this._updateGroundMesh(),this._sceneHelper.setScene(o.viewGL.scene),this._sceneHelper.updateLight(e),o.viewGL.setPostEffect(e.getModel(\"postEffect\"),r),o.viewGL.setTemporalSuperSampling(e.getModel(\"temporalSuperSampling\")),this._maptalks3DModel=e},afterRender:function(e,t,r,i){var n=i.renderer;this._sceneHelper.updateAmbientCubemap(n,e,r),this._sceneHelper.updateSkybox(n,e,r),e.coordinateSystem.viewGL.scene.traverse((function(e){e.material&&(e.material.define(\"fragment\",\"NORMAL_UP_AXIS\",2),e.material.define(\"fragment\",\"NORMAL_FRONT_AXIS\",1))}))},updateCamera:function(e,t,r,i){e.coordinateSystem.setCameraOption(i),this._updateGroundMesh(),r.getZr().refresh()},_dispatchInteractAction:function(e,t,r){var i;e.dispatchAction({type:\"maptalks3DChangeCamera\",pitch:t.getPitch(),zoom:(i=t.getResolution(),19-Math.log(i/Yu)/Math.LN2+1),center:t.getCenter().toArray(),bearing:t.getBearing(),maptalks3DId:this._maptalks3DModel&&this._maptalks3DModel.id})},_updateGroundMesh:function(){if(this._maptalks3DModel){var e=this._maptalks3DModel.coordinateSystem,t=e.dataToPoint(e.center);this._groundMesh.position.set(t[0],t[1],-.001);var r=new Ka.Plane(new Ka.Vector3(0,0,1),0),i=e.viewGL.camera.castRay(new Ka.Vector2(-1,-1)),n=e.viewGL.camera.castRay(new Ka.Vector2(1,1)),a=i.intersectPlane(r),o=n.intersectPlane(r),s=a.dist(o)/e.viewGL.rootNode.scale.x;this._groundMesh.scale.set(s,s,1)}},dispose:function(e,t){this._zrLayer&&this._zrLayer.dispose(),t.getZr().painter.delLayer(-1e3)}}),Yu=12756274*Math.PI/(256*Math.pow(2,20));function Ku(){zu.apply(this,arguments),this.maxPitch=85,this.zoomOffset=1}Ku.prototype=new zu,Ku.prototype.constructor=Ku,Ku.prototype.type=\"maptalks3D\";const Qu=Uu(\"maptalks3D\",Ku,(function(e){e.forEach((function(e){e.setCameraOption(e.model.getMaptalksCameraOption())}))}));(0,i.use)((function(e){e.registerComponentModel(Wu),e.registerComponentView(Zu),e.registerCoordinateSystem(\"maptalks3D\",Qu),e.registerAction({type:\"maptalks3DChangeCamera\",event:\"maptalks3dcamerachanged\",update:\"maptalks3D:updateCamera\"},(function(e,t){t.eachComponent({mainType:\"maptalks3D\",query:e},(function(t){t.setMaptalksCameraOption(e)}))}))}));var Ju=Po.vec3,$u=i.helper.dataStack.isDimensionStacked;function ec(e,t,r){for(var i=e.getDataExtent(t),n=e.getDataExtent(r),a=i[1]-i[0]||i[0],o=n[1]-n[0]||n[0],s=new Uint8Array(2500),l=0;l0&&d>0||c<0&&d<0)),m=[\"x\",\"y\",\"z\"].map((function(t){return e.coordDimToDataDim(t)[0]})),g=$u(r,m[2]),_=g?r.getCalculationInfo(\"stackResultDimension\"):m[2];r.each(m,(function(e,i,a,o){var s=r.get(_,o),l=g?s-a:p?0:f[0],h=t.dataToPoint([e,i,l]),u=t.dataToPoint([e,i,s]),c=Ju.dist(h,u),d=[0,u[1]\"+a.join(\"
\")}(a):i.format.encodeHTML(i.format.addCommas(a)),s=n.getName(t),l=Ih(n,t);i.util.isObject(l)&&l.colorStops&&(l=(l.colorStops[0]||{}).color),l=l||\"transparent\";var h=i.format.getTooltipMarker(l),u=e.name;return\"\\0-\"===u&&(u=\"\"),u=u?i.format.encodeHTML(u)+(r?\": \":\"
\"):\"\",r?h+u+o:u+h+(s?i.format.encodeHTML(s)+\": \"+o:o)}function sc(e,t,r){r=r||e.getSource();var n=t||i.getCoordinateSystemDimensions(e.get(\"coordinateSystem\"))||[\"x\",\"y\",\"z\"],a=i.helper.createDimensions(r,{dimensionsDefine:r.dimensionsDefine||e.get(\"dimensions\"),encodeDefine:r.encodeDefine||e.get(\"encode\"),coordDimensions:n.map((function(t){var r=e.getReferringComponents(t+\"Axis3D\").models[0];return{type:r&&\"category\"===r.get(\"type\")?\"ordinal\":\"float\",name:t}}))});\"cartesian3D\"===e.get(\"coordinateSystem\")&&a.forEach((function(t){if(n.indexOf(t.coordDim)>=0){var r=e.getReferringComponents(t.coordDim+\"Axis3D\").models[0];r&&\"category\"===r.get(\"type\")&&(t.ordinalMeta=r.getOrdinalMeta())}}));var o=i.helper.dataStack.enableDataStack(e,a,{byIndex:!0,stackedCoordDimension:\"z\"}),s=new i.List(a,e);return s.setCalculationInfo(o),s.initData(r),s}var lc=i.SeriesModel.extend({type:\"series.bar3D\",dependencies:[\"globe\"],visualStyleAccessPathvisu:\"itemStyle\",getInitialData:function(e,t){return sc(this)},getFormattedLabel:function(e,t,r,i){var n=ac.getFormattedLabel(this,e,t,r,i);return null==n&&(n=this.getData().get(\"z\",e)),n},formatTooltip:function(e){return oc(this,e)},defaultOption:{coordinateSystem:\"cartesian3D\",globeIndex:0,grid3DIndex:0,zlevel:-10,bevelSize:0,bevelSmoothness:2,onGridPlane:\"xy\",shading:\"color\",minHeight:0,itemStyle:{opacity:1},label:{show:!1,distance:2,textStyle:{fontSize:14,color:\"#000\",backgroundColor:\"rgba(255,255,255,0.7)\",padding:3,borderRadius:3}},emphasis:{label:{show:!0}},animationDurationUpdate:500}});i.util.merge(lc.prototype,Kl);const hc=lc;var uc,cc,dc,fc,pc,mc,gc,_c,vc=Po.vec3,yc=Po.mat3,xc=Vr.extend((function(){return{attributes:{position:new Vr.Attribute(\"position\",\"float\",3,\"POSITION\"),normal:new Vr.Attribute(\"normal\",\"float\",3,\"NORMAL\"),color:new Vr.Attribute(\"color\",\"float\",4,\"COLOR\"),prevPosition:new Vr.Attribute(\"prevPosition\",\"float\",3),prevNormal:new Vr.Attribute(\"prevNormal\",\"float\",3)},dynamic:!0,enableNormal:!1,bevelSize:1,bevelSegments:0,_dataIndices:null,_vertexOffset:0,_triangleOffset:0}}),{resetOffset:function(){this._vertexOffset=0,this._triangleOffset=0},setBarCount:function(e){var t=this.enableNormal,r=this.getBarVertexCount()*e,i=this.getBarTriangleCount()*e;this.vertexCount!==r&&(this.attributes.position.init(r),t?this.attributes.normal.init(r):this.attributes.normal.value=null,this.attributes.color.init(r)),this.triangleCount!==i&&(this.indices=r>65535?new Uint32Array(3*i):new Uint16Array(3*i),this._dataIndices=new Uint32Array(r))},getBarVertexCount:function(){var e=this.bevelSize>0?this.bevelSegments:0;return e>0?this._getBevelBarVertexCount(e):this.enableNormal?24:8},getBarTriangleCount:function(){var e=this.bevelSize>0?this.bevelSegments:0;return e>0?this._getBevelBarTriangleCount(e):12},_getBevelBarVertexCount:function(e){return 4*(e+1)*(e+1)*2},_getBevelBarTriangleCount:function(e){return(4*e+3+1)*(2*e+1)*2+4},setColor:function(e,t){for(var r=this.getBarVertexCount(),i=r*(e+1),n=r*e;n0&&this.bevelSegments>0)this._addBevelBar(e,c,m,g,this.bevelSize,this.bevelSegments,_);else{vc.copy(n,c),vc.normalize(n,n),vc.cross(a,m,n),vc.normalize(a,a),vc.cross(i,n,a),vc.normalize(a,a),vc.negate(o,i),vc.negate(s,n),vc.negate(l,a),t(h[0],e,i,g[0]/2),t(h[0],h[0],a,g[2]/2),t(h[1],e,i,g[0]/2),t(h[1],h[1],l,g[2]/2),t(h[2],e,o,g[0]/2),t(h[2],h[2],l,g[2]/2),t(h[3],e,o,g[0]/2),t(h[3],h[3],a,g[2]/2),t(r,e,n,g[1]),t(h[4],r,i,g[0]/2),t(h[4],h[4],a,g[2]/2),t(h[5],r,i,g[0]/2),t(h[5],h[5],l,g[2]/2),t(h[6],r,o,g[0]/2),t(h[6],h[6],l,g[2]/2),t(h[7],r,o,g[0]/2),t(h[7],h[7],a,g[2]/2);var x=this.attributes;if(this.enableNormal){u[0]=i,u[1]=o,u[2]=n,u[3]=s,u[4]=a,u[5]=l;for(var b=this._vertexOffset,w=0;w0&&(f++,u[3]<.99&&(p=!0))}})),o.geometry.setBarCount(f);var m=r.getLayout(\"orient\"),g=this._barIndexOfData=new Int32Array(r.count());f=0,r.each((function(e){if(r.hasValue(e)){var t=r.getItemLayout(e),i=t[0],n=t[1],o=t[2],s=4*e;u[0]=c[s++],u[1]=c[s++],u[2]=c[s++],u[3]=c[s++],u[3]>0&&(a._barMesh.geometry.addBar(i,n,m,o,u,e),g[e]=f++)}else g[e]=-1})),o.geometry.dirty(),o.geometry.updateBoundingBox();var _=o.material;_.transparent=p,_.depthMask=!p,o.geometry.sortTriangles=p,this._initHandler(e,t)},_initHandler:function(e,t){var r=e.getData(),i=this._barMesh,n=\"cartesian3D\"===e.coordinateSystem.type;i.seriesIndex=e.seriesIndex;var a=-1;i.off(\"mousemove\"),i.off(\"mouseout\"),i.on(\"mousemove\",(function(e){var o=i.geometry.getDataIndexOfVertex(e.triangle[0]);o!==a&&(this._downplay(a),this._highlight(o),this._labelsBuilder.updateLabels([o]),n&&t.dispatchAction({type:\"grid3DShowAxisPointer\",value:[r.get(\"x\",o),r.get(\"y\",o),r.get(\"z\",o,!0)]})),a=o,i.dataIndex=o}),this),i.on(\"mouseout\",(function(e){this._downplay(a),this._labelsBuilder.updateLabels(),a=-1,i.dataIndex=-1,n&&t.dispatchAction({type:\"grid3DHideAxisPointer\"})}),this)},_highlight:function(e){var t=this._data;if(t){var r=this._barIndexOfData[e];if(!(r<0)){var n=t.getItemModel(e).getModel(\"emphasis.itemStyle\"),a=n.get(\"color\"),o=n.get(\"opacity\");if(null==a){var s=Ih(t,e);a=i.color.lift(s,-.4)}null==o&&(o=Rh(t,e));var l=Ka.parseColor(a);l[3]*=o,this._barMesh.geometry.setColor(r,l),this._api.getZr().refresh()}}},_downplay:function(e){var t=this._data;if(t){var r=this._barIndexOfData[e];if(!(r<0)){var i=Ih(t,e),n=Rh(t,e),a=Ka.parseColor(i);a[3]*=n,this._barMesh.geometry.setColor(r,a),this._api.getZr().refresh()}}},highlight:function(e,t,r,i){this._toggleStatus(\"highlight\",e,t,r,i)},downplay:function(e,t,r,i){this._toggleStatus(\"downplay\",e,t,r,i)},_toggleStatus:function(e,t,r,n,a){var o=t.getData(),s=An(o,a),l=this;null!=s?i.util.each(ac.normalizeToArray(s),(function(t){\"highlight\"===e?this._highlight(t):this._downplay(t)}),this):o.each((function(t){\"highlight\"===e?l._highlight(t):l._downplay(t)}))},remove:function(){this.groupGL.removeAll()},dispose:function(){this._labelsBuilder.dispose(),this.groupGL.removeAll()}});(0,i.use)((function(e){e.registerChartView(Tc),e.registerSeriesModel(hc),nc(e),e.registerProcessor((function(e,t){e.eachSeriesByType(\"bar3d\",(function(e){var t=e.getData();t.filterSelf((function(e){return t.hasValue(e)}))}))}))}));const Sc=i.SeriesModel.extend({type:\"series.line3D\",dependencies:[\"grid3D\"],visualStyleAccessPath:\"lineStyle\",visualDrawType:\"stroke\",getInitialData:function(e,t){return sc(this)},formatTooltip:function(e){return oc(this,e)},defaultOption:{coordinateSystem:\"cartesian3D\",zlevel:-10,grid3DIndex:0,lineStyle:{width:2},animationDurationUpdate:500}});function Mc(e,t,r,i,n,a,o){if(0===n)return!1;var s,l=n;if(o>t+l&&o>i+l||oe+l&&a>r+l||a=0){var g=3*l,_=new vt(this._points[g],this._points[g+1],this._points[g+2]);a.push({dataIndex:l,point:_,pointWorld:_.clone(),target:this._line3DMesh,distance:this._camera.getWorldPosition().dist(_)})}},remove:function(){this.groupGL.removeAll()},dispose:function(){this.groupGL.removeAll()}});(0,i.use)((function(e){e.registerChartView(Ec),e.registerSeriesModel(Sc),e.registerLayout((function(e,t){e.eachSeriesByType(\"line3D\",(function(e){var t=e.getData(),r=e.coordinateSystem;if(r){if(\"cartesian3D\"!==r.type)return;var i=new Float32Array(3*t.count()),n=[],a=[],o=r.dimensions.map((function(t){return e.coordDimToDataDim(t)[0]}));r&&t.each(o,(function(e,t,o,s){n[0]=e,n[1]=t,n[2]=o,r.dataToPoint(n,a),i[3*s]=a[0],i[3*s+1]=a[1],i[3*s+2]=a[2]})),t.setLayout(\"points\",i)}}))}))}));const Cc=i.SeriesModel.extend({type:\"series.scatter3D\",dependencies:[\"globe\",\"grid3D\",\"geo3D\"],visualStyleAccessPath:\"itemStyle\",hasSymbolVisual:!0,getInitialData:function(e,t){return sc(this)},getFormattedLabel:function(e,t,r,i){var n=ac.getFormattedLabel(this,e,t,r,i);if(null==n){var a=this.getData(),o=a.dimensions[a.dimensions.length-1];n=a.get(o,e)}return n},formatTooltip:function(e){return oc(this,e)},defaultOption:{coordinateSystem:\"cartesian3D\",zlevel:-10,progressive:1e5,progressiveThreshold:1e5,grid3DIndex:0,globeIndex:0,symbol:\"circle\",symbolSize:10,blendMode:\"source-over\",label:{show:!1,position:\"right\",distance:5,textStyle:{fontSize:14,color:\"#000\",backgroundColor:\"rgba(255,255,255,0.7)\",padding:3,borderRadius:3}},itemStyle:{opacity:.8},emphasis:{label:{show:!0}},animationDurationUpdate:500}});function Dc(e,t,r){(t=t||document.createElement(\"canvas\")).width=e,t.height=e;var i=t.getContext(\"2d\");return r&&r(i),t}var Lc={getMarginByStyle:function(e){var t=e.minMargin||0,r=0;e.stroke&&\"none\"!==e.stroke&&(r=null==e.lineWidth?1:e.lineWidth);var i=e.shadowBlur||0,n=e.shadowOffsetX||0,a=e.shadowOffsetY||0,o={};return o.left=Math.max(r/2,-n+i,t),o.right=Math.max(r/2,n+i,t),o.top=Math.max(r/2,-a+i,t),o.bottom=Math.max(r/2,a+i,t),o},createSymbolSprite:function(e,t,r,n){var a=function(e,t,r,n){i.util.isArray(t)||(t=[t,t]);var a=Lc.getMarginByStyle(r,void 0),o=t[0]+a.left+a.right,s=t[1]+a.top+a.bottom,l=i.helper.createSymbol(e,0,0,t[0],t[1]),h=Math.max(o,s);l.x=a.left,l.y=a.top,o>s?l.y+=(h-s)/2:l.x+=(h-o)/2;var u=l.getBoundingRect();return l.x-=u.x,l.y-=u.y,l.setStyle(r),l.update(),l.__size=h,l}(e,t,r),o=Lc.getMarginByStyle(r);return{image:Dc(a.__size,n,(function(e){i.innerDrawElementOnCanvas(e,a)})),margin:o}},createSDFFromCanvas:function(e,t,r,i){return Dc(t,i,(function(t){var i=e.getContext(\"2d\").getImageData(0,0,e.width,e.height);t.putImageData(function(e,t,r){var i=t.width,n=t.height,a=e.canvas.width,o=e.canvas.height,s=i/a,l=n/o;function h(e){return e<128?1:-1}function u(e,a){var o=1/0;e=Math.floor(e*s);for(var u=(a=Math.floor(a*l))*i+e,c=h(t.data[4*u]),d=Math.max(a-r,0);d=2e4},doSortVertices:function(e,t){var r=this.indices,i=Oc.create();if(!r){r=this.indices=this.vertexCount>65535?new Uint32Array(this.vertexCount):new Uint16Array(this.vertexCount);for(var n=0;n.05);else for(n=0;n<3;n++)this._progressiveQuickSort(3*t+n);this.dirtyIndices()},_simpleSort:function(e){var t=this._zList,r=this.indices;function i(e,r){return t[r]-t[e]}e?Array.prototype.sort.call(r,i):Ch.sort(r,i,0,r.length-1)},_progressiveQuickSort:function(e){var t=this._zList,r=this.indices;this._quickSort=this._quickSort||new Ch,this._quickSort.step(r,(function(e,r){return t[r]-t[e]}),e)}};var Ic=Po.vec4;Ka.Shader.import(\"@export ecgl.sdfSprite.vertex\\n\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\nuniform float elapsedTime : 0;\\n\\nattribute vec3 position : POSITION;\\n\\n#ifdef VERTEX_SIZE\\nattribute float size;\\n#else\\nuniform float u_Size;\\n#endif\\n\\n#ifdef VERTEX_COLOR\\nattribute vec4 a_FillColor: COLOR;\\nvarying vec4 v_Color;\\n#endif\\n\\n#ifdef VERTEX_ANIMATION\\nattribute vec3 prevPosition;\\nattribute float prevSize;\\nuniform float percent : 1.0;\\n#endif\\n\\n\\n#ifdef POSITIONTEXTURE_ENABLED\\nuniform sampler2D positionTexture;\\n#endif\\n\\nvarying float v_Size;\\n\\nvoid main()\\n{\\n\\n#ifdef POSITIONTEXTURE_ENABLED\\n gl_Position = worldViewProjection * vec4(texture2D(positionTexture, position.xy).xy, -10.0, 1.0);\\n#else\\n\\n #ifdef VERTEX_ANIMATION\\n vec3 pos = mix(prevPosition, position, percent);\\n #else\\n vec3 pos = position;\\n #endif\\n gl_Position = worldViewProjection * vec4(pos, 1.0);\\n#endif\\n\\n#ifdef VERTEX_SIZE\\n#ifdef VERTEX_ANIMATION\\n v_Size = mix(prevSize, size, percent);\\n#else\\n v_Size = size;\\n#endif\\n#else\\n v_Size = u_Size;\\n#endif\\n\\n#ifdef VERTEX_COLOR\\n v_Color = a_FillColor;\\n #endif\\n\\n gl_PointSize = v_Size;\\n}\\n\\n@end\\n\\n@export ecgl.sdfSprite.fragment\\n\\nuniform vec4 color: [1, 1, 1, 1];\\nuniform vec4 strokeColor: [1, 1, 1, 1];\\nuniform float smoothing: 0.07;\\n\\nuniform float lineWidth: 0.0;\\n\\n#ifdef VERTEX_COLOR\\nvarying vec4 v_Color;\\n#endif\\n\\nvarying float v_Size;\\n\\nuniform sampler2D sprite;\\n\\n@import clay.util.srgb\\n\\nvoid main()\\n{\\n gl_FragColor = color;\\n\\n vec4 _strokeColor = strokeColor;\\n\\n#ifdef VERTEX_COLOR\\n gl_FragColor *= v_Color;\\n #endif\\n\\n#ifdef SPRITE_ENABLED\\n float d = texture2D(sprite, gl_PointCoord).r;\\n gl_FragColor.a *= smoothstep(0.5 - smoothing, 0.5 + smoothing, d);\\n\\n if (lineWidth > 0.0) {\\n float sLineWidth = lineWidth / 2.0;\\n\\n float outlineMaxValue0 = 0.5 + sLineWidth;\\n float outlineMaxValue1 = 0.5 + sLineWidth + smoothing;\\n float outlineMinValue0 = 0.5 - sLineWidth - smoothing;\\n float outlineMinValue1 = 0.5 - sLineWidth;\\n\\n if (d <= outlineMaxValue1 && d >= outlineMinValue0) {\\n float a = _strokeColor.a;\\n if (d <= outlineMinValue1) {\\n a = a * smoothstep(outlineMinValue0, outlineMinValue1, d);\\n }\\n else {\\n a = a * smoothstep(outlineMaxValue1, outlineMaxValue0, d);\\n }\\n gl_FragColor.rgb = mix(gl_FragColor.rgb * gl_FragColor.a, _strokeColor.rgb, a);\\n gl_FragColor.a = gl_FragColor.a * (1.0 - a) + a;\\n }\\n }\\n#endif\\n\\n#ifdef SRGB_DECODE\\n gl_FragColor = sRGBToLinear(gl_FragColor);\\n#endif\\n}\\n@end\");const Rc=Ka.Mesh.extend((function(){var e=new Ka.Geometry({dynamic:!0,attributes:{color:new Ka.Geometry.Attribute(\"color\",\"float\",4,\"COLOR\"),position:new Ka.Geometry.Attribute(\"position\",\"float\",3,\"POSITION\"),size:new Ka.Geometry.Attribute(\"size\",\"float\",1),prevPosition:new Ka.Geometry.Attribute(\"prevPosition\",\"float\",3),prevSize:new Ka.Geometry.Attribute(\"prevSize\",\"float\",1)}});Object.assign(e,Nc);var t=new Ka.Material({shader:Ka.createShader(\"ecgl.sdfSprite\"),transparent:!0,depthMask:!1});t.enableTexture(\"sprite\"),t.define(\"both\",\"VERTEX_COLOR\"),t.define(\"both\",\"VERTEX_SIZE\");var r=new Ka.Texture2D({image:document.createElement(\"canvas\"),flipY:!1});return t.set(\"sprite\",r),e.pick=this._pick.bind(this),{geometry:e,material:t,mode:Ka.Mesh.POINTS,sizeScale:1}}),{_pick:function(e,t,r,i,n,a){var o=this._positionNDC;if(o)for(var s=r.viewport,l=2/s.width,h=2/s.height,u=this.geometry.vertexCount-1;u>=0;u--){var c,d=o[2*(c=this.geometry.indices?this.geometry.indices[u]:u)],f=o[2*c+1],p=this.geometry.attributes.size.get(c)/this.sizeScale/2;if(e>d-p*l&&ef-p*h&&t2?(p=this._updateSymbolSprite(e,d,u,c),s.enableTexture(\"sprite\")):s.disableTexture(\"sprite\"),h.position.init(n-i);var m=[];if(f){s.undefine(\"VERTEX_SIZE\"),s.undefine(\"VERTEX_COLOR\");var g=function(e){const t=e.getVisual(\"style\");if(t)return t[e.getVisual(\"drawType\")]}(o),_=function(e){return e.getVisual(\"style\").opacity}(o);Ka.parseColor(g,m),m[3]*=_,s.set({color:m,u_Size:u.maxSize*this._sizeScale})}else s.set({color:[1,1,1,1]}),s.define(\"VERTEX_SIZE\"),s.define(\"VERTEX_COLOR\"),h.size.init(n-i),h.color.init(n-i),this._originalOpacity=new Float32Array(n-i);for(var v=o.getLayout(\"points\"),y=h.position.value,x=0;x1?(o[0]=r.maxSize,o[1]=r.maxSize/r.aspect):(o[1]=r.maxSize,o[0]=r.maxSize*r.aspect),o[0]=o[0]||1,o[1]=o[1]||1,this._symbolType===r.type&&(a=o,(n=this._symbolSize)&&a&&n[0]===a[0]&&n[1]===a[1])&&this._lineWidth===t.lineWidth||(Pc.createSymbolSprite(r.type,o,{fill:\"#fff\",lineWidth:t.lineWidth,stroke:\"transparent\",shadowColor:\"transparent\",minMargin:Math.min(o[0]/2,10)},this._spriteImageCanvas),Pc.createSDFFromCanvas(this._spriteImageCanvas,Math.min(this._spriteImageCanvas.width,32),20,this._mesh.material.get(\"sprite\").image),this._symbolType=r.type,this._symbolSize=o,this._lineWidth=t.lineWidth),this._spriteImageCanvas.width/r.maxSize*i},_updateMaterial:function(e,t){var r=\"lighter\"===e.get(\"blendMode\")?Ka.additiveBlend:null,i=this._mesh.material;i.blend=r,i.set(\"lineWidth\",t.lineWidth/20);var n=Ka.parseColor(t.stroke);i.set(\"strokeColor\",n),i.transparent=!0,i.depthMask=!1,i.depthTest=!this.is2D,i.sortVertices=!this.is2D},_updateLabelBuilder:function(e,t,r){var i=e.getData(),n=this._mesh.geometry,a=n.attributes.position.value,o=(t=this._startDataIndex,this._mesh.sizeScale);this._labelsBuilder.updateData(i,t,r),this._labelsBuilder.getLabelPosition=function(e,r,i){var n=3*(e-t);return[a[n],a[n+1],a[n+2]]},this._labelsBuilder.getLabelDistance=function(e,r,i){return n.attributes.size.get(e-t)/o/2+i},this._labelsBuilder.updateLabels()},_updateAnimation:function(e){Ka.updateVertexAnimation([[\"prevPosition\",\"position\"],[\"prevSize\",\"size\"]],this._prevMesh,this._mesh,e)},_updateHandler:function(e,t,r){var i,n=e.getData(),a=this._mesh,o=this,s=-1,l=e.coordinateSystem&&\"cartesian3D\"===e.coordinateSystem.type;l&&(i=e.coordinateSystem.model),a.seriesIndex=e.seriesIndex,a.off(\"mousemove\"),a.off(\"mouseout\"),a.on(\"mousemove\",(function(t){var h=t.vertexIndex+o._startDataIndex;h!==s&&(this.highlightOnMouseover&&(this.downplay(n,s),this.highlight(n,h),this._labelsBuilder.updateLabels([h])),l&&r.dispatchAction({type:\"grid3DShowAxisPointer\",value:[n.get(e.coordDimToDataDim(\"x\")[0],h),n.get(e.coordDimToDataDim(\"y\")[0],h),n.get(e.coordDimToDataDim(\"z\")[0],h)],grid3DIndex:i.componentIndex})),a.dataIndex=h,s=h}),this),a.on(\"mouseout\",(function(e){var t=e.vertexIndex+o._startDataIndex;this.highlightOnMouseover&&(this.downplay(n,t),this._labelsBuilder.updateLabels()),s=-1,a.dataIndex=-1,l&&r.dispatchAction({type:\"grid3DHideAxisPointer\",grid3DIndex:i.componentIndex})}),this)},updateLayout:function(e,t,r){var i=e.getData();if(this._mesh){var n=this._mesh.geometry.attributes.position.value,a=i.getLayout(\"points\");if(this.is2D)for(var o=0;othis._endDataIndex||tthis._endDataIndex||t 1.0 || v_Percent < 0.0) {\\n discard;\\n }\\n\\n float fade = v_Percent;\\n\\n#ifdef SRGB_DECODE\\n gl_FragColor = sRGBToLinear(color * v_Color);\\n#else\\n gl_FragColor = color * v_Color;\\n#endif\\n\\n @import ecgl.common.wireframe.fragmentMain\\n\\n if (v_Percent > (1.0 - v_SpotPercent)) {\\n gl_FragColor.rgb *= spotIntensity;\\n }\\n\\n gl_FragColor.a *= fade;\\n}\\n\\n@end\");const ad=Ka.Mesh.extend((function(){var e=new Ka.Material({shader:new Ka.Shader(Ka.Shader.source(\"ecgl.trail2.vertex\"),Ka.Shader.source(\"ecgl.trail2.fragment\")),transparent:!0,depthMask:!1}),t=new Ro({dynamic:!0});return t.createAttribute(\"dist\",\"float\",1),t.createAttribute(\"distAll\",\"float\",1),t.createAttribute(\"start\",\"float\",1),{geometry:t,material:e,culling:!1,$ignorePicking:!0}}),{updateData:function(e,t,r){var i=e.hostModel,n=this.geometry,a=i.getModel(\"effect\"),o=a.get(\"trailWidth\")*t.getDevicePixelRatio(),s=a.get(\"trailLength\"),l=i.get(\"effect.constantSpeed\"),h=1e3*i.get(\"effect.period\"),u=null!=l;u?this.material.set(\"speed\",l/1e3):this.material.set(\"period\",h),this.material[u?\"define\":\"undefine\"](\"vertex\",\"CONSTANT_SPEED\");var c=i.get(\"polyline\");n.trailLength=s,this.material.set(\"trailLength\",s),n.resetOffset(),[\"position\",\"positionPrev\",\"positionNext\"].forEach((function(e){n.attributes[e].value=r.attributes[e].value})),[\"dist\",\"distAll\",\"start\",\"offset\",\"color\"].forEach((function(e){n.attributes[e].init(n.vertexCount)})),n.indices=r.indices;var d=[],f=a.get(\"trailColor\"),p=a.get(\"trailOpacity\"),m=null!=f,g=null!=p;this.updateWorldTransform();var _=this.worldTransform.x.len(),v=this.worldTransform.y.len(),y=this.worldTransform.z.len(),x=0,b=0;e.each((function(t){var i=e.getItemLayout(t),a=g?p:Rh(e,t),s=Ih(e,t);null==a&&(a=1),(d=Ka.parseColor(m?f:s,d))[3]*=a;for(var l=c?r.getPolylineVertexCount(i):r.getCubicCurveVertexCount(i[0],i[1],i[2],i[3]),w=0,T=[],S=[],M=x;Mx&&(w+=nd.dist(T,S)),n.attributes.dist.set(M,w),nd.copy(S,T);b=Math.max(b,w);var A=Math.random()*(u?w:h);for(M=x;M0?1:-1)*o/2),n.attributes.color.set(M,d);x+=l})),this.material.set(\"spotSize\",.1*b*s),this.material.set(\"spotIntensity\",a.get(\"spotIntensity\")),n.dirty()},setAnimationTime:function(e){this.material.set(\"time\",e)}});Ka.Shader.import(is);const od=i.ChartView.extend({type:\"lines3D\",__ecgl__:!0,init:function(e,t){this.groupGL=new Ka.Node,this._meshLinesMaterial=new Ka.Material({shader:Ka.createShader(\"ecgl.meshLines3D\"),transparent:!0,depthMask:!1}),this._linesMesh=new Ka.Mesh({geometry:new Ro,material:this._meshLinesMaterial,$ignorePicking:!0}),this._trailMesh=new ad},render:function(e,t,r){this.groupGL.add(this._linesMesh);var i=e.coordinateSystem,n=e.getData();if(i&&i.viewGL){i.viewGL.add(this.groupGL),this._updateLines(e,t,r);var a=i.viewGL.isLinearSpace()?\"define\":\"undefine\";this._linesMesh.material[a](\"fragment\",\"SRGB_DECODE\"),this._trailMesh.material[a](\"fragment\",\"SRGB_DECODE\")}var o=this._trailMesh;if(o.stopAnimation(),e.get(\"effect.show\")){this.groupGL.add(o),o.updateData(n,r,this._linesMesh.geometry),o.__time=o.__time||0;var s=36e5;this._curveEffectsAnimator=o.animate(\"\",{loop:!0}).when(s,{__time:s}).during((function(){o.setAnimationTime(o.__time)})).start()}else this.groupGL.remove(o),this._curveEffectsAnimator=null;this._linesMesh.material.blend=this._trailMesh.material.blend=\"lighter\"===e.get(\"blendMode\")?Ka.additiveBlend:null},pauseEffect:function(){this._curveEffectsAnimator&&this._curveEffectsAnimator.pause()},resumeEffect:function(){this._curveEffectsAnimator&&this._curveEffectsAnimator.resume()},toggleEffect:function(){var e=this._curveEffectsAnimator;e&&(e.isPaused()?e.resume():e.pause())},_updateLines:function(e,t,r){var i=e.getData(),n=e.coordinateSystem,a=this._linesMesh.geometry,o=e.get(\"polyline\");a.expandLine=!0;var s=function(e){return null!=e.radius?e.radius:null!=e.size?Math.max(e.size[0],e.size[1],e.size[2]):100}(n);a.segmentScale=s/20;var l=\"lineStyle.width\".split(\".\"),h=r.getDevicePixelRatio(),u=0;i.each((function(e){var t=i.getItemModel(e).get(l);null==t&&(t=1),i.setItemVisual(e,\"lineWidth\",t),u=Math.max(t,u)})),a.useNativeLine=!1;var c=0,d=0;i.each((function(e){var t=i.getItemLayout(e);o?(c+=a.getPolylineVertexCount(t),d+=a.getPolylineTriangleCount(t)):(c+=a.getCubicCurveVertexCount(t[0],t[1],t[2],t[3]),d+=a.getCubicCurveTriangleCount(t[0],t[1],t[2],t[3]))})),a.setVertexCount(c),a.setTriangleCount(d),a.resetOffset();var f=[];i.each((function(e){var t=i.getItemLayout(e),r=Ih(i,e),n=Rh(i,e),s=i.getItemVisual(e,\"lineWidth\")*h;null==n&&(n=1),(f=Ka.parseColor(r,f))[3]*=n,o?a.addPolyline(t,f,s):a.addCubicCurve(t[0],t[1],t[2],t[3],f,s)})),a.dirty()},remove:function(){this.groupGL.removeAll()},dispose:function(){this.groupGL.removeAll()}});function sd(e,t){for(var r=[],i=0;i0;this._updateSurfaceMesh(this._surfaceMesh,e,u,f);var p=this._surfaceMesh.material;f?(p.define(\"WIREFRAME_QUAD\"),p.set(\"wireframeLineWidth\",d),p.set(\"wireframeLineColor\",Ka.parseColor(c.get(\"lineStyle.color\")))):p.undefine(\"WIREFRAME_QUAD\"),this._initHandler(e,r),this._updateAnimation(e)},_updateAnimation:function(e){Ka.updateVertexAnimation([[\"prevPosition\",\"position\"],[\"prevNormal\",\"normal\"]],this._prevSurfaceMesh,this._surfaceMesh,e)},_createSurfaceMesh:function(){var e=new Ka.Mesh({geometry:new Ka.Geometry({dynamic:!0,sortTriangles:!0}),shadowDepthMaterial:new Ka.Material({shader:new Ka.Shader(Ka.Shader.source(\"ecgl.sm.depth.vertex\"),Ka.Shader.source(\"ecgl.sm.depth.fragment\"))}),culling:!1,renderOrder:10,renderNormal:!0});return e.geometry.createAttribute(\"barycentric\",\"float\",4),e.geometry.createAttribute(\"prevPosition\",\"float\",3),e.geometry.createAttribute(\"prevNormal\",\"float\",3),Object.assign(e.geometry,Nh),e},_initHandler:function(e,t){var r=e.getData(),i=this._surfaceMesh,n=e.coordinateSystem;i.seriesIndex=e.seriesIndex;var a=-1;i.off(\"mousemove\"),i.off(\"mouseout\"),i.on(\"mousemove\",(function(e){var o=function(e,t){for(var r=1/0,n=-1,a=[],o=0;o=0){var s=[];i.geometry.attributes.position.get(o,s);for(var l=n.pointToData(s),h=1/0,u=-1,c=[],d=0;d65535?Uint32Array:Uint16Array)((p-1)*(m-1)*6),w=function(e,t,r){r[1]=e*m+t,r[0]=e*m+t+1,r[3]=(e+1)*m+t+1,r[2]=(e+1)*m+t},T=!1;if(l){var S=[],M=[],A=0;g?u.init(n.vertexCount):u.value=null;for(var E=[[],[],[]],C=[],D=[],L=fd.create(),P=function(e,t,r){var i=3*t;return r[0]=e[i],r[1]=e[i+1],r[2]=e[i+2],r},O=new Float32Array(o.length),N=new Float32Array(o.length/3*4),I=0;I0;){if(Math.floor(s/u)===s/u)return[u,s/u];u--}return[u=Math.floor(Math.sqrt(s)),u]},dispose:function(){this.groupGL.removeAll()},remove:function(){this.groupGL.removeAll()}});function md(e,t){for(var r=[],i=0;i=0&&e.call(t,r[n],n)},e.prototype.eachEdge=function(e,t){for(var r=this.edges,i=r.length,n=0;n=0&&r[n].node1.dataIndex>=0&&r[n].node2.dataIndex>=0&&e.call(t,r[n],n)},e.prototype.breadthFirstTraverse=function(e,t,r,i){if(t instanceof Ad||(t=this._nodesMap[Sd(t)]),t){for(var n=\"out\"===r?\"outEdges\":\"in\"===r?\"inEdges\":\"edges\",a=0;a=0&&r.node2.dataIndex>=0})),n=0,a=i.length;n=0&&this[e][t].setItemVisual(this.dataIndex,r,i)},getVisual:function(r){return this[e][t].getItemVisual(this.dataIndex,r)},setLayout:function(r,i){this.dataIndex>=0&&this[e][t].setItemLayout(this.dataIndex,r,i)},getLayout:function(){return this[e][t].getItemLayout(this.dataIndex)},getGraphicEl:function(){return this[e][t].getItemGraphicEl(this.dataIndex)},getRawIndex:function(){return this[e][t].getRawIndex(this.dataIndex)}}}_a(Ad,Cd(\"hostGraph\",\"data\")),_a(Ed,Cd(\"hostGraph\",\"edgeData\"));const Dd=Md;var Ld=_o();function Pd(e,t){if(Ld(this).mainData===this){var r=ga({},Ld(this).datas);r[this.dataType]=t,Bd(t,r,e)}else Fd(t,this.dataType,Ld(this).mainData,e);return t}function Od(e,t){return e.struct&&e.struct.update(),t}function Nd(e,t){return ya(Ld(t).datas,(function(r,i){r!==t&&Fd(r.cloneShallow(),i,t,e)})),t}function Id(e){var t=Ld(this).mainData;return null==e||null==t?t:Ld(t).datas[e]}function Rd(){var e=Ld(this).mainData;return null==e?[{data:e}]:xa(ba(Ld(e).datas),(function(t){return{type:t,data:Ld(e).datas[t]}}))}function Bd(e,t,r){Ld(e).datas={},ya(t,(function(t,i){Fd(t,i,e,r)}))}function Fd(e,t,r,i){Ld(r).datas[t]=e,Ld(e).mainData=r,e.dataType=t,i.struct&&(e[i.structAttr]=i.struct,i.struct[i.datasAttr[t]]=e),e.getLinkedData=Id,e.getLinkedDataAll=Rd}var zd=i.SeriesModel.extend({type:\"series.graphGL\",visualStyleAccessPath:\"itemStyle\",hasSymbolVisual:!0,init:function(e){zd.superApply(this,\"init\",arguments),this.legendDataProvider=function(){return this._categoriesData},this._updateCategoriesData()},mergeOption:function(e){zd.superApply(this,\"mergeOption\",arguments),this._updateCategoriesData()},getFormattedLabel:function(e,t,r,i){var n=ac.getFormattedLabel(this,e,t,r,i);if(null==n){var a=this.getData(),o=a.dimensions[a.dimensions.length-1];n=a.get(o,e)}return n},getInitialData:function(e,t){var r=e.edges||e.links||[],n=e.data||e.nodes||[],a=this;if(n&&r)return function(e,t,r,n,a){for(var o=new Dd(!0),s=0;s \"+p)),c++)}var m=i.helper.createDimensions(e,{coordDimensions:[\"value\"]});(l=new i.List(m,r)).initData(e);var g,_,v,y=new i.List([\"value\"],r);return y.initData(u,h),a&&a(l,y),_=(g={mainData:l,struct:o,structAttr:\"graph\",datas:{node:l,edge:y},datasAttr:{node:\"data\",edge:\"edgeData\"}}).mainData,(v=g.datas)||(v={main:_},g.datasAttr={main:\"data\"}),g.datas=g.mainData=null,Bd(_,v,g),ya(v,(function(e){ya(_.TRANSFERABLE_METHODS,(function(t){e.wrapMethod(t,wa(Pd,g))}))})),_.wrapMethod(\"cloneShallow\",wa(Nd,g)),ya(_.CHANGABLE_METHODS,(function(e){_.wrapMethod(e,wa(Od,g))})),function(e,t){if(!e)throw new Error(void 0)}(v[_.dataType]===_),o.update(),o}(n,r,this,0,(function(e,r){e.wrapMethod(\"getItemModel\",(function(e){const t=a._categoriesModels[e.getShallow(\"category\")];return t&&(t.parentModel=e.parentModel,e.parentModel=t),e}));const i=t.getModel([]).getModel;function n(e,t){const r=i.call(this,e,t);return r.resolveParentPath=o,r}function o(e){if(e&&(\"label\"===e[0]||\"label\"===e[1])){const t=e.slice();return\"label\"===e[0]?t[0]=\"edgeLabel\":\"label\"===e[1]&&(t[1]=\"edgeLabel\"),t}return e}r.wrapMethod(\"getItemModel\",(function(e){return e.resolveParentPath=o,e.getModel=n,e}))})).data},getGraph:function(){return this.getData().graph},getEdgeData:function(){return this.getGraph().edgeData},getCategoriesData:function(){return this._categoriesData},formatTooltip:function(e,t,r){if(\"edge\"===r){var n=this.getData(),a=this.getDataParams(e,r),o=n.graph.getEdgeByIndex(e),s=n.getName(o.node1.dataIndex),l=n.getName(o.node2.dataIndex),h=[];return null!=s&&h.push(s),null!=l&&h.push(l),h=i.format.encodeHTML(h.join(\" > \")),a.value&&(h+=\" : \"+i.format.encodeHTML(a.value)),h}return zd.superApply(this,\"formatTooltip\",arguments)},_updateCategoriesData:function(){var e=(this.option.categories||[]).map((function(e){return null!=e.value?e:Object.assign({value:0},e)})),t=new i.List([\"value\"],this);t.initData(e),this._categoriesData=t,this._categoriesModels=t.mapArray((function(e){return t.getItemModel(e,!0)}))},setView:function(e){null!=e.zoom&&(this.option.zoom=e.zoom),null!=e.offset&&(this.option.offset=e.offset)},setNodePosition:function(e){for(var t=0;t65535?this.indices instanceof Uint16Array&&(this.indices=new Uint32Array(this.indices)):this.indices instanceof Uint32Array&&(this.indices=new Uint16Array(this.indices)))},setTriangleCount:function(e){this.triangleCount!==e&&(this.indices=0===e?null:this.vertexCount>65535?new Uint32Array(3*e):new Uint16Array(3*e))},_getCubicCurveApproxStep:function(e,t,r,i){return 1/(Ud.dist(e,t)+Ud.dist(r,t)+Ud.dist(i,r)+1)*this.segmentScale},getCubicCurveVertexCount:function(e,t,r,i){var n=this._getCubicCurveApproxStep(e,t,r,i),a=Math.ceil(1/n);return this.useNativeLine?2*a:2*a+2},getCubicCurveTriangleCount:function(e,t,r,i){var n=this._getCubicCurveApproxStep(e,t,r,i),a=Math.ceil(1/n);return this.useNativeLine?0:2*a},getLineVertexCount:function(){return this.getPolylineVertexCount(kd)},getLineTriangleCount:function(){return this.getPolylineTriangleCount(kd)},getPolylineVertexCount:function(e){var t;return t=\"number\"==typeof e?e:\"number\"!=typeof e[0]?e.length:e.length/2,this.useNativeLine?2*(t-1):2*(t-1)+2},getPolylineTriangleCount:function(e){var t;return t=\"number\"==typeof e?e:\"number\"!=typeof e[0]?e.length:e.length/2,this.useNativeLine?0:2*(t-1)},addCubicCurve:function(e,t,r,i,n,a){null==a&&(a=1);var o=e[0],s=e[1],l=t[0],h=t[1],u=r[0],c=r[1],d=i[0],f=i[1],p=this._getCubicCurveApproxStep(e,t,r,i),m=p*p,g=m*p,_=3*p,v=3*m,y=6*m,x=6*g,b=o-2*l+u,w=s-2*h+c,T=3*(l-u)-o+d,S=3*(h-c)-s+f,M=o,A=s,E=(l-o)*_+b*v+T*g,C=(h-s)*_+w*v+S*g,D=b*y+T*x,L=w*y+S*x,P=T*x,O=S*x,N=0,I=0,R=Math.ceil(1/p),B=new Float32Array(3*(R+1)),F=(B=[],0);for(I=0;I1&&(M=E>0?Math.min(M,d):Math.max(M,d),A=C>0?Math.min(A,f):Math.max(A,f));this.addPolyline(B,n,a)},addLine:function(e,t,r,i){this.addPolyline([e,t],r,i)},addPolyline:function(){var e=Ud.create(),t=Ud.create(),r=Ud.create(),i=Ud.create(),n=[],a=[],o=[];return function(s,l,h,u,c){if(s.length){var d=\"number\"!=typeof s[0];if(null==c&&(c=d?s.length:s.length/2),!(c<2)){null==u&&(u=0),null==h&&(h=1),this._itemVertexOffsets.push(this._vertexOffset);for(var f,p=d?\"number\"!=typeof l[0]:l.length/4===c,m=this.attributes.position,g=this.attributes.color,_=this.attributes.offset,v=this.attributes.normal,y=this.indices,x=this._vertexOffset,b=0;b1&&(m.copy(x,x-1),g.copy(x,x-1),x++);else{var S;if(b0){Ud.sub(e,n,o),Ud.sub(t,a,n),Ud.normalize(e,e),Ud.normalize(t,t),Ud.add(i,e,t),Ud.normalize(i,i);var M=h/2*Math.min(1/Ud.dot(e,i),2);r[0]=-i[1],r[1]=i[0],S=M}else Ud.sub(e,a,n),Ud.normalize(e,e),r[0]=-e[1],r[1]=e[0],S=h/2;else Ud.sub(e,n,o),Ud.normalize(e,e),r[0]=-e[1],r[1]=e[0],S=h/2;v.set(x,r),v.set(x+1,r),_.set(x,S),_.set(x+1,-S),Ud.copy(o,n),m.set(x,n),m.set(x+1,n),g.set(x,f),g.set(x+1,f),x+=2}if(this.useNativeLine)g.set(x,f),m.set(x,n),x++;else if(b>0){var A=3*this._faceOffset;(y=this.indices)[A]=x-4,y[A+1]=x-3,y[A+2]=x-2,y[A+3]=x-3,y[A+4]=x-1,y[A+5]=x-2,this._faceOffset+=2}}this._vertexOffset=x}}}}(),setItemColor:function(e,t){for(var r=this._itemVertexOffsets[e],i=e 0.0) {\\n float factor = 0.0;\\n if (preventOverlap) {\\n float d = sqrt(d2);\\n d = d - n0.w - n1.w;\\n if (d > 0.0) {\\n factor = scaling * n0.z * n1.z / (d * d);\\n }\\n else if (d < 0.0) {\\n factor = scaling * 100.0 * n0.z * n1.z;\\n }\\n }\\n else {\\n factor = scaling * n0.z * n1.z / d2;\\n }\\n force += dir * factor;\\n }\\n }\\n\\n vec2 dir = gravityCenter - n0.xy;\\n float d = 1.0;\\n if (!strongGravityMode) {\\n d = length(dir);\\n }\\n\\n force += dir * n0.z * gravity / (d + 1.0);\\n\\n gl_FragColor = vec4(force, 0.0, 1.0);\\n}\\n@end\\n\\n@export ecgl.forceAtlas2.updateEdgeAttraction.vertex\\n\\nattribute vec2 node1;\\nattribute vec2 node2;\\nattribute float weight;\\n\\nuniform sampler2D positionTex;\\nuniform float edgeWeightInfluence;\\nuniform bool preventOverlap;\\nuniform bool linLogMode;\\n\\nuniform vec2 windowSize: WINDOW_SIZE;\\n\\nvarying vec2 v_Force;\\n\\nvoid main() {\\n\\n vec4 n0 = texture2D(positionTex, node1);\\n vec4 n1 = texture2D(positionTex, node2);\\n\\n vec2 dir = n1.xy - n0.xy;\\n float d = length(dir);\\n float w;\\n if (edgeWeightInfluence == 0.0) {\\n w = 1.0;\\n }\\n else if (edgeWeightInfluence == 1.0) {\\n w = weight;\\n }\\n else {\\n w = pow(weight, edgeWeightInfluence);\\n }\\n vec2 offset = vec2(1.0 / windowSize.x, 1.0 / windowSize.y);\\n vec2 scale = vec2((windowSize.x - 1.0) / windowSize.x, (windowSize.y - 1.0) / windowSize.y);\\n vec2 pos = node1 * scale * 2.0 - 1.0;\\n gl_Position = vec4(pos + offset, 0.0, 1.0);\\n gl_PointSize = 1.0;\\n\\n float factor;\\n if (preventOverlap) {\\n d = d - n1.w - n0.w;\\n }\\n if (d <= 0.0) {\\n v_Force = vec2(0.0);\\n return;\\n }\\n\\n if (linLogMode) {\\n factor = w * log(d) / d;\\n }\\n else {\\n factor = w;\\n }\\n v_Force = dir * factor;\\n}\\n@end\\n\\n@export ecgl.forceAtlas2.updateEdgeAttraction.fragment\\n\\nvarying vec2 v_Force;\\n\\nvoid main() {\\n gl_FragColor = vec4(v_Force, 0.0, 0.0);\\n}\\n@end\\n\\n@export ecgl.forceAtlas2.calcWeightedSum.vertex\\n\\nattribute vec2 node;\\n\\nvarying vec2 v_NodeUv;\\n\\nvoid main() {\\n\\n v_NodeUv = node;\\n gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\\n gl_PointSize = 1.0;\\n}\\n@end\\n\\n@export ecgl.forceAtlas2.calcWeightedSum.fragment\\n\\nvarying vec2 v_NodeUv;\\n\\nuniform sampler2D positionTex;\\nuniform sampler2D forceTex;\\nuniform sampler2D forcePrevTex;\\n\\nvoid main() {\\n vec2 force = texture2D(forceTex, v_NodeUv).rg;\\n vec2 forcePrev = texture2D(forcePrevTex, v_NodeUv).rg;\\n\\n float mass = texture2D(positionTex, v_NodeUv).z;\\n float swing = length(force - forcePrev) * mass;\\n float traction = length(force + forcePrev) * 0.5 * mass;\\n\\n gl_FragColor = vec4(swing, traction, 0.0, 0.0);\\n}\\n@end\\n\\n@export ecgl.forceAtlas2.calcGlobalSpeed\\n\\nuniform sampler2D globalSpeedPrevTex;\\nuniform sampler2D weightedSumTex;\\nuniform float jitterTolerence;\\n\\nvoid main() {\\n vec2 weightedSum = texture2D(weightedSumTex, vec2(0.5)).xy;\\n float prevGlobalSpeed = texture2D(globalSpeedPrevTex, vec2(0.5)).x;\\n float globalSpeed = jitterTolerence * jitterTolerence\\n * weightedSum.y / weightedSum.x;\\n if (prevGlobalSpeed > 0.0) {\\n globalSpeed = min(globalSpeed / prevGlobalSpeed, 1.5) * prevGlobalSpeed;\\n }\\n gl_FragColor = vec4(globalSpeed, 0.0, 0.0, 1.0);\\n}\\n@end\\n\\n@export ecgl.forceAtlas2.updatePosition\\n\\nuniform sampler2D forceTex;\\nuniform sampler2D forcePrevTex;\\nuniform sampler2D positionTex;\\nuniform sampler2D globalSpeedTex;\\n\\nvarying vec2 v_Texcoord;\\n\\nvoid main() {\\n vec2 force = texture2D(forceTex, v_Texcoord).xy;\\n vec2 forcePrev = texture2D(forcePrevTex, v_Texcoord).xy;\\n vec4 node = texture2D(positionTex, v_Texcoord);\\n\\n float globalSpeed = texture2D(globalSpeedTex, vec2(0.5)).r;\\n float swing = length(force - forcePrev);\\n float speed = 0.1 * globalSpeed / (0.1 + globalSpeed * sqrt(swing));\\n\\n float df = length(force);\\n if (df > 0.0) {\\n speed = min(df * speed, 10.0) / df;\\n\\n gl_FragColor = vec4(node.xy + speed * force, node.zw);\\n }\\n else {\\n gl_FragColor = node;\\n }\\n}\\n@end\\n\\n@export ecgl.forceAtlas2.edges.vertex\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\n\\nattribute vec2 node;\\nattribute vec4 a_Color : COLOR;\\nvarying vec4 v_Color;\\n\\nuniform sampler2D positionTex;\\n\\nvoid main()\\n{\\n gl_Position = worldViewProjection * vec4(\\n texture2D(positionTex, node).xy, -10.0, 1.0\\n );\\n v_Color = a_Color;\\n}\\n@end\\n\\n@export ecgl.forceAtlas2.edges.fragment\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\nvarying vec4 v_Color;\\nvoid main() {\\n gl_FragColor = color * v_Color;\\n}\\n@end\");var Wd={repulsionByDegree:!0,linLogMode:!1,strongGravityMode:!1,gravity:1,scaling:1,edgeWeightInfluence:1,jitterTolerence:.1,preventOverlap:!1,dissuadeHubs:!1,gravityCenter:null};function jd(e){var t={type:Ka.Texture.FLOAT,minFilter:Ka.Texture.NEAREST,magFilter:Ka.Texture.NEAREST};this._positionSourceTex=new Ka.Texture2D(t),this._positionSourceTex.flipY=!1,this._positionTex=new Ka.Texture2D(t),this._positionPrevTex=new Ka.Texture2D(t),this._forceTex=new Ka.Texture2D(t),this._forcePrevTex=new Ka.Texture2D(t),this._weightedSumTex=new Ka.Texture2D(t),this._weightedSumTex.width=this._weightedSumTex.height=1,this._globalSpeedTex=new Ka.Texture2D(t),this._globalSpeedPrevTex=new Ka.Texture2D(t),this._globalSpeedTex.width=this._globalSpeedTex.height=1,this._globalSpeedPrevTex.width=this._globalSpeedPrevTex.height=1,this._nodeRepulsionPass=new pn({fragment:Ka.Shader.source(\"ecgl.forceAtlas2.updateNodeRepulsion\")}),this._positionPass=new pn({fragment:Ka.Shader.source(\"ecgl.forceAtlas2.updatePosition\")}),this._globalSpeedPass=new pn({fragment:Ka.Shader.source(\"ecgl.forceAtlas2.calcGlobalSpeed\")}),this._copyPass=new pn({fragment:Ka.Shader.source(\"clay.compositor.output\")});var r=function(e){e.blendEquation(e.FUNC_ADD),e.blendFunc(e.ONE,e.ONE)};this._edgeForceMesh=new Ka.Mesh({geometry:new Ka.Geometry({attributes:{node1:new Ka.Geometry.Attribute(\"node1\",\"float\",2),node2:new Ka.Geometry.Attribute(\"node2\",\"float\",2),weight:new Ka.Geometry.Attribute(\"weight\",\"float\",1)},dynamic:!0,mainAttribute:\"node1\"}),material:new Ka.Material({transparent:!0,shader:Ka.createShader(\"ecgl.forceAtlas2.updateEdgeAttraction\"),blend:r,depthMask:!1,depthText:!1}),mode:Ka.Mesh.POINTS}),this._weightedSumMesh=new Ka.Mesh({geometry:new Ka.Geometry({attributes:{node:new Ka.Geometry.Attribute(\"node\",\"float\",2)},dynamic:!0,mainAttribute:\"node\"}),material:new Ka.Material({transparent:!0,shader:Ka.createShader(\"ecgl.forceAtlas2.calcWeightedSum\"),blend:r,depthMask:!1,depthText:!1}),mode:Ka.Mesh.POINTS}),this._framebuffer=new zi({depthBuffer:!1}),this._dummyCamera=new Ka.OrthographicCamera({left:-1,right:1,top:1,bottom:-1,near:0,far:100}),this._globalSpeed=0}jd.prototype.updateOption=function(e){for(var t in Wd)this[t]=Wd[t];var r=this._nodes.length;if(this.jitterTolerence=r>5e4?10:r>5e3?1:.1,this.scaling=r>100?2:10,e)for(var t in Wd)null!=e[t]&&(this[t]=e[t]);if(this.repulsionByDegree)for(var i=this._positionSourceTex.pixels,n=0;ne},jd.prototype._swapTexture=function(){var e=this._positionPrevTex;this._positionPrevTex=this._positionTex,this._positionTex=e,e=this._forcePrevTex,this._forcePrevTex=this._forceTex,this._forceTex=e,e=this._globalSpeedPrevTex,this._globalSpeedPrevTex=this._globalSpeedTex,this._globalSpeedTex=e},jd.prototype._initFromSource=function(e){this._framebuffer.attach(this._positionPrevTex),this._framebuffer.bind(e),this._copyPass.setUniform(\"texture\",this._positionSourceTex),this._copyPass.render(e),e.gl.clearColor(0,0,0,0),this._framebuffer.attach(this._forcePrevTex),e.gl.clear(e.gl.COLOR_BUFFER_BIT),this._framebuffer.attach(this._globalSpeedPrevTex),e.gl.clear(e.gl.COLOR_BUFFER_BIT),this._framebuffer.unbind(e)},jd.prototype._resize=function(e,t){[\"_positionSourceTex\",\"_positionTex\",\"_positionPrevTex\",\"_forceTex\",\"_forcePrevTex\"].forEach((function(r){this[r].width=e,this[r].height=t,this[r].dirty()}),this)},jd.prototype.dispose=function(e){this._framebuffer.dispose(e),this._copyPass.dispose(e),this._nodeRepulsionPass.dispose(e),this._positionPass.dispose(e),this._globalSpeedPass.dispose(e),this._edgeForceMesh.geometry.dispose(e),this._weightedSumMesh.geometry.dispose(e),this._positionSourceTex.dispose(e),this._positionTex.dispose(e),this._positionPrevTex.dispose(e),this._forceTex.dispose(e),this._forcePrevTex.dispose(e),this._weightedSumTex.dispose(e),this._globalSpeedTex.dispose(e),this._globalSpeedPrevTex.dispose(e)};const Xd=jd;var qd=function(){var e=function(){return new Float32Array(2)},t=function(e,t){var r=t[0]-e[0],i=t[1]-e[1];return Math.sqrt(r*r+i*i)},r=function(e){var t=e[0],r=e[1];return Math.sqrt(t*t+r*r)},i=function(e,t,r,i){return e[0]=t[0]+r[0]*i,e[1]=t[1]+r[1]*i,e},n=function(e,t,r){return e[0]=t[0]+r[0],e[1]=t[1]+r[1],e},a=function(e,t,r){return e[0]=t[0]-r[0],e[1]=t[1]-r[1],e},o=function(e,t,r){return e[0]=t,e[1]=r,e};function s(){this.subRegions=[],this.nSubRegions=0,this.node=null,this.mass=0,this.centerOfMass=null,this.bbox=new Float32Array(4),this.size=0}var l=s.prototype;function h(){this.position=new Float32Array(2),this.force=e(),this.forcePrev=e(),this.mass=1,this.inDegree=0,this.outDegree=0}function u(e,t){this.source=e,this.target=t,this.weight=1}function c(){this.autoSettings=!0,this.barnesHutOptimize=!0,this.barnesHutTheta=1.5,this.repulsionByDegree=!0,this.linLogMode=!1,this.strongGravityMode=!1,this.gravity=1,this.scaling=1,this.edgeWeightInfluence=1,this.jitterTolerence=.1,this.preventOverlap=!1,this.dissuadeHubs=!1,this.rootRegion=new s,this.rootRegion.centerOfMass=e(),this.nodes=[],this.edges=[],this.bbox=new Float32Array(4),this.gravityCenter=null,this._massArr=null,this._swingingArr=null,this._sizeArr=null,this._globalSpeed=0}l.beforeUpdate=function(){for(var e=0;e=e&&this.bbox[1]<=t&&this.bbox[3]>=t},l.setBBox=function(e,t,r,i){this.bbox[0]=e,this.bbox[1]=t,this.bbox[2]=r,this.bbox[3]=i,this.size=(r-e+i-t)/2},l._newSubRegion=function(){var e=this.subRegions[this.nSubRegions];return e||(e=new s,this.subRegions[this.nSubRegions]=e),this.nSubRegions++,e},l._addNodeToSubRegion=function(e){var t=this.findSubRegion(e.position[0],e.position[1]),r=this.bbox;if(!t){var i=(r[0]+r[2])/2,n=(r[1]+r[3])/2,a=(r[2]-r[0])/2,o=(r[3]-r[1])/2,s=e.position[0]>=i?1:0,l=e.position[1]>=n?1:0;(t=this._newSubRegion()).setBBox(s*a+r[0],l*o+r[1],(s+1)*a+r[0],(l+1)*o+r[1])}t.addNode(e)},l._updateCenterOfMass=function(e){null==this.centerOfMass&&(this.centerOfMass=new Float32Array(2));var t=this.centerOfMass[0]*this.mass,r=this.centerOfMass[1]*this.mass;t+=e.position[0]*e.mass,r+=e.position[1]*e.mass,this.mass+=e.mass,this.centerOfMass[0]=t/this.mass,this.centerOfMass[1]=r/this.mass};var d=c.prototype;d.initNodes=function(e,t,r){var i=t.length;this.nodes.length=0;for(var n=void 0!==r,a=0;a0&&(this.strongGravityMode?this.applyNodeStrongGravity(c):this.applyNodeGravity(c))}for(h=0;h0&&(_=Math.min(_/this._globalSpeed,1.5)*this._globalSpeed),this._globalSpeed=_,h=0;h0&&(y=Math.min(x*y,10)/x,i(u.position,u.position,u.force,y))}},d.applyRegionToNodeRepulsion=function(){var t=e();return function(e,r){if(e.node)this.applyNodeToNodeRepulsion(e.node,r,!0);else{a(t,r.position,e.centerOfMass);var n=t[0]*t[0]+t[1]*t[1];if(n>this.barnesHutTheta*e.size*e.size){var o=this.scaling*r.mass*e.mass/n;i(r.force,r.force,t,o)}else for(var s=0;s0)s=this.scaling*e.mass*r.mass/(l*l);else{if(!(l<0))return;s=100*this.scaling*e.mass*r.mass}}else s=this.scaling*e.mass*r.mass/o;i(e.force,e.force,t,s),i(r.force,r.force,t,-s)}}}}(),d.applyEdgeAttraction=function(){var t=e();return function(e){var n=e.source,o=e.target;a(t,n.position,o.position);var s,l,h=r(t);s=0===this.edgeWeightInfluence?1:1===this.edgeWeightInfluence?e.weight:Math.pow(e.weight,this.edgeWeightInfluence),this.preventOverlap&&(h=h-n.size-o.size)<=0||(l=this.linLogMode?-s*Math.log(h+1)/(h+1):-s,i(n.force,n.force,t,l),i(o.force,o.force,t,-l))}}(),d.applyNodeGravity=function(){var t=e();return function(e){a(t,this.gravityCenter,e.position);var n=r(t);i(e.force,e.force,t,this.gravity*e.mass/(n+1))}}(),d.applyNodeStrongGravity=function(){var t=e();return function(e){a(t,this.gravityCenter,e.position),i(e.force,e.force,t,this.gravity*e.mass)}}(),d.updateBBox=function(){for(var e=1/0,t=1/0,r=-1/0,i=-1/0,n=0;n5e4?10:a>5e3?1:.1,t.scaling=a>100?2:10,t.barnesHutOptimize=a>1e3,e)for(var r in Zd)null!=e[r]&&(t[r]=e[r]);if(!t.gravityCenter){for(var o=[1/0,1/0],s=[-1/0,-1/0],l=0;le},Yd.prototype.getNodePosition=function(e,t){if(t||(t=new Float32Array(2*this._nodes.length)),this._positionArr)for(var r=0;r0?1.1:.9,a=Math.max(Math.min(this._zoom*n,this.maxZoom),this.minZoom);n=a/this._zoom;var o=this._convertPos(r,i),s=(o.x-this._dx)*(n-1),l=(o.y-this._dy)*(n-1);this._dx-=s,this._dy-=l,this._zoom=a,this._needsUpdate=!0}}},dispose:function(){var e=this.zr;e.off(\"mousedown\",this._mouseDownHandler),e.off(\"mousemove\",this._mouseMoveHandler),e.off(\"mouseup\",this._mouseUpHandler),e.off(\"mousewheel\",this._mouseWheelHandler),e.off(\"globalout\",this._mouseUpHandler),e.animation.off(\"frame\",this._update)}});var Jd=Po.vec2;Ka.Shader.import(\"@export ecgl.lines2D.vertex\\n\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\n\\nattribute vec2 position: POSITION;\\nattribute vec4 a_Color : COLOR;\\nvarying vec4 v_Color;\\n\\n#ifdef POSITIONTEXTURE_ENABLED\\nuniform sampler2D positionTexture;\\n#endif\\n\\nvoid main()\\n{\\n gl_Position = worldViewProjection * vec4(position, -10.0, 1.0);\\n\\n v_Color = a_Color;\\n}\\n\\n@end\\n\\n@export ecgl.lines2D.fragment\\n\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\n\\nvarying vec4 v_Color;\\n\\nvoid main()\\n{\\n gl_FragColor = color * v_Color;\\n}\\n@end\\n\\n\\n@export ecgl.meshLines2D.vertex\\n\\nattribute vec2 position: POSITION;\\nattribute vec2 normal;\\nattribute float offset;\\nattribute vec4 a_Color : COLOR;\\n\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\nuniform vec4 viewport : VIEWPORT;\\n\\nvarying vec4 v_Color;\\nvarying float v_Miter;\\n\\nvoid main()\\n{\\n vec4 p2 = worldViewProjection * vec4(position + normal, -10.0, 1.0);\\n gl_Position = worldViewProjection * vec4(position, -10.0, 1.0);\\n\\n p2.xy /= p2.w;\\n gl_Position.xy /= gl_Position.w;\\n\\n vec2 N = normalize(p2.xy - gl_Position.xy);\\n gl_Position.xy += N * offset / viewport.zw * 2.0;\\n\\n gl_Position.xy *= gl_Position.w;\\n\\n v_Color = a_Color;\\n}\\n@end\\n\\n\\n@export ecgl.meshLines2D.fragment\\n\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\n\\nvarying vec4 v_Color;\\nvarying float v_Miter;\\n\\nvoid main()\\n{\\n gl_FragColor = color * v_Color;\\n}\\n\\n@end\");var $d=1;const ef=i.ChartView.extend({type:\"graphGL\",__ecgl__:!0,init:function(e,t){this.groupGL=new Ka.Node,this.viewGL=new Il(\"orthographic\"),this.viewGL.camera.left=this.viewGL.camera.right=0,this.viewGL.add(this.groupGL),this._pointsBuilder=new Fc(!0,t),this._forceEdgesMesh=new Ka.Mesh({material:new Ka.Material({shader:Ka.createShader(\"ecgl.forceAtlas2.edges\"),transparent:!0,depthMask:!1,depthTest:!1}),$ignorePicking:!0,geometry:new Ka.Geometry({attributes:{node:new Ka.Geometry.Attribute(\"node\",\"float\",2),color:new Ka.Geometry.Attribute(\"color\",\"float\",4,\"COLOR\")},dynamic:!0,mainAttribute:\"node\"}),renderOrder:-1,mode:Ka.Mesh.LINES}),this._edgesMesh=new Ka.Mesh({material:new Ka.Material({shader:Ka.createShader(\"ecgl.meshLines2D\"),transparent:!0,depthMask:!1,depthTest:!1}),$ignorePicking:!0,geometry:new Hd({useNativeLine:!1,dynamic:!0}),renderOrder:-1,culling:!1}),this._layoutId=0,this._control=new Qd({zr:t.getZr(),viewGL:this.viewGL}),this._control.setTarget(this.groupGL),this._control.init(),this._clickHandler=this._clickHandler.bind(this)},render:function(e,t,r){this.groupGL.add(this._pointsBuilder.rootNode),this._model=e,this._api=r,this._initLayout(e,t,r),this._pointsBuilder.update(e,t,r),this._forceLayoutInstance instanceof Xd||this.groupGL.remove(this._forceEdgesMesh),this._updateCamera(e,r),this._control.off(\"update\"),this._control.on(\"update\",(function(){r.dispatchAction({type:\"graphGLRoam\",seriesId:e.id,zoom:this._control.getZoom(),offset:this._control.getOffset()}),this._pointsBuilder.updateView(this.viewGL.camera)}),this),this._control.setZoom(Mn(e.get(\"zoom\"),1)),this._control.setOffset(e.get(\"offset\")||[0,0]);var i=this._pointsBuilder.getPointsMesh();if(i.off(\"mousemove\",this._mousemoveHandler),i.off(\"mouseout\",this._mouseOutHandler,this),r.getZr().off(\"click\",this._clickHandler),this._pointsBuilder.highlightOnMouseover=!0,e.get(\"focusNodeAdjacency\")){var n=e.get(\"focusNodeAdjacencyOn\");\"click\"===n?r.getZr().on(\"click\",this._clickHandler):\"mouseover\"===n&&(i.on(\"mousemove\",this._mousemoveHandler,this),i.on(\"mouseout\",this._mouseOutHandler,this),this._pointsBuilder.highlightOnMouseover=!1)}this._lastMouseOverDataIndex=-1},_clickHandler:function(e){if(!this._layouting){var t=this._pointsBuilder.getPointsMesh().dataIndex;t>=0?this._api.dispatchAction({type:\"graphGLFocusNodeAdjacency\",seriesId:this._model.id,dataIndex:t}):this._api.dispatchAction({type:\"graphGLUnfocusNodeAdjacency\",seriesId:this._model.id})}},_mousemoveHandler:function(e){if(!this._layouting){var t=this._pointsBuilder.getPointsMesh().dataIndex;t>=0?t!==this._lastMouseOverDataIndex&&this._api.dispatchAction({type:\"graphGLFocusNodeAdjacency\",seriesId:this._model.id,dataIndex:t}):this._mouseOutHandler(e),this._lastMouseOverDataIndex=t}},_mouseOutHandler:function(e){this._layouting||(this._api.dispatchAction({type:\"graphGLUnfocusNodeAdjacency\",seriesId:this._model.id}),this._lastMouseOverDataIndex=-1)},_updateForceEdgesGeometry:function(e,t){var r=this._forceEdgesMesh.geometry,i=t.getEdgeData(),n=0,a=this._forceLayoutInstance,o=2*i.count();r.attributes.node.init(o),r.attributes.color.init(o),i.each((function(t){var o=e[t];r.attributes.node.set(n,a.getNodeUV(o.node1)),r.attributes.node.set(n+1,a.getNodeUV(o.node2));var s=Ih(i,o.dataIndex),l=Ka.parseColor(s);l[3]*=Mn(Rh(i,o.dataIndex),1),r.attributes.color.set(n,l),r.attributes.color.set(n+1,l),n+=2})),r.dirty()},_updateMeshLinesGeometry:function(){var e=this._model.getEdgeData(),t=this._edgesMesh.geometry,r=(e=this._model.getEdgeData(),this._model.getData().getLayout(\"points\"));t.resetOffset(),t.setVertexCount(e.count()*t.getLineVertexCount()),t.setTriangleCount(e.count()*t.getLineTriangleCount());var i=[],n=[],a=[\"lineStyle\",\"width\"];this._originalEdgeColors=new Float32Array(4*e.count()),this._edgeIndicesMap=new Float32Array(e.count()),e.each((function(o){var s=e.graph.getEdgeByIndex(o),l=2*s.node1.dataIndex,h=2*s.node2.dataIndex;i[0]=r[l],i[1]=r[l+1],n[0]=r[h],n[1]=r[h+1];var u=Ih(e,s.dataIndex),c=Ka.parseColor(u);c[3]*=Mn(Rh(e,s.dataIndex),1);var d=e.getItemModel(s.dataIndex),f=Mn(d.get(a),1)*this._api.getDevicePixelRatio();t.addLine(i,n,c,f);for(var p=0;p<4;p++)this._originalEdgeColors[4*s.dataIndex+p]=c[p];this._edgeIndicesMap[s.dataIndex]=o}),this),t.dirty()},_updateForceNodesGeometry:function(e){for(var t=this._pointsBuilder.getPointsMesh(),r=[],i=0;i=f&&(l._syncNodePosition(e),d=0),r.getZr().refresh(),Qa((function(){p(t)}))}))};Qa((function(){l._forceLayoutInstanceToDispose&&(l._forceLayoutInstanceToDispose.dispose(n.layer.renderer),l._forceLayoutInstanceToDispose=null),p(h)})),this._layouting=!0}}},stopLayout:function(e,t,r,i){i&&null!=i.from&&i.from!==this.uid||(this._layoutId=0,this.groupGL.remove(this._forceEdgesMesh),this.groupGL.add(this._edgesMesh),this._forceLayoutInstance&&this.viewGL.layer&&(i&&i.beforeLayout||(this._syncNodePosition(e),this._updateAfterLayout(e,t,r)),this._api.getZr().refresh(),this._layouting=!1))},_syncNodePosition:function(e){var t=this._forceLayoutInstance.getNodePosition(this.viewGL.layer.renderer);e.getData().setLayout(\"points\",t),e.setNodePosition(t)},_updateAfterLayout:function(e,t,r){this._updateMeshLinesGeometry(),this._pointsBuilder.removePositionTexture(),this._pointsBuilder.updateLayout(e,t,r),this._pointsBuilder.updateView(this.viewGL.camera),this._pointsBuilder.updateLabels(),this._pointsBuilder.showLabels()},focusNodeAdjacency:function(e,t,r,i){var n=this._model.getData();this._downplayAll();var a=i.dataIndex,o=n.graph,s=[],l=o.getNodeByIndex(a);s.push(l),l.edges.forEach((function(e){e.dataIndex<0||(e.node1!==l&&s.push(e.node1),e.node2!==l&&s.push(e.node2))}),this),this._pointsBuilder.fadeOutAll(.05),this._fadeOutEdgesAll(.05),s.forEach((function(e){this._pointsBuilder.highlight(n,e.dataIndex)}),this),this._pointsBuilder.updateLabels(s.map((function(e){return e.dataIndex})));var h=[];l.edges.forEach((function(e){e.dataIndex>=0&&(this._highlightEdge(e.dataIndex),h.push(e))}),this),this._focusNodes=s,this._focusEdges=h},unfocusNodeAdjacency:function(e,t,r,i){this._downplayAll(),this._pointsBuilder.fadeInAll(),this._fadeInEdgesAll(),this._pointsBuilder.updateLabels()},_highlightEdge:function(e){var t=this._model.getEdgeData().getItemModel(e),r=Ka.parseColor(t.get(\"emphasis.lineStyle.color\")||t.get(\"lineStyle.color\")),i=Mn(t.get(\"emphasis.lineStyle.opacity\"),t.get(\"lineStyle.opacity\"),1);r[3]*=i,this._edgesMesh.geometry.setItemColor(this._edgeIndicesMap[e],r)},_downplayAll:function(){this._focusNodes&&this._focusNodes.forEach((function(e){this._pointsBuilder.downplay(this._model.getData(),e.dataIndex)}),this),this._focusEdges&&this._focusEdges.forEach((function(e){this._downplayEdge(e.dataIndex)}),this)},_downplayEdge:function(e){var t=this._getColor(e,[]);this._edgesMesh.geometry.setItemColor(this._edgeIndicesMap[e],t)},_setEdgeFade:(tf=[],function(e,t){this._getColor(e,tf),tf[3]*=t,this._edgesMesh.geometry.setItemColor(this._edgeIndicesMap[e],tf)}),_getColor:function(e,t){for(var r=0;r<4;r++)t[r]=this._originalEdgeColors[4*e+r];return t},_fadeOutEdgesAll:function(e){this._model.getData().graph.eachEdge((function(t){this._setEdgeFade(t.dataIndex,e)}),this)},_fadeInEdgesAll:function(){this._fadeOutEdgesAll(1)},_updateCamera:function(e,t){this.viewGL.setViewport(0,0,t.getWidth(),t.getHeight(),t.getDevicePixelRatio());for(var r=this.viewGL.camera,i=e.getData().getLayout(\"points\"),n=Jd.create(1/0,1/0),a=Jd.create(-1/0,-1/0),o=[],s=0;sr.left&&hr.top)){var u=Math.max(a[0]-n[0],10),c=u/t.getWidth()*t.getHeight();u*=1.4,c*=1.4,n[0]-=.2*u,r.left=n[0],r.top=l-c/2,r.bottom=l+c/2,r.right=u+n[0],r.near=0,r.far=100}},dispose:function(){var e=this.viewGL.layer.renderer;this._forceLayoutInstance&&this._forceLayoutInstance.dispose(e),this.groupGL.removeAll(),this._layoutId=-1,this._pointsBuilder.dispose()},remove:function(){this.groupGL.removeAll(),this._control.dispose()}});var tf;function rf(e){return e instanceof Array||(e=[e,e]),e}(0,i.use)((function(e){function t(){}e.registerChartView(ef),e.registerSeriesModel(Gd),e.registerVisual((function(e){const t={};e.eachSeriesByType(\"graphGL\",(function(e){var r=e.getCategoriesData(),n=e.getData(),a={};r.each((function(i){var n=r.getName(i);a[\"ec-\"+n]=i;var o=r.getItemModel(i),s=o.getModel(\"itemStyle\").getItemStyle();s.fill||(s.fill=e.getColorFromPalette(n,t)),r.setItemVisual(i,\"style\",s);var l=[\"symbol\",\"symbolSize\",\"symbolKeepAspect\"];for(let e=0;e65535?new Uint32Array(3*i):new Uint16Array(3*i))},addLine:function(e){var t=this._vertexOffset;this.attributes.position.set(t,[e[0],e[1],1]),this.attributes.position.set(t+1,[e[0],e[1],-1]),this.attributes.position.set(t+2,[e[0],e[1],2]),this.attributes.position.set(t+3,[e[0],e[1],-2]),this.setTriangleIndices(this._faceOffset++,[t,t+1,t+2]),this.setTriangleIndices(this._faceOffset++,[t+1,t+2,t+3]),this._vertexOffset+=4}});Xe.import(\"@export ecgl.vfParticle.particle.fragment\\n\\nuniform sampler2D particleTexture;\\nuniform sampler2D spawnTexture;\\nuniform sampler2D velocityTexture;\\n\\nuniform float deltaTime;\\nuniform float elapsedTime;\\n\\nuniform float speedScaling : 1.0;\\n\\nuniform vec2 textureSize;\\nuniform vec4 region : [0, 0, 1, 1];\\nuniform float firstFrameTime;\\n\\nvarying vec2 v_Texcoord;\\n\\n\\nvoid main()\\n{\\n vec4 p = texture2D(particleTexture, v_Texcoord);\\n bool spawn = false;\\n if (p.w <= 0.0) {\\n p = texture2D(spawnTexture, fract(v_Texcoord + elapsedTime / 10.0));\\n p.w -= firstFrameTime;\\n spawn = true;\\n }\\n vec2 v = texture2D(velocityTexture, fract(p.xy * region.zw + region.xy)).xy;\\n v = (v - 0.5) * 2.0;\\n p.z = length(v);\\n p.xy += v * deltaTime / 10.0 * speedScaling;\\n p.w -= deltaTime;\\n\\n if (spawn || p.xy != fract(p.xy)) {\\n p.z = 0.0;\\n }\\n p.xy = fract(p.xy);\\n\\n gl_FragColor = p;\\n}\\n@end\\n\\n@export ecgl.vfParticle.renderPoints.vertex\\n\\n#define PI 3.1415926\\n\\nattribute vec2 texcoord : TEXCOORD_0;\\n\\nuniform sampler2D particleTexture;\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\n\\nuniform float size : 1.0;\\n\\nvarying float v_Mag;\\nvarying vec2 v_Uv;\\n\\nvoid main()\\n{\\n vec4 p = texture2D(particleTexture, texcoord);\\n\\n if (p.w > 0.0 && p.z > 1e-5) {\\n gl_Position = worldViewProjection * vec4(p.xy * 2.0 - 1.0, 0.0, 1.0);\\n }\\n else {\\n gl_Position = vec4(100000.0, 100000.0, 100000.0, 1.0);\\n }\\n\\n v_Mag = p.z;\\n v_Uv = p.xy;\\n\\n gl_PointSize = size;\\n}\\n\\n@end\\n\\n@export ecgl.vfParticle.renderPoints.fragment\\n\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\nuniform sampler2D gradientTexture;\\nuniform sampler2D colorTexture;\\nuniform sampler2D spriteTexture;\\n\\nvarying float v_Mag;\\nvarying vec2 v_Uv;\\n\\nvoid main()\\n{\\n gl_FragColor = color;\\n#ifdef SPRITETEXTURE_ENABLED\\n gl_FragColor *= texture2D(spriteTexture, gl_PointCoord);\\n if (color.a == 0.0) {\\n discard;\\n }\\n#endif\\n#ifdef GRADIENTTEXTURE_ENABLED\\n gl_FragColor *= texture2D(gradientTexture, vec2(v_Mag, 0.5));\\n#endif\\n#ifdef COLORTEXTURE_ENABLED\\n gl_FragColor *= texture2D(colorTexture, v_Uv);\\n#endif\\n}\\n\\n@end\\n\\n@export ecgl.vfParticle.renderLines.vertex\\n\\n#define PI 3.1415926\\n\\nattribute vec3 position : POSITION;\\n\\nuniform sampler2D particleTexture;\\nuniform sampler2D prevParticleTexture;\\n\\nuniform float size : 1.0;\\nuniform vec4 vp: VIEWPORT;\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\n\\nvarying float v_Mag;\\nvarying vec2 v_Uv;\\n\\n@import clay.util.rand\\n\\nvoid main()\\n{\\n vec4 p = texture2D(particleTexture, position.xy);\\n vec4 p2 = texture2D(prevParticleTexture, position.xy);\\n\\n p.xy = p.xy * 2.0 - 1.0;\\n p2.xy = p2.xy * 2.0 - 1.0;\\n\\n if (p.w > 0.0 && p.z > 1e-5) {\\n vec2 dir = normalize(p.xy - p2.xy);\\n vec2 norm = vec2(dir.y / vp.z, -dir.x / vp.w) * sign(position.z) * size;\\n if (abs(position.z) == 2.0) {\\n gl_Position = vec4(p.xy + norm, 0.0, 1.0);\\n v_Uv = p.xy;\\n v_Mag = p.z;\\n }\\n else {\\n gl_Position = vec4(p2.xy + norm, 0.0, 1.0);\\n v_Mag = p2.z;\\n v_Uv = p2.xy;\\n }\\n gl_Position = worldViewProjection * gl_Position;\\n }\\n else {\\n gl_Position = vec4(100000.0, 100000.0, 100000.0, 1.0);\\n }\\n}\\n\\n@end\\n\\n@export ecgl.vfParticle.renderLines.fragment\\n\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\nuniform sampler2D gradientTexture;\\nuniform sampler2D colorTexture;\\n\\nvarying float v_Mag;\\nvarying vec2 v_Uv;\\n\\nvoid main()\\n{\\n gl_FragColor = color;\\n #ifdef GRADIENTTEXTURE_ENABLED\\n gl_FragColor *= texture2D(gradientTexture, vec2(v_Mag, 0.5));\\n#endif\\n#ifdef COLORTEXTURE_ENABLED\\n gl_FragColor *= texture2D(colorTexture, v_Uv);\\n#endif\\n}\\n\\n@end\\n\");var of=function(){this.motionBlurFactor=.99,this.vectorFieldTexture=new Dr({type:wr.FLOAT,flipY:!1}),this.particleLife=[5,20],this._particleType=\"point\",this._particleSize=1,this.particleColor=[1,1,1,1],this.particleSpeedScaling=1,this._thisFrameTexture=null,this._particlePass=null,this._spawnTexture=null,this._particleTexture0=null,this._particleTexture1=null,this._particlePointsMesh=null,this._surfaceFrameBuffer=null,this._elapsedTime=0,this._scene=null,this._camera=null,this._lastFrameTexture=null,this._supersampling=1,this._downsampleTextures=[],this._width=512,this._height=512,this.init()};of.prototype={constructor:of,init:function(){var e={type:wr.FLOAT,minFilter:wr.NEAREST,magFilter:wr.NEAREST,useMipmap:!1};this._spawnTexture=new Dr(e),this._particleTexture0=new Dr(e),this._particleTexture1=new Dr(e),this._frameBuffer=new zi({depthBuffer:!1}),this._particlePass=new pn({fragment:Xe.source(\"ecgl.vfParticle.particle.fragment\")}),this._particlePass.setUniform(\"velocityTexture\",this.vectorFieldTexture),this._particlePass.setUniform(\"spawnTexture\",this._spawnTexture),this._downsamplePass=new pn({fragment:Xe.source(\"clay.compositor.downsample\")});var t=new Sr({renderOrder:10,material:new le({shader:new Xe(Xe.source(\"ecgl.vfParticle.renderPoints.vertex\"),Xe.source(\"ecgl.vfParticle.renderPoints.fragment\"))}),mode:Sr.POINTS,geometry:new Vr({dynamic:!0,mainAttribute:\"texcoord0\"})}),r=new Sr({renderOrder:10,material:new le({shader:new Xe(Xe.source(\"ecgl.vfParticle.renderLines.vertex\"),Xe.source(\"ecgl.vfParticle.renderLines.fragment\"))}),geometry:new af,culling:!1}),i=new Sr({material:new le({shader:new Xe(Xe.source(\"ecgl.color.vertex\"),Xe.source(\"ecgl.color.fragment\"))}),geometry:new ki});i.material.enableTexture(\"diffuseMap\"),this._particlePointsMesh=t,this._particleLinesMesh=r,this._lastFrameFullQuadMesh=i,this._camera=new un,this._thisFrameTexture=new Dr,this._lastFrameTexture=new Dr},setParticleDensity:function(e,t){for(var r=new Float32Array(e*t*4),i=0,n=this.particleLife,a=0;a0?e[e.length-1]:this._lastFrameTexture},setRegion:function(e){this._particlePass.setUniform(\"region\",e)},resize:function(e,t){this._lastFrameTexture.width=e*this._supersampling,this._lastFrameTexture.height=t*this._supersampling,this._thisFrameTexture.width=e*this._supersampling,this._thisFrameTexture.height=t*this._supersampling,this._width=e,this._height=t},setParticleSize:function(e){var t=this._getParticleMesh();if(e<=2)return t.material.disableTexture(\"spriteTexture\"),void(t.material.transparent=!1);this._spriteTexture||(this._spriteTexture=new Dr),this._spriteTexture.image&&this._spriteTexture.image.width===e||(this._spriteTexture.image=function(e){var t=document.createElement(\"canvas\");t.width=t.height=e;var r=t.getContext(\"2d\");return r.fillStyle=\"#fff\",r.arc(e/2,e/2,e/2,0,2*Math.PI),r.fill(),t}(e),this._spriteTexture.dirty()),t.material.transparent=!0,t.material.enableTexture(\"spriteTexture\"),t.material.set(\"spriteTexture\",this._spriteTexture),this._particleSize=e},setGradientTexture:function(e){var t=this._getParticleMesh().material;t[e?\"enableTexture\":\"disableTexture\"](\"gradientTexture\"),t.setUniform(\"gradientTexture\",e)},setColorTextureImage:function(e,t){this._getParticleMesh().material.setTextureImage(\"colorTexture\",e,t,{flipY:!0})},setParticleType:function(e){this._particleType=e},clearFrame:function(e){var t=this._frameBuffer;t.attach(this._lastFrameTexture),t.bind(e),e.gl.clear(e.gl.DEPTH_BUFFER_BIT|e.gl.COLOR_BUFFER_BIT),t.unbind(e)},setSupersampling:function(e){this._supersampling=e,this.resize(this._width,this._height)},_updateDownsampleTextures:function(e,t){for(var r=this._downsampleTextures,i=Math.max(Math.floor(Math.log(this._supersampling/t.getDevicePixelRatio())/Math.log(2)),0),n=2,a=this._width*this._supersampling,o=this._height*this._supersampling,s=0;s=359&&(n[0]>0&&(n[0]=0),a[0]1?(t.material.shader!==this._meshLinesShader&&t.material.attachShader(this._meshLinesShader),t.mode=Ka.Mesh.TRIANGLES):(t.material.shader!==this._nativeLinesShader&&t.material.attachShader(this._nativeLinesShader),t.mode=Ka.Mesh.LINES),r=r||0,i=i||n.count(),s.resetOffset();var u=0,c=0,d=[],f=[],p=[],m=[],g=[],_=.3,v=.7;function y(){f[0]=d[0]*v+m[0]*_-(d[1]-m[1])*a,f[1]=d[1]*v+m[1]*_-(m[0]-d[0])*a,p[0]=d[0]*_+m[0]*v-(d[1]-m[1])*a,p[1]=d[1]*_+m[1]*v-(m[0]-d[0])*a}if(o||0!==a)for(var x=r;x{t.exports=e}},r={};function i(e){if(r[e])return r[e].exports;var n=r[e]={exports:{}};return t[e](n,n.exports,i),n.exports}return i.g=function(){if(\"object\"==typeof globalThis)return globalThis;try{return this||new Function(\"return this\")()}catch(e){if(\"object\"==typeof window)return window}}(),i.r=e=>{\"undefined\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(e,\"__esModule\",{value:!0})},i(468)})()}));": "))return e;for(var r=e.split(\"/\"),i=t.split(\"/\"),n=r[0];\".\"===n||\"..\"===n;)\"..\"===n&&i.pop(),r.shift(),n=r[0];return i.join(\"/\")+\"/\"+r.join(\"/\")},extend:function(e,t){if(t)for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r]);return e},defaults:function(e,t){if(t)for(var r in t)void 0===e[r]&&(e[r]=t[r]);return e},extendWithPropList:function(e,t,r){if(t)for(var i=0;i=400?e.onerror&&e.onerror():e.onload&&e.onload(t.response)},e.onerror&&(t.onerror=e.onerror),t.send(null)}};var F,z={supportWebGL:function(){if(null==F)try{var e=document.createElement(\"canvas\");if(!e.getContext(\"webgl\")&&!e.getContext(\"experimental-webgl\"))throw new Error}catch(e){F=!1}return F}};z.Int8Array=\"undefined\"==typeof Int8Array?Array:Int8Array,z.Uint8Array=\"undefined\"==typeof Uint8Array?Array:Uint8Array,z.Uint16Array=\"undefined\"==typeof Uint16Array?Array:Uint16Array,z.Uint32Array=\"undefined\"==typeof Uint32Array?Array:Uint32Array,z.Int16Array=\"undefined\"==typeof Int16Array?Array:Int16Array,z.Float32Array=\"undefined\"==typeof Float32Array?Array:Float32Array,z.Float64Array=\"undefined\"==typeof Float64Array?Array:Float64Array;var G={};\"undefined\"!=typeof window?G=window:void 0!==r.g&&(G=r.g),z.requestAnimationFrame=G.requestAnimationFrame||G.msRequestAnimationFrame||G.mozRequestAnimationFrame||G.webkitRequestAnimationFrame||function(e){setTimeout(e,16)},z.createCanvas=function(){return document.createElement(\"canvas\")},z.createImage=function(){return new G.Image},z.request={get:B.get},z.addEventListener=function(e,t,r,i){e.addEventListener(t,r,i)},z.removeEventListener=function(e,t,r){e.removeEventListener(t,r)};const U=z;var k=function(){this.head=null,this.tail=null,this._length=0};k.prototype.insert=function(e){var t=new k.Entry(e);return this.insertEntry(t),t},k.prototype.insertAt=function(e,t){if(!(e<0)){for(var r=this.head,i=0;r&&i!=e;)r=r.next,i++;if(r){var n=new k.Entry(t),a=r.prev;a?(a.next=n,n.prev=a):this.head=n,n.next=r,r.prev=n}else this.insert(t)}},k.prototype.insertBeforeEntry=function(e,t){var r=new k.Entry(e),i=t.prev;i?(i.next=r,r.prev=i):this.head=r,r.next=t,t.prev=r,this._length++},k.prototype.insertEntry=function(e){this.head?(this.tail.next=e,e.prev=this.tail,this.tail=e):this.head=this.tail=e,this._length++},k.prototype.remove=function(e){var t=e.prev,r=e.next;t?t.next=r:this.head=r,r?r.prev=t:this.tail=t,e.next=e.prev=null,this._length--},k.prototype.removeAt=function(e){if(!(e<0)){for(var t=this.head,r=0;t&&r!=e;)t=t.next,r++;return t?(this.remove(t),t.value):void 0}},k.prototype.getHead=function(){if(this.head)return this.head.value},k.prototype.getTail=function(){if(this.tail)return this.tail.value},k.prototype.getAt=function(e){if(!(e<0)){for(var t=this.head,r=0;t&&r!=e;)t=t.next,r++;return t.value}},k.prototype.indexOf=function(e){for(var t=this.head,r=0;t;){if(t.value===e)return r;t=t.next,r++}},k.prototype.length=function(){return this._length},k.prototype.isEmpty=function(){return 0===this._length},k.prototype.forEach=function(e,t){for(var r=this.head,i=0,n=void 0!==t;r;)n?e.call(t,r.value,i):e(r.value,i),r=r.next,i++},k.prototype.clear=function(){this.tail=this.head=null,this._length=0},k.Entry=function(e){this.value=e,this.next=null,this.prev=null};const V=k;var H=function(e){this._list=new V,this._map={},this._maxSize=e||10};H.prototype.setMaxSize=function(e){this._maxSize=e},H.prototype.put=function(e,t){if(!this._map.hasOwnProperty(e)){var r=this._list.length();if(r>=this._maxSize&&r>0){var i=this._list.head;this._list.remove(i),delete this._map[i.key]}var n=this._list.insert(t);n.key=e,this._map[e]=n}},H.prototype.get=function(e){var t=this._map[e];if(this._map.hasOwnProperty(e))return t!==this._list.tail&&(this._list.remove(t),this._list.insertEntry(t)),t.value},H.prototype.remove=function(e){var t=this._map[e];void 0!==t&&(delete this._map[e],this._list.remove(t))},H.prototype.clear=function(){this._list.clear(),this._map={}};const W=H;var j={},X={transparent:[0,0,0,0],aliceblue:[240,248,255,1],antiquewhite:[250,235,215,1],aqua:[0,255,255,1],aquamarine:[127,255,212,1],azure:[240,255,255,1],beige:[245,245,220,1],bisque:[255,228,196,1],black:[0,0,0,1],blanchedalmond:[255,235,205,1],blue:[0,0,255,1],blueviolet:[138,43,226,1],brown:[165,42,42,1],burlywood:[222,184,135,1],cadetblue:[95,158,160,1],chartreuse:[127,255,0,1],chocolate:[210,105,30,1],coral:[255,127,80,1],cornflowerblue:[100,149,237,1],cornsilk:[255,248,220,1],crimson:[220,20,60,1],cyan:[0,255,255,1],darkblue:[0,0,139,1],darkcyan:[0,139,139,1],darkgoldenrod:[184,134,11,1],darkgray:[169,169,169,1],darkgreen:[0,100,0,1],darkgrey:[169,169,169,1],darkkhaki:[189,183,107,1],darkmagenta:[139,0,139,1],darkolivegreen:[85,107,47,1],darkorange:[255,140,0,1],darkorchid:[153,50,204,1],darkred:[139,0,0,1],darksalmon:[233,150,122,1],darkseagreen:[143,188,143,1],darkslateblue:[72,61,139,1],darkslategray:[47,79,79,1],darkslategrey:[47,79,79,1],darkturquoise:[0,206,209,1],darkviolet:[148,0,211,1],deeppink:[255,20,147,1],deepskyblue:[0,191,255,1],dimgray:[105,105,105,1],dimgrey:[105,105,105,1],dodgerblue:[30,144,255,1],firebrick:[178,34,34,1],floralwhite:[255,250,240,1],forestgreen:[34,139,34,1],fuchsia:[255,0,255,1],gainsboro:[220,220,220,1],ghostwhite:[248,248,255,1],gold:[255,215,0,1],goldenrod:[218,165,32,1],gray:[128,128,128,1],green:[0,128,0,1],greenyellow:[173,255,47,1],grey:[128,128,128,1],honeydew:[240,255,240,1],hotpink:[255,105,180,1],indianred:[205,92,92,1],indigo:[75,0,130,1],ivory:[255,255,240,1],khaki:[240,230,140,1],lavender:[230,230,250,1],lavenderblush:[255,240,245,1],lawngreen:[124,252,0,1],lemonchiffon:[255,250,205,1],lightblue:[173,216,230,1],lightcoral:[240,128,128,1],lightcyan:[224,255,255,1],lightgoldenrodyellow:[250,250,210,1],lightgray:[211,211,211,1],lightgreen:[144,238,144,1],lightgrey:[211,211,211,1],lightpink:[255,182,193,1],lightsalmon:[255,160,122,1],lightseagreen:[32,178,170,1],lightskyblue:[135,206,250,1],lightslategray:[119,136,153,1],lightslategrey:[119,136,153,1],lightsteelblue:[176,196,222,1],lightyellow:[255,255,224,1],lime:[0,255,0,1],limegreen:[50,205,50,1],linen:[250,240,230,1],magenta:[255,0,255,1],maroon:[128,0,0,1],mediumaquamarine:[102,205,170,1],mediumblue:[0,0,205,1],mediumorchid:[186,85,211,1],mediumpurple:[147,112,219,1],mediumseagreen:[60,179,113,1],mediumslateblue:[123,104,238,1],mediumspringgreen:[0,250,154,1],mediumturquoise:[72,209,204,1],mediumvioletred:[199,21,133,1],midnightblue:[25,25,112,1],mintcream:[245,255,250,1],mistyrose:[255,228,225,1],moccasin:[255,228,181,1],navajowhite:[255,222,173,1],navy:[0,0,128,1],oldlace:[253,245,230,1],olive:[128,128,0,1],olivedrab:[107,142,35,1],orange:[255,165,0,1],orangered:[255,69,0,1],orchid:[218,112,214,1],palegoldenrod:[238,232,170,1],palegreen:[152,251,152,1],paleturquoise:[175,238,238,1],palevioletred:[219,112,147,1],papayawhip:[255,239,213,1],peachpuff:[255,218,185,1],peru:[205,133,63,1],pink:[255,192,203,1],plum:[221,160,221,1],powderblue:[176,224,230,1],purple:[128,0,128,1],red:[255,0,0,1],rosybrown:[188,143,143,1],royalblue:[65,105,225,1],saddlebrown:[139,69,19,1],salmon:[250,128,114,1],sandybrown:[244,164,96,1],seagreen:[46,139,87,1],seashell:[255,245,238,1],sienna:[160,82,45,1],silver:[192,192,192,1],skyblue:[135,206,235,1],slateblue:[106,90,205,1],slategray:[112,128,144,1],slategrey:[112,128,144,1],snow:[255,250,250,1],springgreen:[0,255,127,1],steelblue:[70,130,180,1],tan:[210,180,140,1],teal:[0,128,128,1],thistle:[216,191,216,1],tomato:[255,99,71,1],turquoise:[64,224,208,1],violet:[238,130,238,1],wheat:[245,222,179,1],white:[255,255,255,1],whitesmoke:[245,245,245,1],yellow:[255,255,0,1],yellowgreen:[154,205,50,1]};function q(e){return(e=Math.round(e))<0?0:e>255?255:e}function Z(e){return e<0?0:e>1?1:e}function Y(e){return e.length&&\"%\"===e.charAt(e.length-1)?q(parseFloat(e)/100*255):q(parseInt(e,10))}function K(e){return e.length&&\"%\"===e.charAt(e.length-1)?Z(parseFloat(e)/100):Z(parseFloat(e))}function Q(e,t,r){return r<0?r+=1:r>1&&(r-=1),6*r<1?e+(t-e)*r*6:2*r<1?t:3*r<2?e+(t-e)*(2/3-r)*6:e}function J(e,t,r){return e+(t-e)*r}function $(e,t,r,i,n){return e[0]=t,e[1]=r,e[2]=i,e[3]=n,e}function ee(e,t){return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e}var te=new W(20),re=null;function ie(e,t){re&&ee(re,t),re=te.put(e,re||t.slice())}function ne(e,t){var r=(parseFloat(e[0])%360+360)%360/360,i=K(e[1]),n=K(e[2]),a=n<=.5?n*(i+1):n+i-n*i,o=2*n-a;return $(t=t||[],q(255*Q(o,a,r+1/3)),q(255*Q(o,a,r)),q(255*Q(o,a,r-1/3)),1),4===e.length&&(t[3]=e[3]),t}j.parse=function(e,t){if(e){t=t||[];var r=te.get(e);if(r)return ee(t,r);var i,n=(e+=\"\").replace(/ /g,\"\").toLowerCase();if(n in X)return ee(t,X[n]),ie(e,t),t;if(\"#\"===n.charAt(0))return 4===n.length?(i=parseInt(n.substr(1),16))>=0&&i<=4095?($(t,(3840&i)>>4|(3840&i)>>8,240&i|(240&i)>>4,15&i|(15&i)<<4,1),ie(e,t),t):void $(t,0,0,0,1):7===n.length?(i=parseInt(n.substr(1),16))>=0&&i<=16777215?($(t,(16711680&i)>>16,(65280&i)>>8,255&i,1),ie(e,t),t):void $(t,0,0,0,1):void 0;var a=n.indexOf(\"(\"),o=n.indexOf(\")\");if(-1!==a&&o+1===n.length){var s=n.substr(0,a),l=n.substr(a+1,o-(a+1)).split(\",\"),h=1;switch(s){case\"rgba\":if(4!==l.length)return void $(t,0,0,0,1);h=K(l.pop());case\"rgb\":return 3!==l.length?void $(t,0,0,0,1):($(t,Y(l[0]),Y(l[1]),Y(l[2]),h),ie(e,t),t);case\"hsla\":return 4!==l.length?void $(t,0,0,0,1):(l[3]=K(l[3]),ne(l,t),ie(e,t),t);case\"hsl\":return 3!==l.length?void $(t,0,0,0,1):(ne(l,t),ie(e,t),t);default:return}}$(t,0,0,0,1)}},j.parseToFloat=function(e,t){if(t=j.parse(e,t))return t[0]/=255,t[1]/=255,t[2]/=255,t},j.lift=function(e,t){var r=j.parse(e);if(r){for(var i=0;i<3;i++)r[i]=t<0?r[i]*(1-t)|0:(255-r[i])*t+r[i]|0;return j.stringify(r,4===r.length?\"rgba\":\"rgb\")}},j.toHex=function(e){var t=j.parse(e);if(t)return((1<<24)+(t[0]<<16)+(t[1]<<8)+ +t[2]).toString(16).slice(1)},j.fastLerp=function(e,t,r){if(t&&t.length&&e>=0&&e<=1){r=r||[];var i=e*(t.length-1),n=Math.floor(i),a=Math.ceil(i),o=t[n],s=t[a],l=i-n;return r[0]=q(J(o[0],s[0],l)),r[1]=q(J(o[1],s[1],l)),r[2]=q(J(o[2],s[2],l)),r[3]=Z(J(o[3],s[3],l)),r}},j.fastMapToColor=j.fastLerp,j.lerp=function(e,t,r){if(t&&t.length&&e>=0&&e<=1){var i=e*(t.length-1),n=Math.floor(i),a=Math.ceil(i),o=j.parse(t[n]),s=j.parse(t[a]),l=i-n,h=j.stringify([q(J(o[0],s[0],l)),q(J(o[1],s[1],l)),q(J(o[2],s[2],l)),Z(J(o[3],s[3],l))],\"rgba\");return r?{color:h,leftIndex:n,rightIndex:a,value:i}:h}},j.mapToColor=j.lerp,j.modifyHSL=function(e,t,r,i){if(e=j.parse(e))return e=function(e){if(e){var t,r,i=e[0]/255,n=e[1]/255,a=e[2]/255,o=Math.min(i,n,a),s=Math.max(i,n,a),l=s-o,h=(s+o)/2;if(0===l)t=0,r=0;else{r=h<.5?l/(s+o):l/(2-s-o);var u=((s-i)/6+l/2)/l,c=((s-n)/6+l/2)/l,d=((s-a)/6+l/2)/l;i===s?t=d-c:n===s?t=1/3+u-d:a===s&&(t=2/3+c-u),t<0&&(t+=1),t>1&&(t-=1)}var f=[360*t,r,h];return null!=e[3]&&f.push(e[3]),f}}(e),null!=t&&(e[0]=(n=t,(n=Math.round(n))<0?0:n>360?360:n)),null!=r&&(e[1]=K(r)),null!=i&&(e[2]=K(i)),j.stringify(ne(e),\"rgba\");var n},j.modifyAlpha=function(e,t){if((e=j.parse(e))&&null!=t)return e[3]=Z(t),j.stringify(e,\"rgba\")},j.stringify=function(e,t){if(e&&e.length){var r=e[0]+\",\"+e[1]+\",\"+e[2];return\"rgba\"!==t&&\"hsva\"!==t&&\"hsla\"!==t||(r+=\",\"+e[3]),t+\"(\"+r+\")\"}};var ae=j.parseToFloat,oe={};function se(e){var t=Object.keys(e);t.sort();for(var r=[],i=0;i=0},getEnabledUniforms:function(){return this._enabledUniforms},getTextureUniforms:function(){return this._textureUniforms},set:function(e,t){if(\"object\"==typeof e)for(var r in e){var i=e[r];this.setUniform(r,i)}else this.setUniform(e,t)},get:function(e){var t=this.uniforms[e];if(t)return t.value},attachShader:function(e,t){var r=this.uniforms;this.uniforms=e.createUniforms(),this.shader=e;var i=this.uniforms;this._enabledUniforms=Object.keys(i),this._enabledUniforms.sort(),this._textureUniforms=this._enabledUniforms.filter((function(e){var t=this.uniforms[e].type;return\"t\"===t||\"tv\"===t}),this);var n=this.vertexDefines,a=this.fragmentDefines;if(this.vertexDefines=f.clone(e.vertexDefines),this.fragmentDefines=f.clone(e.fragmentDefines),t){for(var o in r)i[o]&&(i[o].value=r[o].value);f.defaults(this.vertexDefines,n),f.defaults(this.fragmentDefines,a)}var s={};for(var l in e.textures)s[l]={shaderType:e.textures[l].shaderType,type:e.textures[l].type,enabled:!(!t||!this._textureStatus[l])&&this._textureStatus[l].enabled};this._textureStatus=s,this._programKey=\"\"},clone:function(){var e=new this.constructor({name:this.name,shader:this.shader});for(var t in this.uniforms)e.uniforms[t].value=this.uniforms[t].value;return e.depthTest=this.depthTest,e.depthMask=this.depthMask,e.transparent=this.transparent,e.blend=this.blend,e.vertexDefines=f.clone(this.vertexDefines),e.fragmentDefines=f.clone(this.fragmentDefines),e.enableTexture(this.getEnabledTextures()),e.precision=this.precision,e},define:function(e,t,r){var i=this.vertexDefines,n=this.fragmentDefines;\"vertex\"!==e&&\"fragment\"!==e&&\"both\"!==e&&arguments.length<3&&(r=t,t=e,e=\"both\"),r=null!=r?r:null,\"vertex\"!==e&&\"both\"!==e||i[t]!==r&&(i[t]=r,this._programKey=\"\"),\"fragment\"!==e&&\"both\"!==e||n[t]!==r&&(n[t]=r,\"both\"!==e&&(this._programKey=\"\"))},undefine:function(e,t){\"vertex\"!==e&&\"fragment\"!==e&&\"both\"!==e&&arguments.length<2&&(t=e,e=\"both\"),\"vertex\"!==e&&\"both\"!==e||this.isDefined(\"vertex\",t)&&(delete this.vertexDefines[t],this._programKey=\"\"),\"fragment\"!==e&&\"both\"!==e||this.isDefined(\"fragment\",t)&&(delete this.fragmentDefines[t],\"both\"!==e&&(this._programKey=\"\"))},isDefined:function(e,t){switch(e){case\"vertex\":return void 0!==this.vertexDefines[t];case\"fragment\":return void 0!==this.fragmentDefines[t]}},getDefine:function(e,t){switch(e){case\"vertex\":return this.vertexDefines[t];case\"fragment\":return this.fragmentDefines[t]}},enableTexture:function(e){if(Array.isArray(e))for(var t=0;t0&&(n=1/Math.sqrt(n),e[0]=t[0]*n,e[1]=t[1]*n),e},fe.dot=function(e,t){return e[0]*t[0]+e[1]*t[1]},fe.cross=function(e,t,r){var i=t[0]*r[1]-t[1]*r[0];return e[0]=e[1]=0,e[2]=i,e},fe.lerp=function(e,t,r,i){var n=t[0],a=t[1];return e[0]=n+i*(r[0]-n),e[1]=a+i*(r[1]-a),e},fe.random=function(e,t){t=t||1;var r=2*GLMAT_RANDOM()*Math.PI;return e[0]=Math.cos(r)*t,e[1]=Math.sin(r)*t,e},fe.transformMat2=function(e,t,r){var i=t[0],n=t[1];return e[0]=r[0]*i+r[2]*n,e[1]=r[1]*i+r[3]*n,e},fe.transformMat2d=function(e,t,r){var i=t[0],n=t[1];return e[0]=r[0]*i+r[2]*n+r[4],e[1]=r[1]*i+r[3]*n+r[5],e},fe.transformMat3=function(e,t,r){var i=t[0],n=t[1];return e[0]=r[0]*i+r[3]*n+r[6],e[1]=r[1]*i+r[4]*n+r[7],e},fe.transformMat4=function(e,t,r){var i=t[0],n=t[1];return e[0]=r[0]*i+r[4]*n+r[12],e[1]=r[1]*i+r[5]*n+r[13],e},fe.forEach=(he=fe.create(),function(e,t,r,i,n,a){var o,s;for(t||(t=2),r||(r=0),s=i?Math.min(i*t+r,e.length):e.length,o=r;o0&&i.push(\"#define \"+n.toUpperCase()+\"_COUNT \"+a)}if(r)for(var o=0;ol.getMaxJointNumber()&&(d.USE_SKIN_MATRICES_TEXTURE=null),c+=\"\\n\"+Me(d)+\"\\n\"}a&&(c+=\"\\n#define INSTANCING\\n\");var f=c+Me(t.vertexDefines,s,u),p=c+Me(t.fragmentDefines,s,u),m=f+\"\\n\"+t.shader.vertex,g=[\"OES_standard_derivatives\",\"EXT_shader_texture_lod\"].filter((function(e){return null!=l.getGLExtension(e)}));g.indexOf(\"EXT_shader_texture_lod\")>=0&&(p+=\"\\n#define SUPPORT_TEXTURE_LOD\"),g.indexOf(\"OES_standard_derivatives\")>=0&&(p+=\"\\n#define SUPPORT_STANDARD_DERIVATIVES\");var _,v,y=function(e){for(var t=[],r=0;r=0){if(1!==s&&4!==s){ke();break}s=2,h=[]}else if(1!==s)if(4!==s)u(c),s=0;else{var d=c;Ie.indexOf(d)>=0||Re.indexOf(d)>=0||Be.indexOf(d)>=0?l[o].semantic=d:\"ignore\"===d||\"unconfigurable\"===d?l[o].ignore=!0:l[o].value=\"bool\"===e?\"true\"===d:parseFloat(d)}else l[o].value=\"bool\"===e?\"true\"===c:parseFloat(c),h=null;else{if(2!==s){ke();break}if(!(h instanceof Array)){ke();break}h.push(+i[++a])}else l[o].value=new U.Float32Array(h),h=null,s=5;else if(2===s){if(!(h instanceof Array)){ke();break}h.push(+i[++a])}else s=5;else s=4;else{if(0!==s&&3!==s){ke();break}s=1}}return l}function He(e,t){\"object\"==typeof e&&(t=e.fragment,e=e.vertex),e=Ue(e),t=Ue(t),this._shaderID=function(e,t){var r=\"vertex:\"+e+\"fragment:\"+t;if(ze[r])return ze[r];var i=f.genGUID();return ze[r]=i,Ge[i]={vertex:e,fragment:t},i}(e,t),this._vertexCode=He.parseImport(e),this._fragmentCode=He.parseImport(t),this.attributeSemantics={},this.matrixSemantics={},this.uniformSemantics={},this.matrixSemanticKeys=[],this.uniformTemplates={},this.attributes={},this.textures={},this.vertexDefines={},this.fragmentDefines={},this._parseAttributes(),this._parseUniforms(),this._parseDefines()}He.prototype={constructor:He,createUniforms:function(){var e={};for(var t in this.uniformTemplates){var r=this.uniformTemplates[t];e[t]={type:r.type,value:r.value()}}return e},_parseImport:function(){this._vertexCode=He.parseImport(this.vertex),this._fragmentCode=He.parseImport(this.fragment)},_addSemanticUniform:function(e,t,r){if(Ie.indexOf(r)>=0)this.attributeSemantics[r]={symbol:e,type:t};else if(Be.indexOf(r)>=0){var i=!1,n=r;r.match(/TRANSPOSE$/)&&(i=!0,n=r.slice(0,-9)),this.matrixSemantics[r]={symbol:e,type:t,isTranspose:i,semanticNoTranspose:n}}else Re.indexOf(r)>=0&&(this.uniformSemantics[r]={symbol:e,type:t})},_addMaterialUniform:function(e,t,r,i,n,a){a[e]={type:r,value:n?Ne.array:i||Ne[t],semantic:null}},_parseUniforms:function(){var e={},t=this;function r(e){return null!=e?function(){return e}:null}function i(i,n,a){var o=Ve(n,a),s=[];for(var l in o){var h=o[l],u=h.semantic,c=l,d=Pe[n],f=r(o[l].value);o[l].isArray&&(c+=\"[\"+o[l].arraySize+\"]\",d+=\"v\"),s.push(c),t._uniformList.push(l),h.ignore||(\"sampler2D\"!==n&&\"samplerCube\"!==n||(t.textures[l]={shaderType:\"fragment\",type:n}),u?t._addSemanticUniform(l,d,u):t._addMaterialUniform(l,n,d,f,o[l].isArray,e))}return s.length>0?\"uniform \"+n+\" \"+s.join(\",\")+\";\\n\":\"\"}this._uniformList=[],this._vertexCode=this._vertexCode.replace(Ce,i),this._fragmentCode=this._fragmentCode.replace(Ce,i),t.matrixSemanticKeys=Object.keys(this.matrixSemantics),this.uniformTemplates=e},_parseAttributes:function(){var e={},t=this;this._vertexCode=this._vertexCode.replace(De,(function(r,i,n){var a=Ve(i,n),o=Fe[i]||1,s=[];for(var l in a){var h=a[l].semantic;if(e[l]={type:\"float\",size:o,semantic:h||null},h){if(Ie.indexOf(h)<0)throw new Error('Unkown semantic \"'+h+'\"');t.attributeSemantics[h]={symbol:l,type:i}}s.push(l)}return\"attribute \"+i+\" \"+s.join(\",\")+\";\\n\"})),this.attributes=e},_parseDefines:function(){var e=this;function t(t,r,i){var n=e.fragmentDefines;return n[r]||(n[r]=\"false\"!==i&&(\"true\"===i||(i?isNaN(parseFloat(i))?i.trim():parseFloat(i):null))),\"\"}this._vertexCode=this._vertexCode.replace(Le,t),this._fragmentCode=this._fragmentCode.replace(Le,t)},clone:function(){var e=Ge[this._shaderID];return new He(e.vertex,e.fragment)}},Object.defineProperty&&(Object.defineProperty(He.prototype,\"shaderID\",{get:function(){return this._shaderID}}),Object.defineProperty(He.prototype,\"vertex\",{get:function(){return this._vertexCode}}),Object.defineProperty(He.prototype,\"fragment\",{get:function(){return this._fragmentCode}}),Object.defineProperty(He.prototype,\"uniforms\",{get:function(){return this._uniformList}}));var We=/(@import)\\s*([0-9a-zA-Z_\\-\\.]*)/g;He.parseImport=function(e){return e.replace(We,(function(e,t,r){return(e=He.source(r))?He.parseImport(e):(console.error('Shader chunk \"'+r+'\" not existed in library'),\"\")}))};var je=/(@export)\\s*([0-9a-zA-Z_\\-\\.]*)\\s*\\n([\\s\\S]*?)@end/g;He.import=function(e){e.replace(je,(function(e,t,r,i){if(i=i.replace(/(^[\\s\\t\\xa0\\u3000]+)|([\\u3000\\xa0\\s\\t]+\\x24)/g,\"\")){for(var n,a=r.split(\".\"),o=He.codes,s=0;s 0.0) {\\n if (texture2D(alphaMap, v_Texcoord).a <= alphaCutoff) {\\n discard;\\n }\\n }\\n gl_FragColor = vec4(0.0,0.0,0.0,1.0);\\n}\\n@end\";var Ze={create:function(){var e=new ce(16);return e[0]=1,e[1]=0,e[2]=0,e[3]=0,e[4]=0,e[5]=1,e[6]=0,e[7]=0,e[8]=0,e[9]=0,e[10]=1,e[11]=0,e[12]=0,e[13]=0,e[14]=0,e[15]=1,e},clone:function(e){var t=new ce(16);return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4],t[5]=e[5],t[6]=e[6],t[7]=e[7],t[8]=e[8],t[9]=e[9],t[10]=e[10],t[11]=e[11],t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15],t},copy:function(e,t){return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4],e[5]=t[5],e[6]=t[6],e[7]=t[7],e[8]=t[8],e[9]=t[9],e[10]=t[10],e[11]=t[11],e[12]=t[12],e[13]=t[13],e[14]=t[14],e[15]=t[15],e},identity:function(e){return e[0]=1,e[1]=0,e[2]=0,e[3]=0,e[4]=0,e[5]=1,e[6]=0,e[7]=0,e[8]=0,e[9]=0,e[10]=1,e[11]=0,e[12]=0,e[13]=0,e[14]=0,e[15]=1,e},transpose:function(e,t){if(e===t){var r=t[1],i=t[2],n=t[3],a=t[6],o=t[7],s=t[11];e[1]=t[4],e[2]=t[8],e[3]=t[12],e[4]=r,e[6]=t[9],e[7]=t[13],e[8]=i,e[9]=a,e[11]=t[14],e[12]=n,e[13]=o,e[14]=s}else e[0]=t[0],e[1]=t[4],e[2]=t[8],e[3]=t[12],e[4]=t[1],e[5]=t[5],e[6]=t[9],e[7]=t[13],e[8]=t[2],e[9]=t[6],e[10]=t[10],e[11]=t[14],e[12]=t[3],e[13]=t[7],e[14]=t[11],e[15]=t[15];return e},invert:function(e,t){var r=t[0],i=t[1],n=t[2],a=t[3],o=t[4],s=t[5],l=t[6],h=t[7],u=t[8],c=t[9],d=t[10],f=t[11],p=t[12],m=t[13],g=t[14],_=t[15],v=r*s-i*o,y=r*l-n*o,x=r*h-a*o,b=i*l-n*s,w=i*h-a*s,T=n*h-a*l,S=u*m-c*p,M=u*g-d*p,A=u*_-f*p,E=c*g-d*m,C=c*_-f*m,D=d*_-f*g,L=v*D-y*C+x*E+b*A-w*M+T*S;return L?(L=1/L,e[0]=(s*D-l*C+h*E)*L,e[1]=(n*C-i*D-a*E)*L,e[2]=(m*T-g*w+_*b)*L,e[3]=(d*w-c*T-f*b)*L,e[4]=(l*A-o*D-h*M)*L,e[5]=(r*D-n*A+a*M)*L,e[6]=(g*x-p*T-_*y)*L,e[7]=(u*T-d*x+f*y)*L,e[8]=(o*C-s*A+h*S)*L,e[9]=(i*A-r*C-a*S)*L,e[10]=(p*w-m*x+_*v)*L,e[11]=(c*x-u*w-f*v)*L,e[12]=(s*M-o*E-l*S)*L,e[13]=(r*E-i*M+n*S)*L,e[14]=(m*y-p*b-g*v)*L,e[15]=(u*b-c*y+d*v)*L,e):null},adjoint:function(e,t){var r=t[0],i=t[1],n=t[2],a=t[3],o=t[4],s=t[5],l=t[6],h=t[7],u=t[8],c=t[9],d=t[10],f=t[11],p=t[12],m=t[13],g=t[14],_=t[15];return e[0]=s*(d*_-f*g)-c*(l*_-h*g)+m*(l*f-h*d),e[1]=-(i*(d*_-f*g)-c*(n*_-a*g)+m*(n*f-a*d)),e[2]=i*(l*_-h*g)-s*(n*_-a*g)+m*(n*h-a*l),e[3]=-(i*(l*f-h*d)-s*(n*f-a*d)+c*(n*h-a*l)),e[4]=-(o*(d*_-f*g)-u*(l*_-h*g)+p*(l*f-h*d)),e[5]=r*(d*_-f*g)-u*(n*_-a*g)+p*(n*f-a*d),e[6]=-(r*(l*_-h*g)-o*(n*_-a*g)+p*(n*h-a*l)),e[7]=r*(l*f-h*d)-o*(n*f-a*d)+u*(n*h-a*l),e[8]=o*(c*_-f*m)-u*(s*_-h*m)+p*(s*f-h*c),e[9]=-(r*(c*_-f*m)-u*(i*_-a*m)+p*(i*f-a*c)),e[10]=r*(s*_-h*m)-o*(i*_-a*m)+p*(i*h-a*s),e[11]=-(r*(s*f-h*c)-o*(i*f-a*c)+u*(i*h-a*s)),e[12]=-(o*(c*g-d*m)-u*(s*g-l*m)+p*(s*d-l*c)),e[13]=r*(c*g-d*m)-u*(i*g-n*m)+p*(i*d-n*c),e[14]=-(r*(s*g-l*m)-o*(i*g-n*m)+p*(i*l-n*s)),e[15]=r*(s*d-l*c)-o*(i*d-n*c)+u*(i*l-n*s),e},determinant:function(e){var t=e[0],r=e[1],i=e[2],n=e[3],a=e[4],o=e[5],s=e[6],l=e[7],h=e[8],u=e[9],c=e[10],d=e[11],f=e[12],p=e[13],m=e[14],g=e[15];return(t*o-r*a)*(c*g-d*m)-(t*s-i*a)*(u*g-d*p)+(t*l-n*a)*(u*m-c*p)+(r*s-i*o)*(h*g-d*f)-(r*l-n*o)*(h*m-c*f)+(i*l-n*s)*(h*p-u*f)},multiply:function(e,t,r){var i=t[0],n=t[1],a=t[2],o=t[3],s=t[4],l=t[5],h=t[6],u=t[7],c=t[8],d=t[9],f=t[10],p=t[11],m=t[12],g=t[13],_=t[14],v=t[15],y=r[0],x=r[1],b=r[2],w=r[3];return e[0]=y*i+x*s+b*c+w*m,e[1]=y*n+x*l+b*d+w*g,e[2]=y*a+x*h+b*f+w*_,e[3]=y*o+x*u+b*p+w*v,y=r[4],x=r[5],b=r[6],w=r[7],e[4]=y*i+x*s+b*c+w*m,e[5]=y*n+x*l+b*d+w*g,e[6]=y*a+x*h+b*f+w*_,e[7]=y*o+x*u+b*p+w*v,y=r[8],x=r[9],b=r[10],w=r[11],e[8]=y*i+x*s+b*c+w*m,e[9]=y*n+x*l+b*d+w*g,e[10]=y*a+x*h+b*f+w*_,e[11]=y*o+x*u+b*p+w*v,y=r[12],x=r[13],b=r[14],w=r[15],e[12]=y*i+x*s+b*c+w*m,e[13]=y*n+x*l+b*d+w*g,e[14]=y*a+x*h+b*f+w*_,e[15]=y*o+x*u+b*p+w*v,e},multiplyAffine:function(e,t,r){var i=t[0],n=t[1],a=t[2],o=t[4],s=t[5],l=t[6],h=t[8],u=t[9],c=t[10],d=t[12],f=t[13],p=t[14],m=r[0],g=r[1],_=r[2];return e[0]=m*i+g*o+_*h,e[1]=m*n+g*s+_*u,e[2]=m*a+g*l+_*c,m=r[4],g=r[5],_=r[6],e[4]=m*i+g*o+_*h,e[5]=m*n+g*s+_*u,e[6]=m*a+g*l+_*c,m=r[8],g=r[9],_=r[10],e[8]=m*i+g*o+_*h,e[9]=m*n+g*s+_*u,e[10]=m*a+g*l+_*c,m=r[12],g=r[13],_=r[14],e[12]=m*i+g*o+_*h+d,e[13]=m*n+g*s+_*u+f,e[14]=m*a+g*l+_*c+p,e}};Ze.mul=Ze.multiply,Ze.mulAffine=Ze.multiplyAffine,Ze.translate=function(e,t,r){var i,n,a,o,s,l,h,u,c,d,f,p,m=r[0],g=r[1],_=r[2];return t===e?(e[12]=t[0]*m+t[4]*g+t[8]*_+t[12],e[13]=t[1]*m+t[5]*g+t[9]*_+t[13],e[14]=t[2]*m+t[6]*g+t[10]*_+t[14],e[15]=t[3]*m+t[7]*g+t[11]*_+t[15]):(i=t[0],n=t[1],a=t[2],o=t[3],s=t[4],l=t[5],h=t[6],u=t[7],c=t[8],d=t[9],f=t[10],p=t[11],e[0]=i,e[1]=n,e[2]=a,e[3]=o,e[4]=s,e[5]=l,e[6]=h,e[7]=u,e[8]=c,e[9]=d,e[10]=f,e[11]=p,e[12]=i*m+s*g+c*_+t[12],e[13]=n*m+l*g+d*_+t[13],e[14]=a*m+h*g+f*_+t[14],e[15]=o*m+u*g+p*_+t[15]),e},Ze.scale=function(e,t,r){var i=r[0],n=r[1],a=r[2];return e[0]=t[0]*i,e[1]=t[1]*i,e[2]=t[2]*i,e[3]=t[3]*i,e[4]=t[4]*n,e[5]=t[5]*n,e[6]=t[6]*n,e[7]=t[7]*n,e[8]=t[8]*a,e[9]=t[9]*a,e[10]=t[10]*a,e[11]=t[11]*a,e[12]=t[12],e[13]=t[13],e[14]=t[14],e[15]=t[15],e},Ze.rotate=function(e,t,r,i){var n,a,o,s,l,h,u,c,d,f,p,m,g,_,v,y,x,b,w,T,S,M,A,E,C=i[0],D=i[1],L=i[2],P=Math.sqrt(C*C+D*D+L*L);return Math.abs(P)0&&(a=1/Math.sqrt(a),e[0]=t[0]*a,e[1]=t[1]*a,e[2]=t[2]*a),e},Ke.dot=function(e,t){return e[0]*t[0]+e[1]*t[1]+e[2]*t[2]},Ke.cross=function(e,t,r){var i=t[0],n=t[1],a=t[2],o=r[0],s=r[1],l=r[2];return e[0]=n*l-a*s,e[1]=a*o-i*l,e[2]=i*s-n*o,e},Ke.lerp=function(e,t,r,i){var n=t[0],a=t[1],o=t[2];return e[0]=n+i*(r[0]-n),e[1]=a+i*(r[1]-a),e[2]=o+i*(r[2]-o),e},Ke.random=function(e,t){t=t||1;var r=2*de()*Math.PI,i=2*de()-1,n=Math.sqrt(1-i*i)*t;return e[0]=Math.cos(r)*n,e[1]=Math.sin(r)*n,e[2]=i*t,e},Ke.transformMat4=function(e,t,r){var i=t[0],n=t[1],a=t[2],o=r[3]*i+r[7]*n+r[11]*a+r[15];return o=o||1,e[0]=(r[0]*i+r[4]*n+r[8]*a+r[12])/o,e[1]=(r[1]*i+r[5]*n+r[9]*a+r[13])/o,e[2]=(r[2]*i+r[6]*n+r[10]*a+r[14])/o,e},Ke.transformMat3=function(e,t,r){var i=t[0],n=t[1],a=t[2];return e[0]=i*r[0]+n*r[3]+a*r[6],e[1]=i*r[1]+n*r[4]+a*r[7],e[2]=i*r[2]+n*r[5]+a*r[8],e},Ke.transformQuat=function(e,t,r){var i=t[0],n=t[1],a=t[2],o=r[0],s=r[1],l=r[2],h=r[3],u=h*i+s*a-l*n,c=h*n+l*i-o*a,d=h*a+o*n-s*i,f=-o*i-s*n-l*a;return e[0]=u*h+f*-o+c*-l-d*-s,e[1]=c*h+f*-s+d*-o-u*-l,e[2]=d*h+f*-l+u*-s-c*-o,e},Ke.rotateX=function(e,t,r,i){var n=[],a=[];return n[0]=t[0]-r[0],n[1]=t[1]-r[1],n[2]=t[2]-r[2],a[0]=n[0],a[1]=n[1]*Math.cos(i)-n[2]*Math.sin(i),a[2]=n[1]*Math.sin(i)+n[2]*Math.cos(i),e[0]=a[0]+r[0],e[1]=a[1]+r[1],e[2]=a[2]+r[2],e},Ke.rotateY=function(e,t,r,i){var n=[],a=[];return n[0]=t[0]-r[0],n[1]=t[1]-r[1],n[2]=t[2]-r[2],a[0]=n[2]*Math.sin(i)+n[0]*Math.cos(i),a[1]=n[1],a[2]=n[2]*Math.cos(i)-n[0]*Math.sin(i),e[0]=a[0]+r[0],e[1]=a[1]+r[1],e[2]=a[2]+r[2],e},Ke.rotateZ=function(e,t,r,i){var n=[],a=[];return n[0]=t[0]-r[0],n[1]=t[1]-r[1],n[2]=t[2]-r[2],a[0]=n[0]*Math.cos(i)-n[1]*Math.sin(i),a[1]=n[0]*Math.sin(i)+n[1]*Math.cos(i),a[2]=n[2],e[0]=a[0]+r[0],e[1]=a[1]+r[1],e[2]=a[2]+r[2],e},Ke.forEach=function(){var e=Ke.create();return function(t,r,i,n,a,o){var s,l;for(r||(r=3),i||(i=0),l=n?Math.min(n*r+i,t.length):t.length,s=i;s1?0:Math.acos(n)};const Qe=Ke;Xe.import(qe);var Je=Ye.create,$e={};function et(e){return e.material}function tt(e,t,r){return t.uniforms[r].value}function rt(e,t,r,i){return r!==i}function it(e){return!0}function nt(){}var at={float:S,byte:5120,ubyte:T,short:5122,ushort:5123};function ot(e,t,r){this.availableAttributes=e,this.availableAttributeSymbols=t,this.indicesBuffer=r,this.vao=null}function st(e){var t,r;this.bind=function(e){t||((t=U.createCanvas()).width=t.height=1,t.getContext(\"2d\"));var i=e.gl,n=!r;n&&(r=i.createTexture()),i.bindTexture(i.TEXTURE_2D,r),n&&i.texImage2D(i.TEXTURE_2D,0,i.RGBA,i.RGBA,i.UNSIGNED_BYTE,t)},this.unbind=function(e){e.gl.bindTexture(e.gl.TEXTURE_2D,null)},this.isRenderable=function(){return!0}}var lt=m.extend((function(){return{canvas:null,_width:100,_height:100,devicePixelRatio:\"undefined\"!=typeof window&&window.devicePixelRatio||1,clearColor:[0,0,0,0],clearBit:17664,alpha:!0,depth:!0,stencil:!1,antialias:!0,premultipliedAlpha:!0,preserveDrawingBuffer:!1,throwError:!0,gl:null,viewport:{},maxJointNumber:20,__currentFrameBuffer:null,_viewportStack:[],_clearStack:[],_sceneRendering:null}}),(function(){this.canvas||(this.canvas=U.createCanvas());var e=this.canvas;try{var t={alpha:this.alpha,depth:this.depth,stencil:this.stencil,antialias:this.antialias,premultipliedAlpha:this.premultipliedAlpha,preserveDrawingBuffer:this.preserveDrawingBuffer};if(this.gl=e.getContext(\"webgl\",t)||e.getContext(\"experimental-webgl\",t),!this.gl)throw new Error;this._glinfo=new v(this.gl),this.gl.targetRenderer&&console.error(\"Already created a renderer\"),this.gl.targetRenderer=this,this.resize()}catch(e){throw\"Error creating WebGL Context \"+e}this._programMgr=new Ee(this),this._placeholderTexture=new st(this)}),{resize:function(e,t){var r=this.canvas,i=this.devicePixelRatio;null!=e?(r.style&&(r.style.width=e+\"px\",r.style.height=t+\"px\"),r.width=e*i,r.height=t*i,this._width=e,this._height=t):(this._width=r.width/i,this._height=r.height/i),this.setViewport(0,0,this._width,this._height)},getWidth:function(){return this._width},getHeight:function(){return this._height},getViewportAspect:function(){var e=this.viewport;return e.width/e.height},setDevicePixelRatio:function(e){this.devicePixelRatio=e,this.resize(this._width,this._height)},getDevicePixelRatio:function(){return this.devicePixelRatio},getGLExtension:function(e){return this._glinfo.getExtension(e)},getGLParameter:function(e){return this._glinfo.getParameter(e)},setViewport:function(e,t,r,i,n){if(\"object\"==typeof e){var a=e;e=a.x,t=a.y,r=a.width,i=a.height,n=a.devicePixelRatio}n=n||this.devicePixelRatio,this.gl.viewport(e*n,t*n,r*n,i*n),this.viewport={x:e,y:t,width:r,height:i,devicePixelRatio:n}},saveViewport:function(){this._viewportStack.push(this.viewport)},restoreViewport:function(){this._viewportStack.length>0&&this.setViewport(this._viewportStack.pop())},saveClear:function(){this._clearStack.push({clearBit:this.clearBit,clearColor:this.clearColor})},restoreClear:function(){if(this._clearStack.length>0){var e=this._clearStack.pop();this.clearColor=e.clearColor,this.clearBit=e.clearBit}},bindSceneRendering:function(e){this._sceneRendering=e},render:function(e,t,r,i){var n=this.gl,a=this.clearColor;if(this.clearBit){n.colorMask(!0,!0,!0,!0),n.depthMask(!0);var o=this.viewport,s=!1,l=o.devicePixelRatio;(o.width!==this._width||o.height!==this._height||l&&l!==this.devicePixelRatio||o.x||o.y)&&(s=!0,n.enable(n.SCISSOR_TEST),n.scissor(o.x*l,o.y*l,o.width*l,o.height*l)),n.clearColor(a[0],a[1],a[2],a[3]),n.clear(this.clearBit),s&&n.disable(n.SCISSOR_TEST)}if(r||e.update(!1),e.updateLights(),t=t||e.getMainCamera()){t.update();var h=e.updateRenderList(t,!0);this._sceneRendering=e;var u=h.opaque,c=h.transparent,d=e.material;e.trigger(\"beforerender\",this,e,t,h),i?(this.renderPreZ(u,e,t),n.depthFunc(n.LEQUAL)):n.depthFunc(n.LESS);for(var f=Je(),p=Qe.create(),m=0;m0){var s=e[n-1],l=s.joints?s.joints.length:0;if((a.joints?a.joints.length:0)===l&&a.material===s.material&&a.lightGroup===s.lightGroup){a.__program=s.__program;continue}}var h=this._programMgr.getProgram(a,o,t);this.validateProgram(h),a.__program=h}},renderPass:function(e,t,r){this.trigger(\"beforerenderpass\",this,e,t,r),(r=r||{}).getMaterial=r.getMaterial||et,r.getUniform=r.getUniform||tt,r.isMaterialChanged=r.isMaterialChanged||rt,r.beforeRender=r.beforeRender||nt,r.afterRender=r.afterRender||nt;var i=r.ifRender||it;this.updatePrograms(e,this._sceneRendering,r),r.sortCompare&&e.sort(r.sortCompare);var n=this.viewport,a=n.devicePixelRatio,o=[n.x*a,n.y*a,n.width*a,n.height*a],s=this.devicePixelRatio,l=this.__currentFrameBuffer?[this.__currentFrameBuffer.getTextureWidth(),this.__currentFrameBuffer.getTextureHeight()]:[this._width*s,this._height*s],h=[o[2],o[3]],u=Date.now();t?(Ye.copy(ht.VIEW,t.viewMatrix.array),Ye.copy(ht.PROJECTION,t.projectionMatrix.array),Ye.copy(ht.VIEWINVERSE,t.worldTransform.array)):(Ye.identity(ht.VIEW),Ye.identity(ht.PROJECTION),Ye.identity(ht.VIEWINVERSE)),Ye.multiply(ht.VIEWPROJECTION,ht.PROJECTION,ht.VIEW),Ye.invert(ht.PROJECTIONINVERSE,ht.PROJECTION),Ye.invert(ht.VIEWPROJECTIONINVERSE,ht.VIEWPROJECTION);for(var c,d,f,p,m,g,_,v,y,x,b,w,T=this.gl,S=this._sceneRendering,M=0;Mthis.getMaxJointNumber()){var a=n.getSubSkinMatricesTexture(e.__uid__,e.joints);t.useTextureSlot(this,a,r),t.setUniform(i,\"1i\",\"skinMatricesTexture\",r),t.setUniform(i,\"1f\",\"skinMatricesTextureSize\",a.width)}else{var o=n.getSubSkinMatrices(e.__uid__,e.joints);t.setUniformOfSemantic(i,\"SKIN_MATRIX\",o)}},_renderObject:function(e,t,r){var i=this.gl,n=e.geometry,a=e.mode;null==a&&(a=4);var o=null,s=e.isInstancedMesh&&e.isInstancedMesh();if(!s||(o=this.getGLExtension(\"ANGLE_instanced_arrays\"))){var l;if(s&&(l=this._bindInstancedAttributes(e,r,o)),t.indicesBuffer){var h=this.getGLExtension(\"OES_element_index_uint\")&&n.indices instanceof Uint32Array?i.UNSIGNED_INT:i.UNSIGNED_SHORT;s?o.drawElementsInstancedANGLE(a,t.indicesBuffer.count,h,0,e.getInstanceCount()):i.drawElements(a,t.indicesBuffer.count,h,0)}else s?o.drawArraysInstancedANGLE(a,0,n.vertexCount,e.getInstanceCount()):i.drawArrays(a,0,n.vertexCount);if(s)for(var u=0;ur?r:e}ct.add=function(e,t,r){return Qe.add(e.array,t.array,r.array),e._dirty=!0,e},ct.set=function(e,t,r,i){Qe.set(e.array,t,r,i),e._dirty=!0},ct.copy=function(e,t){return Qe.copy(e.array,t.array),e._dirty=!0,e},ct.cross=function(e,t,r){return Qe.cross(e.array,t.array,r.array),e._dirty=!0,e},ct.distance=ct.dist=function(e,t){return Qe.distance(e.array,t.array)},ct.divide=ct.div=function(e,t,r){return Qe.divide(e.array,t.array,r.array),e._dirty=!0,e},ct.dot=function(e,t){return Qe.dot(e.array,t.array)},ct.len=function(e){return Qe.length(e.array)},ct.lerp=function(e,t,r,i){return Qe.lerp(e.array,t.array,r.array,i),e._dirty=!0,e},ct.min=function(e,t,r){return Qe.min(e.array,t.array,r.array),e._dirty=!0,e},ct.max=function(e,t,r){return Qe.max(e.array,t.array,r.array),e._dirty=!0,e},ct.multiply=ct.mul=function(e,t,r){return Qe.multiply(e.array,t.array,r.array),e._dirty=!0,e},ct.negate=function(e,t){return Qe.negate(e.array,t.array),e._dirty=!0,e},ct.normalize=function(e,t){return Qe.normalize(e.array,t.array),e._dirty=!0,e},ct.random=function(e,t){return Qe.random(e.array,t),e._dirty=!0,e},ct.scale=function(e,t,r){return Qe.scale(e.array,t.array,r),e._dirty=!0,e},ct.scaleAndAdd=function(e,t,r,i){return Qe.scaleAndAdd(e.array,t.array,r.array,i),e._dirty=!0,e},ct.squaredDistance=ct.sqrDist=function(e,t){return Qe.sqrDist(e.array,t.array)},ct.squaredLength=ct.sqrLen=function(e){return Qe.sqrLen(e.array)},ct.subtract=ct.sub=function(e,t,r){return Qe.subtract(e.array,t.array,r.array),e._dirty=!0,e},ct.transformMat3=function(e,t,r){return Qe.transformMat3(e.array,t.array,r.array),e._dirty=!0,e},ct.transformMat4=function(e,t,r){return Qe.transformMat4(e.array,t.array,r.array),e._dirty=!0,e},ct.transformQuat=function(e,t,r){return Qe.transformQuat(e.array,t.array,r.array),e._dirty=!0,e};var mt=Math.atan2,gt=Math.asin,_t=Math.abs;ct.eulerFromQuat=function(e,t,r){e._dirty=!0,t=t.array;var i=e.array,n=t[0],a=t[1],o=t[2],s=t[3],l=n*n,h=a*a,u=o*o,c=s*s;switch(r=(r||\"XYZ\").toUpperCase()){case\"XYZ\":i[0]=mt(2*(n*s-a*o),c-l-h+u),i[1]=gt(pt(2*(n*o+a*s),-1,1)),i[2]=mt(2*(o*s-n*a),c+l-h-u);break;case\"YXZ\":i[0]=gt(pt(2*(n*s-a*o),-1,1)),i[1]=mt(2*(n*o+a*s),c-l-h+u),i[2]=mt(2*(n*a+o*s),c-l+h-u);break;case\"ZXY\":i[0]=gt(pt(2*(n*s+a*o),-1,1)),i[1]=mt(2*(a*s-o*n),c-l-h+u),i[2]=mt(2*(o*s-n*a),c-l+h-u);break;case\"ZYX\":i[0]=mt(2*(n*s+o*a),c-l-h+u),i[1]=gt(pt(2*(a*s-n*o),-1,1)),i[2]=mt(2*(n*a+o*s),c+l-h-u);break;case\"YZX\":i[0]=mt(2*(n*s-o*a),c-l+h-u),i[1]=mt(2*(a*s-n*o),c+l-h-u),i[2]=gt(pt(2*(n*a+o*s),-1,1));break;case\"XZY\":i[0]=mt(2*(n*s+a*o),c-l+h-u),i[1]=mt(2*(n*o+a*s),c+l-h-u),i[2]=gt(pt(2*(o*s-n*a),-1,1));break;default:console.warn(\"Unkown order: \"+r)}return e},ct.eulerFromMat3=function(e,t,r){var i=t.array,n=i[0],a=i[3],o=i[6],s=i[1],l=i[4],h=i[7],u=i[2],c=i[5],d=i[8],f=e.array;switch(r=(r||\"XYZ\").toUpperCase()){case\"XYZ\":f[1]=gt(pt(o,-1,1)),_t(o)<.99999?(f[0]=mt(-h,d),f[2]=mt(-a,n)):(f[0]=mt(c,l),f[2]=0);break;case\"YXZ\":f[0]=gt(-pt(h,-1,1)),_t(h)<.99999?(f[1]=mt(o,d),f[2]=mt(s,l)):(f[1]=mt(-u,n),f[2]=0);break;case\"ZXY\":f[0]=gt(pt(c,-1,1)),_t(c)<.99999?(f[1]=mt(-u,d),f[2]=mt(-a,l)):(f[1]=0,f[2]=mt(s,n));break;case\"ZYX\":f[1]=gt(-pt(u,-1,1)),_t(u)<.99999?(f[0]=mt(c,d),f[2]=mt(s,n)):(f[0]=0,f[2]=mt(-a,l));break;case\"YZX\":f[2]=gt(pt(s,-1,1)),_t(s)<.99999?(f[0]=mt(-h,l),f[1]=mt(-u,n)):(f[0]=0,f[1]=mt(o,d));break;case\"XZY\":f[2]=gt(-pt(a,-1,1)),_t(a)<.99999?(f[0]=mt(c,l),f[1]=mt(o,n)):(f[0]=mt(-h,d),f[1]=0);break;default:console.warn(\"Unkown order: \"+r)}return e._dirty=!0,e},Object.defineProperties(ct,{POSITIVE_X:{get:function(){return new ct(1,0,0)}},NEGATIVE_X:{get:function(){return new ct(-1,0,0)}},POSITIVE_Y:{get:function(){return new ct(0,1,0)}},NEGATIVE_Y:{get:function(){return new ct(0,-1,0)}},POSITIVE_Z:{get:function(){return new ct(0,0,1)}},NEGATIVE_Z:{get:function(){return new ct(0,0,-1)}},UP:{get:function(){return new ct(0,1,0)}},ZERO:{get:function(){return new ct}}});const vt=ct;var yt,xt,bt,wt,Tt,St=function(e,t){this.origin=e||new vt,this.direction=t||new vt};St.prototype={constructor:St,intersectPlane:function(e,t){var r=e.normal.array,i=e.distance,n=this.origin.array,a=this.direction.array,o=Qe.dot(r,a);if(0===o)return null;t||(t=new vt);var s=(Qe.dot(r,n)-i)/o;return Qe.scaleAndAdd(t.array,n,a,-s),t._dirty=!0,t},mirrorAgainstPlane:function(e){var t=Qe.dot(e.normal.array,this.direction.array);Qe.scaleAndAdd(this.direction.array,this.direction.array,e.normal.array,2*-t),this.direction._dirty=!0},distanceToPoint:(Tt=Qe.create(),function(e){Qe.sub(Tt,e,this.origin.array);var t=Qe.dot(Tt,this.direction.array);if(t<0)return Qe.distance(this.origin.array,e);var r=Qe.lenSquared(Tt);return Math.sqrt(r-t*t)}),intersectSphere:function(){var e=Qe.create();return function(t,r,i){var n=this.origin.array,a=this.direction.array;t=t.array,Qe.sub(e,t,n);var o=Qe.dot(e,a),s=Qe.squaredLength(e)-o*o,l=r*r;if(!(s>l)){var h=Math.sqrt(l-s),u=o-h,c=o+h;return i||(i=new vt),u<0?c<0?null:(Qe.scaleAndAdd(i.array,n,a,c),i):(Qe.scaleAndAdd(i.array,n,a,u),i)}}}(),intersectBoundingBox:function(e,t){var r,i,n,a,o,s,l=this.direction.array,h=this.origin.array,u=e.min.array,c=e.max.array,d=1/l[0],f=1/l[1],p=1/l[2];if(d>=0?(r=(u[0]-h[0])*d,i=(c[0]-h[0])*d):(i=(u[0]-h[0])*d,r=(c[0]-h[0])*d),f>=0?(n=(u[1]-h[1])*f,a=(c[1]-h[1])*f):(a=(u[1]-h[1])*f,n=(c[1]-h[1])*f),r>a||n>i)return null;if((n>r||r!=r)&&(r=n),(a=0?(o=(u[2]-h[2])*p,s=(c[2]-h[2])*p):(s=(u[2]-h[2])*p,o=(c[2]-h[2])*p),r>s||o>i)return null;if((o>r||r!=r)&&(r=o),(s=0?r:i;return t||(t=new vt),Qe.scaleAndAdd(t.array,h,l,m),t},intersectTriangle:(yt=Qe.create(),xt=Qe.create(),bt=Qe.create(),wt=Qe.create(),function(e,t,r,i,n,a){var o=this.direction.array,s=this.origin.array;e=e.array,t=t.array,r=r.array,Qe.sub(yt,t,e),Qe.sub(xt,r,e),Qe.cross(wt,xt,o);var l=Qe.dot(yt,wt);if(i){if(l>-1e-5)return null}else if(l>-1e-5&&l<1e-5)return null;Qe.sub(bt,s,e);var h=Qe.dot(wt,bt)/l;if(h<0||h>1)return null;Qe.cross(wt,yt,bt);var u=Qe.dot(o,wt)/l;if(u<0||u>1||h+u>1)return null;Qe.cross(wt,yt,xt);var c=-Qe.dot(bt,wt)/l;return c<0?null:(n||(n=new vt),a&&vt.set(a,1-h-u,h,u),Qe.scaleAndAdd(n.array,s,o,c),n)}),applyTransform:function(e){vt.add(this.direction,this.direction,this.origin),vt.transformMat4(this.origin,this.origin,e),vt.transformMat4(this.direction,this.direction,e),vt.sub(this.direction,this.direction,this.origin),vt.normalize(this.direction,this.direction)},copy:function(e){vt.copy(this.origin,e.origin),vt.copy(this.direction,e.direction)},clone:function(){var e=new St;return e.copy(this),e}};const Mt=St;var At={create:function(){var e=new ce(4);return e[0]=0,e[1]=0,e[2]=0,e[3]=0,e},clone:function(e){var t=new ce(4);return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t},fromValues:function(e,t,r,i){var n=new ce(4);return n[0]=e,n[1]=t,n[2]=r,n[3]=i,n},copy:function(e,t){return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e},set:function(e,t,r,i,n){return e[0]=t,e[1]=r,e[2]=i,e[3]=n,e},add:function(e,t,r){return e[0]=t[0]+r[0],e[1]=t[1]+r[1],e[2]=t[2]+r[2],e[3]=t[3]+r[3],e},subtract:function(e,t,r){return e[0]=t[0]-r[0],e[1]=t[1]-r[1],e[2]=t[2]-r[2],e[3]=t[3]-r[3],e}};At.sub=At.subtract,At.multiply=function(e,t,r){return e[0]=t[0]*r[0],e[1]=t[1]*r[1],e[2]=t[2]*r[2],e[3]=t[3]*r[3],e},At.mul=At.multiply,At.divide=function(e,t,r){return e[0]=t[0]/r[0],e[1]=t[1]/r[1],e[2]=t[2]/r[2],e[3]=t[3]/r[3],e},At.div=At.divide,At.min=function(e,t,r){return e[0]=Math.min(t[0],r[0]),e[1]=Math.min(t[1],r[1]),e[2]=Math.min(t[2],r[2]),e[3]=Math.min(t[3],r[3]),e},At.max=function(e,t,r){return e[0]=Math.max(t[0],r[0]),e[1]=Math.max(t[1],r[1]),e[2]=Math.max(t[2],r[2]),e[3]=Math.max(t[3],r[3]),e},At.scale=function(e,t,r){return e[0]=t[0]*r,e[1]=t[1]*r,e[2]=t[2]*r,e[3]=t[3]*r,e},At.scaleAndAdd=function(e,t,r,i){return e[0]=t[0]+r[0]*i,e[1]=t[1]+r[1]*i,e[2]=t[2]+r[2]*i,e[3]=t[3]+r[3]*i,e},At.distance=function(e,t){var r=t[0]-e[0],i=t[1]-e[1],n=t[2]-e[2],a=t[3]-e[3];return Math.sqrt(r*r+i*i+n*n+a*a)},At.dist=At.distance,At.squaredDistance=function(e,t){var r=t[0]-e[0],i=t[1]-e[1],n=t[2]-e[2],a=t[3]-e[3];return r*r+i*i+n*n+a*a},At.sqrDist=At.squaredDistance,At.length=function(e){var t=e[0],r=e[1],i=e[2],n=e[3];return Math.sqrt(t*t+r*r+i*i+n*n)},At.len=At.length,At.squaredLength=function(e){var t=e[0],r=e[1],i=e[2],n=e[3];return t*t+r*r+i*i+n*n},At.sqrLen=At.squaredLength,At.negate=function(e,t){return e[0]=-t[0],e[1]=-t[1],e[2]=-t[2],e[3]=-t[3],e},At.inverse=function(e,t){return e[0]=1/t[0],e[1]=1/t[1],e[2]=1/t[2],e[3]=1/t[3],e},At.normalize=function(e,t){var r=t[0],i=t[1],n=t[2],a=t[3],o=r*r+i*i+n*n+a*a;return o>0&&(o=1/Math.sqrt(o),e[0]=t[0]*o,e[1]=t[1]*o,e[2]=t[2]*o,e[3]=t[3]*o),e},At.dot=function(e,t){return e[0]*t[0]+e[1]*t[1]+e[2]*t[2]+e[3]*t[3]},At.lerp=function(e,t,r,i){var n=t[0],a=t[1],o=t[2],s=t[3];return e[0]=n+i*(r[0]-n),e[1]=a+i*(r[1]-a),e[2]=o+i*(r[2]-o),e[3]=s+i*(r[3]-s),e},At.random=function(e,t){return t=t||1,e[0]=de(),e[1]=de(),e[2]=de(),e[3]=de(),At.normalize(e,e),At.scale(e,e,t),e},At.transformMat4=function(e,t,r){var i=t[0],n=t[1],a=t[2],o=t[3];return e[0]=r[0]*i+r[4]*n+r[8]*a+r[12]*o,e[1]=r[1]*i+r[5]*n+r[9]*a+r[13]*o,e[2]=r[2]*i+r[6]*n+r[10]*a+r[14]*o,e[3]=r[3]*i+r[7]*n+r[11]*a+r[15]*o,e},At.transformQuat=function(e,t,r){var i=t[0],n=t[1],a=t[2],o=r[0],s=r[1],l=r[2],h=r[3],u=h*i+s*a-l*n,c=h*n+l*i-o*a,d=h*a+o*n-s*i,f=-o*i-s*n-l*a;return e[0]=u*h+f*-o+c*-l-d*-s,e[1]=c*h+f*-s+d*-o-u*-l,e[2]=d*h+f*-l+u*-s-c*-o,e},At.forEach=function(){var e=At.create();return function(t,r,i,n,a,o){var s,l;for(r||(r=4),i||(i=0),l=n?Math.min(n*r+i,t.length):t.length,s=i;s.999999?(e[0]=0,e[1]=0,e[2]=0,e[3]=1,e):(Qe.cross(Lt,t,r),e[0]=Lt[0],e[1]=Lt[1],e[2]=Lt[2],e[3]=1+i,It.normalize(e,e))}),It.setAxes=(Nt=Dt.create(),function(e,t,r,i){return Nt[0]=r[0],Nt[3]=r[1],Nt[6]=r[2],Nt[1]=i[0],Nt[4]=i[1],Nt[7]=i[2],Nt[2]=-t[0],Nt[5]=-t[1],Nt[8]=-t[2],It.normalize(e,It.fromMat3(e,Nt))}),It.clone=Et.clone,It.fromValues=Et.fromValues,It.copy=Et.copy,It.set=Et.set,It.identity=function(e){return e[0]=0,e[1]=0,e[2]=0,e[3]=1,e},It.setAxisAngle=function(e,t,r){r*=.5;var i=Math.sin(r);return e[0]=i*t[0],e[1]=i*t[1],e[2]=i*t[2],e[3]=Math.cos(r),e},It.add=Et.add,It.multiply=function(e,t,r){var i=t[0],n=t[1],a=t[2],o=t[3],s=r[0],l=r[1],h=r[2],u=r[3];return e[0]=i*u+o*s+n*h-a*l,e[1]=n*u+o*l+a*s-i*h,e[2]=a*u+o*h+i*l-n*s,e[3]=o*u-i*s-n*l-a*h,e},It.mul=It.multiply,It.scale=Et.scale,It.rotateX=function(e,t,r){r*=.5;var i=t[0],n=t[1],a=t[2],o=t[3],s=Math.sin(r),l=Math.cos(r);return e[0]=i*l+o*s,e[1]=n*l+a*s,e[2]=a*l-n*s,e[3]=o*l-i*s,e},It.rotateY=function(e,t,r){r*=.5;var i=t[0],n=t[1],a=t[2],o=t[3],s=Math.sin(r),l=Math.cos(r);return e[0]=i*l-a*s,e[1]=n*l+o*s,e[2]=a*l+i*s,e[3]=o*l-n*s,e},It.rotateZ=function(e,t,r){r*=.5;var i=t[0],n=t[1],a=t[2],o=t[3],s=Math.sin(r),l=Math.cos(r);return e[0]=i*l+n*s,e[1]=n*l-i*s,e[2]=a*l+o*s,e[3]=o*l-a*s,e},It.calculateW=function(e,t){var r=t[0],i=t[1],n=t[2];return e[0]=r,e[1]=i,e[2]=n,e[3]=Math.sqrt(Math.abs(1-r*r-i*i-n*n)),e},It.dot=Et.dot,It.lerp=Et.lerp,It.slerp=function(e,t,r,i){var n,a,o,s,l,h=t[0],u=t[1],c=t[2],d=t[3],f=r[0],p=r[1],m=r[2],g=r[3];return(a=h*f+u*p+c*m+d*g)<0&&(a=-a,f=-f,p=-p,m=-m,g=-g),1-a>1e-6?(n=Math.acos(a),o=Math.sin(n),s=Math.sin((1-i)*n)/o,l=Math.sin(i*n)/o):(s=1-i,l=i),e[0]=s*h+l*f,e[1]=s*u+l*p,e[2]=s*c+l*m,e[3]=s*d+l*g,e},It.invert=function(e,t){var r=t[0],i=t[1],n=t[2],a=t[3],o=r*r+i*i+n*n+a*a,s=o?1/o:0;return e[0]=-r*s,e[1]=-i*s,e[2]=-n*s,e[3]=a*s,e},It.conjugate=function(e,t){return e[0]=-t[0],e[1]=-t[1],e[2]=-t[2],e[3]=t[3],e},It.length=Et.length,It.len=It.length,It.squaredLength=Et.squaredLength,It.sqrLen=It.squaredLength,It.normalize=Et.normalize,It.fromMat3=function(e,t){var r,i=t[0]+t[4]+t[8];if(i>0)r=Math.sqrt(i+1),e[3]=.5*r,r=.5/r,e[0]=(t[5]-t[7])*r,e[1]=(t[6]-t[2])*r,e[2]=(t[1]-t[3])*r;else{var n=0;t[4]>t[0]&&(n=1),t[8]>t[3*n+n]&&(n=2);var a=(n+1)%3,o=(n+2)%3;r=Math.sqrt(t[3*n+n]-t[3*a+a]-t[3*o+o]+1),e[n]=.5*r,r=.5/r,e[3]=(t[3*a+o]-t[3*o+a])*r,e[a]=(t[3*a+n]+t[3*n+a])*r,e[o]=(t[3*o+n]+t[3*n+o])*r}return e};const Rt=It;var Bt,Ft,zt,Gt,Ut=function(){this._axisX=new vt,this._axisY=new vt,this._axisZ=new vt,this.array=Ye.create(),this._dirty=!0};Ut.prototype={constructor:Ut,setArray:function(e){for(var t=0;t0){var t=this.min,r=this.max,i=t.array,n=r.array;tr(i,e[0]),tr(n,e[0]);for(var a=1;an[0]&&(n[0]=o[0]),o[1]>n[1]&&(n[1]=o[1]),o[2]>n[2]&&(n[2]=o[2])}t._dirty=!0,r._dirty=!0}},union:function(e){var t=this.min,r=this.max;return Qe.min(t.array,t.array,e.min.array),Qe.max(r.array,r.array,e.max.array),t._dirty=!0,r._dirty=!0,this},intersection:function(e){var t=this.min,r=this.max;return Qe.max(t.array,t.array,e.min.array),Qe.min(r.array,r.array,e.max.array),t._dirty=!0,r._dirty=!0,this},intersectBoundingBox:function(e){var t=this.min.array,r=this.max.array,i=e.min.array,n=e.max.array;return!(t[0]>n[0]||t[1]>n[1]||t[2]>n[2]||r[0]=n[0]&&r[1]>=n[1]&&r[2]>=n[2]},containPoint:function(e){var t=this.min.array,r=this.max.array,i=e.array;return t[0]<=i[0]&&t[1]<=i[1]&&t[2]<=i[2]&&r[0]>=i[0]&&r[1]>=i[1]&&r[2]>=i[2]},isFinite:function(){var e=this.min.array,t=this.max.array;return isFinite(e[0])&&isFinite(e[1])&&isFinite(e[2])&&isFinite(t[0])&&isFinite(t[1])&&isFinite(t[2])},applyTransform:function(e){this.transformFrom(this,e)},transformFrom:(Zt=Qe.create(),Yt=Qe.create(),Kt=Qe.create(),Qt=Qe.create(),Jt=Qe.create(),$t=Qe.create(),function(e,t){var r=e.min.array,i=e.max.array,n=t.array;return Zt[0]=n[0]*r[0],Zt[1]=n[1]*r[0],Zt[2]=n[2]*r[0],Yt[0]=n[0]*i[0],Yt[1]=n[1]*i[0],Yt[2]=n[2]*i[0],Kt[0]=n[4]*r[1],Kt[1]=n[5]*r[1],Kt[2]=n[6]*r[1],Qt[0]=n[4]*i[1],Qt[1]=n[5]*i[1],Qt[2]=n[6]*i[1],Jt[0]=n[8]*r[2],Jt[1]=n[9]*r[2],Jt[2]=n[10]*r[2],$t[0]=n[8]*i[2],$t[1]=n[9]*i[2],$t[2]=n[10]*i[2],r=this.min.array,i=this.max.array,r[0]=Math.min(Zt[0],Yt[0])+Math.min(Kt[0],Qt[0])+Math.min(Jt[0],$t[0])+n[12],r[1]=Math.min(Zt[1],Yt[1])+Math.min(Kt[1],Qt[1])+Math.min(Jt[1],$t[1])+n[13],r[2]=Math.min(Zt[2],Yt[2])+Math.min(Kt[2],Qt[2])+Math.min(Jt[2],$t[2])+n[14],i[0]=Math.max(Zt[0],Yt[0])+Math.max(Kt[0],Qt[0])+Math.max(Jt[0],$t[0])+n[12],i[1]=Math.max(Zt[1],Yt[1])+Math.max(Kt[1],Qt[1])+Math.max(Jt[1],$t[1])+n[13],i[2]=Math.max(Zt[2],Yt[2])+Math.max(Kt[2],Qt[2])+Math.max(Jt[2],$t[2])+n[14],this.min._dirty=!0,this.max._dirty=!0,this}),applyProjection:function(e){var t=this.min.array,r=this.max.array,i=e.array,n=t[0],a=t[1],o=t[2],s=r[0],l=r[1],h=t[2],u=r[0],c=r[1],d=r[2];if(1===i[15])t[0]=i[0]*n+i[12],t[1]=i[5]*a+i[13],r[2]=i[10]*o+i[14],r[0]=i[0]*u+i[12],r[1]=i[5]*c+i[13],t[2]=i[10]*d+i[14];else{var f=-1/o;t[0]=i[0]*n*f,t[1]=i[5]*a*f,r[2]=(i[10]*o+i[14])*f,f=-1/h,r[0]=i[0]*s*f,r[1]=i[5]*l*f,f=-1/d,t[2]=(i[10]*d+i[14])*f}return this.min._dirty=!0,this.max._dirty=!0,this},updateVertices:function(){var e=this.vertices;if(!e){e=[];for(var t=0;t<8;t++)e[t]=Qe.fromValues(0,0,0);this.vertices=e}var r=this.min.array,i=this.max.array;return er(e[0],r[0],r[1],r[2]),er(e[1],r[0],i[1],r[2]),er(e[2],i[0],r[1],r[2]),er(e[3],i[0],i[1],r[2]),er(e[4],r[0],r[1],i[2]),er(e[5],r[0],i[1],i[2]),er(e[6],i[0],r[1],i[2]),er(e[7],i[0],i[1],i[2]),this},copy:function(e){var t=this.min,r=this.max;return tr(t.array,e.min.array),tr(r.array,e.max.array),t._dirty=!0,r._dirty=!0,this},clone:function(){var e=new rr;return e.copy(this),e}};const ir=rr;var nr,ar,or=0;const sr=m.extend({name:\"\",position:null,rotation:null,scale:null,worldTransform:null,localTransform:null,autoUpdateLocalTransform:!0,_parent:null,_scene:null,_needsUpdateWorldTransform:!0,_inIterating:!1,__depth:0},(function(){this.name||(this.name=(this.type||\"NODE\")+\"_\"+or++),this.position||(this.position=new vt),this.rotation||(this.rotation=new qt),this.scale||(this.scale=new vt(1,1,1)),this.worldTransform=new Ht,this.localTransform=new Ht,this._children=[]}),{target:null,invisible:!1,isSkinnedMesh:function(){return!1},isRenderable:function(){return!1},setName:function(e){var t=this._scene;if(t){var r=t._nodeRepository;delete r[this.name],r[e]=this}this.name=e},add:function(e){var t=e._parent;if(t!==this){t&&t.remove(e),e._parent=this,this._children.push(e);var r=this._scene;r&&r!==e.scene&&e.traverse(this._addSelfToScene,this),e._needsUpdateWorldTransform=!0}},remove:function(e){var t=this._children,r=t.indexOf(e);r<0||(t.splice(r,1),e._parent=null,this._scene&&e.traverse(this._removeSelfFromScene,this))},removeAll:function(){for(var e=this._children,t=0;t0},beforeRender:function(e){},afterRender:function(e,t){},getBoundingBox:function(e,t){return t=sr.prototype.getBoundingBox.call(this,e,t),this.geometry&&this.geometry.boundingBox&&t.union(this.geometry.boundingBox),t},clone:(lr=[\"castShadow\",\"receiveShadow\",\"mode\",\"culling\",\"cullFace\",\"frontFace\",\"frustumCulling\",\"renderOrder\",\"lineWidth\",\"ignorePicking\",\"ignorePreZ\",\"ignoreGBuffer\"],function(){var e=sr.prototype.clone.call(this);e.geometry=this.geometry,e.material=this.material;for(var t=0;t=0&&g[p]>1e-4&&(Qe.transformMat4(T,m,d[_[p]]),Qe.scaleAndAdd(v,v,T,g[p]));S.set(f,v)}}for(f=0;f>t;return e+1},dispose:function(e){var t=this._cache;t.use(e.__uid__);var r=t.get(\"webgl_texture\");r&&e.gl.deleteTexture(r),t.deleteContext(e.__uid__)},isRenderable:function(){},isPowerOfTwo:function(){}});Object.defineProperty(br.prototype,\"width\",{get:function(){return this._width},set:function(e){this._width=e}}),Object.defineProperty(br.prototype,\"height\",{get:function(){return this._height},set:function(e){this._height=e}}),br.BYTE=5120,br.UNSIGNED_BYTE=T,br.SHORT=5122,br.UNSIGNED_SHORT=5123,br.INT=5124,br.UNSIGNED_INT=5125,br.FLOAT=S,br.HALF_FLOAT=36193,br.UNSIGNED_INT_24_8_WEBGL=34042,br.DEPTH_COMPONENT=M,br.DEPTH_STENCIL=34041,br.ALPHA=6406,br.RGB=6407,br.RGBA=A,br.LUMINANCE=6409,br.LUMINANCE_ALPHA=6410,br.SRGB=35904,br.SRGB_ALPHA=35906,br.COMPRESSED_RGB_S3TC_DXT1_EXT=33776,br.COMPRESSED_RGBA_S3TC_DXT1_EXT=33777,br.COMPRESSED_RGBA_S3TC_DXT3_EXT=33778,br.COMPRESSED_RGBA_S3TC_DXT5_EXT=33779,br.NEAREST=E,br.LINEAR=C,br.NEAREST_MIPMAP_NEAREST=D,br.LINEAR_MIPMAP_NEAREST=L,br.NEAREST_MIPMAP_LINEAR=P,br.LINEAR_MIPMAP_LINEAR=O,br.REPEAT=N,br.CLAMP_TO_EDGE=I,br.MIRRORED_REPEAT=33648;const wr=br;var Tr=ur.extend({skeleton:null,joints:null},(function(){this.joints||(this.joints=[])}),{offsetMatrix:null,isInstancedMesh:function(){return!1},isSkinnedMesh:function(){return!!(this.skeleton&&this.joints&&this.joints.length>0)},clone:function(){var e=ur.prototype.clone.call(this);return e.skeleton=this.skeleton,this.joints&&(e.joints=this.joints.slice()),e}});Tr.POINTS=0,Tr.LINES=1,Tr.LINE_LOOP=2,Tr.LINE_STRIP=3,Tr.TRIANGLES=4,Tr.TRIANGLE_STRIP=5,Tr.TRIANGLE_FAN=6,Tr.BACK=x,Tr.FRONT=y,Tr.FRONT_AND_BACK=1032,Tr.CW=b,Tr.CCW=w;const Sr=Tr;const Mr={isPowerOfTwo:function(e){return 0==(e&e-1)},nextPowerOfTwo:function(e){return e--,e|=e>>1,e|=e>>2,e|=e>>4,e|=e>>8,e|=e>>16,++e},nearestPowerOfTwo:function(e){return Math.pow(2,Math.round(Math.log(e)/Math.LN2))}};var Ar=Mr.isPowerOfTwo;function Er(e){return Math.pow(2,Math.round(Math.log(e)/Math.LN2))}var Cr=wr.extend((function(){return{image:null,pixels:null,mipmaps:[],convertToPOT:!1}}),{textureType:\"texture2D\",update:function(e){var t=e.gl;t.bindTexture(t.TEXTURE_2D,this._cache.get(\"webgl_texture\")),this.updateCommon(e);var r=this.format,i=this.type,n=!(!this.convertToPOT||this.mipmaps.length||!this.image||this.wrapS!==wr.REPEAT&&this.wrapT!==wr.REPEAT||!this.NPOT);t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,n?this.wrapS:this.getAvailableWrapS()),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,n?this.wrapT:this.getAvailableWrapT()),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,n?this.magFilter:this.getAvailableMagFilter()),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,n?this.minFilter:this.getAvailableMinFilter());var a=e.getGLExtension(\"EXT_texture_filter_anisotropic\");if(a&&this.anisotropic>1&&t.texParameterf(t.TEXTURE_2D,a.TEXTURE_MAX_ANISOTROPY_EXT,this.anisotropic),36193===i&&(e.getGLExtension(\"OES_texture_half_float\")||(i=S)),this.mipmaps.length)for(var o=this.width,s=this.height,l=0;l=wr.COMPRESSED_RGB_S3TC_DXT1_EXT?e.compressedTexImage2D(e.TEXTURE_2D,r,a,i,n,0,t.pixels):e.texImage2D(e.TEXTURE_2D,r,a,i,n,0,a,o,t.pixels)},generateMipmap:function(e){var t=e.gl;this.useMipmap&&!this.NPOT&&(t.bindTexture(t.TEXTURE_2D,this._cache.get(\"webgl_texture\")),t.generateMipmap(t.TEXTURE_2D))},isPowerOfTwo:function(){return Ar(this.width)&&Ar(this.height)},isRenderable:function(){return this.image?this.image.width>0&&this.image.height>0:!(!this.width||!this.height)},bind:function(e){e.gl.bindTexture(e.gl.TEXTURE_2D,this.getWebGLTexture(e))},unbind:function(e){e.gl.bindTexture(e.gl.TEXTURE_2D,null)},load:function(e,t){var r=U.createImage();t&&(r.crossOrigin=t);var i=this;return r.onload=function(){i.dirty(),i.trigger(\"success\",i)},r.onerror=function(){i.trigger(\"error\",i)},r.src=e,this.image=r,this}});Object.defineProperty(Cr.prototype,\"width\",{get:function(){return this.image?this.image.width:this._width},set:function(e){this.image?console.warn(\"Texture from image can't set width\"):(this._width!==e&&this.dirty(),this._width=e)}}),Object.defineProperty(Cr.prototype,\"height\",{get:function(){return this.image?this.image.height:this._height},set:function(e){this.image?console.warn(\"Texture from image can't set height\"):(this._height!==e&&this.dirty(),this._height=e)}});const Dr=Cr;function Lr(e){return{byte:U.Int8Array,ubyte:U.Uint8Array,short:U.Int16Array,ushort:U.Uint16Array}[e]||U.Float32Array}function Pr(e){return\"attr_\"+e}function Or(e,t,r,i){switch(this.name=e,this.type=t,this.size=r,this.semantic=i||\"\",this.value=null,r){case 1:this.get=function(e){return this.value[e]},this.set=function(e,t){this.value[e]=t},this.copy=function(e,t){this.value[e]=this.value[e]};break;case 2:this.get=function(e,t){var r=this.value;return t[0]=r[2*e],t[1]=r[2*e+1],t},this.set=function(e,t){var r=this.value;r[2*e]=t[0],r[2*e+1]=t[1]},this.copy=function(e,t){var r=this.value;t*=2,r[e*=2]=r[t],r[e+1]=r[t+1]};break;case 3:this.get=function(e,t){var r=3*e,i=this.value;return t[0]=i[r],t[1]=i[r+1],t[2]=i[r+2],t},this.set=function(e,t){var r=3*e,i=this.value;i[r]=t[0],i[r+1]=t[1],i[r+2]=t[2]},this.copy=function(e,t){var r=this.value;t*=3,r[e*=3]=r[t],r[e+1]=r[t+1],r[e+2]=r[t+2]};break;case 4:this.get=function(e,t){var r=this.value,i=4*e;return t[0]=r[i],t[1]=r[i+1],t[2]=r[i+2],t[3]=r[i+3],t},this.set=function(e,t){var r=this.value,i=4*e;r[i]=t[0],r[i+1]=t[1],r[i+2]=t[2],r[i+3]=t[3]},this.copy=function(e,t){var r=this.value;t*=4,r[e*=4]=r[t],r[e+1]=r[t+1],r[e+2]=r[t+2],r[e+3]=r[t+3]}}}function Nr(e,t,r,i,n){this.name=e,this.type=t,this.buffer=r,this.size=i,this.semantic=n,this.symbol=\"\",this.needsRemove=!1}function Ir(e){this.buffer=e,this.count=0}Or.prototype.init=function(e){if(!this.value||this.value.length!==e*this.size){var t=Lr(this.type);this.value=new t(e*this.size)}},Or.prototype.fromArray=function(e){var t,r=Lr(this.type);if(e[0]&&e[0].length){var i=0,n=this.size;t=new r(e.length*n);for(var a=0;a=0){t||(t=[]);var r=this.indices;return t[0]=r[3*e],t[1]=r[3*e+1],t[2]=r[3*e+2],t}},setTriangleIndices:function(e,t){var r=this.indices;r[3*e]=t[0],r[3*e+1]=t[1],r[3*e+2]=t[2]},isUseIndices:function(){return!!this.indices},initIndicesFromArray:function(e){var t,r=this.vertexCount>65535?U.Uint32Array:U.Uint16Array;if(e[0]&&e[0].length){var i=0;t=new r(3*e.length);for(var n=0;n=0&&(t.splice(r,1),delete this.attributes[e],!0)},getAttribute:function(e){return this.attributes[e]},getEnabledAttributes:function(){var e=this._enabledAttributes,t=this._attributeList;if(e)return e;for(var r=[],i=this.vertexCount,n=0;na[0]&&(a[0]=s),l>a[1]&&(a[1]=l),h>a[2]&&(a[2]=h)}r._dirty=!0,i._dirty=!0}},generateVertexNormals:function(){if(this.vertexCount){var e=this.indices,t=this.attributes,r=t.position.value,i=t.normal.value;if(i&&i.length===r.length)for(var n=0;n65535&&(this.indices=new U.Uint32Array(this.indices));for(var e=this.attributes,t=this.indices,r=this.getEnabledAttributes(),i={},n=0;nthis.distance,n=1;n<8;n++)if(Qe.dot(t[n].array,r)>this.distance!=i)return!0},intersectLine:(Qr=Qe.create(),function(e,t,r){var i=this.distanceToPoint(e),n=this.distanceToPoint(t);if(i>0&&n>0||i<0&&n<0)return null;var a=this.normal.array,o=this.distance,s=e.array;Qe.sub(Qr,t.array,e.array),Qe.normalize(Qr,Qr);var l=Qe.dot(a,Qr);if(0===l)return null;r||(r=new vt);var h=(Qe.dot(a,s)-o)/l;return Qe.scaleAndAdd(r.array,s,Qr,-h),r._dirty=!0,r}),applyTransform:(Zr=Ye.create(),Yr=Et.create(),Kr=Et.create(),Kr[3]=1,function(e){e=e.array,Qe.scale(Kr,this.normal.array,this.distance),Et.transformMat4(Kr,Kr,e),this.distance=Qe.dot(Kr,this.normal.array),Ye.invert(Zr,e),Ye.transpose(Zr,Zr),Yr[3]=0,Qe.copy(Yr,this.normal.array),Et.transformMat4(Yr,Yr,Zr),Qe.copy(this.normal.array,Yr)}),copy:function(e){Qe.copy(this.normal.array,e.normal.array),this.normal._dirty=!0,this.distance=e.distance},clone:function(){var e=new Jr;return e.copy(this),e}};const $r=Jr;var ei,ti=Qe.set,ri=Qe.copy,ii=Qe.transformMat4,ni=Math.min,ai=Math.max,oi=function(){this.planes=[];for(var e=0;e<6;e++)this.planes.push(new $r);for(this.boundingBox=new ir,this.vertices=[],e=0;e<8;e++)this.vertices[e]=Qe.fromValues(0,0,0)};oi.prototype={setFromProjection:function(e){var t=this.planes,r=e.array,i=r[0],n=r[1],a=r[2],o=r[3],s=r[4],l=r[5],h=r[6],u=r[7],c=r[8],d=r[9],f=r[10],p=r[11],m=r[12],g=r[13],_=r[14],v=r[15];ti(t[0].normal.array,o-i,u-s,p-c),t[0].distance=-(v-m),t[0].normalize(),ti(t[1].normal.array,o+i,u+s,p+c),t[1].distance=-(v+m),t[1].normalize(),ti(t[2].normal.array,o+n,u+l,p+d),t[2].distance=-(v+g),t[2].normalize(),ti(t[3].normal.array,o-n,u-l,p-d),t[3].distance=-(v-g),t[3].normalize(),ti(t[4].normal.array,o-a,u-h,p-f),t[4].distance=-(v-_),t[4].normalize(),ti(t[5].normal.array,o+a,u+h,p+f),t[5].distance=-(v+_),t[5].normalize();var y=this.boundingBox,x=this.vertices;if(0===v){var b=l/i,w=-_/(f-1),T=-_/(f+1),S=-T/l,M=-w/l;y.min.set(-S*b,-S,T),y.max.set(S*b,S,w),ti(x[0],-S*b,-S,T),ti(x[1],-S*b,S,T),ti(x[2],S*b,-S,T),ti(x[3],S*b,S,T),ti(x[4],-M*b,-M,w),ti(x[5],-M*b,M,w),ti(x[6],M*b,-M,w),ti(x[7],M*b,M,w)}else{var A=(-1-m)/i,E=(1-m)/i,C=(1-g)/l,D=(-1-g)/l,L=(-1-_)/f,P=(1-_)/f;y.min.set(Math.min(A,E),Math.min(D,C),Math.min(P,L)),y.max.set(Math.max(E,A),Math.max(C,D),Math.max(L,P));var O=y.min.array,N=y.max.array;ti(x[0],O[0],O[1],O[2]),ti(x[1],O[0],N[1],O[2]),ti(x[2],N[0],O[1],O[2]),ti(x[3],N[0],N[1],O[2]),ti(x[4],O[0],O[1],N[2]),ti(x[5],O[0],N[1],N[2]),ti(x[6],N[0],O[1],N[2]),ti(x[7],N[0],N[1],N[2])}},getTransformedBoundingBox:(ei=Qe.create(),function(e,t){var r=this.vertices,i=t.array,n=e.min,a=e.max,o=n.array,s=a.array,l=r[0];ii(ei,l,i),ri(o,ei),ri(s,ei);for(var h=1;h<8;h++)l=r[h],ii(ei,l,i),o[0]=ni(ei[0],o[0]),o[1]=ni(ei[1],o[1]),o[2]=ni(ei[2],o[2]),s[0]=ai(ei[0],s[0]),s[1]=ai(ei[1],s[1]),s[2]=ai(ei[2],s[2]);return n._dirty=!0,a._dirty=!0,e})};const si=oi;var li;const hi=sr.extend((function(){return{projectionMatrix:new Ht,invProjectionMatrix:new Ht,viewMatrix:new Ht,frustum:new si}}),(function(){this.update(!0)}),{update:function(e){sr.prototype.update.call(this,e),Ht.invert(this.viewMatrix,this.worldTransform),this.updateProjectionMatrix(),Ht.invert(this.invProjectionMatrix,this.projectionMatrix),this.frustum.setFromProjection(this.projectionMatrix)},setViewMatrix:function(e){Ht.copy(this.viewMatrix,e),Ht.invert(this.worldTransform,e),this.decomposeWorldTransform()},decomposeProjectionMatrix:function(){},setProjectionMatrix:function(e){Ht.copy(this.projectionMatrix,e),Ht.invert(this.invProjectionMatrix,e),this.decomposeProjectionMatrix()},updateProjectionMatrix:function(){},castRay:(li=Et.create(),function(e,t){var r=void 0!==t?t:new Mt,i=e.array[0],n=e.array[1];return Et.set(li,i,n,-1,1),Et.transformMat4(li,li,this.invProjectionMatrix.array),Et.transformMat4(li,li,this.worldTransform.array),Qe.scale(r.origin.array,li,1/li[3]),Et.set(li,i,n,1,1),Et.transformMat4(li,li,this.invProjectionMatrix.array),Et.transformMat4(li,li,this.worldTransform.array),Qe.scale(li,li,1/li[3]),Qe.sub(r.direction.array,li,r.origin.array),Qe.normalize(r.direction.array,r.direction.array),r.direction._dirty=!0,r.origin._dirty=!0,r})});var ui,ci,di=Ye.create(),fi=Ye.create(),pi={};function mi(e){var t=[],r=Object.keys(e);r.sort();for(var i=0;i0&&console.warn(\"Found multiple camera in one scene. Use the fist one.\"),this._cameraList.push(e)):e instanceof qr&&this.lights.push(e),e.name&&(this._nodeRepository[e.name]=e)},removeFromScene:function(e){var t;e instanceof hi?(t=this._cameraList.indexOf(e))>=0&&this._cameraList.splice(t,1):e instanceof qr&&(t=this.lights.indexOf(e))>=0&&this.lights.splice(t,1),e.name&&delete this._nodeRepository[e.name]},getNode:function(e){return this._nodeRepository[e]},setMainCamera:function(e){var t=this._cameraList.indexOf(e);t>=0&&this._cameraList.splice(t,1),this._cameraList.unshift(e)},getMainCamera:function(){return this._cameraList[0]},getLights:function(){return this.lights},updateLights:function(){var e=this.lights;this._previousLightNumber=this._lightNumber;for(var t={},r=0;r0&&this._doUpdateRenderList(o,t,r,i,n)}},isFrustumCulled:(ui=new ir,ci=new Ht,function(e,t,r){var i=e.boundingBox;if(i||(i=e.skeleton&&e.skeleton.boundingBox?e.skeleton.boundingBox:e.geometry.boundingBox),!i)return!1;if(ci.array=r,ui.transformFrom(i,ci),e.castShadow&&this.viewBoundingBoxLastFrame.union(ui),e.frustumCulling){if(!ui.intersectBoundingBox(t.frustum.boundingBox))return!0;ci.array=t.projectionMatrix.array,ui.max.array[2]>0&&ui.min.array[2]<0&&(ui.max.array[2]=-1e-20),ui.applyProjection(ci);var n=ui.min.array,a=ui.max.array;if(a[0]<-1||n[0]>1||a[1]<-1||n[1]>1||a[2]<-1||n[2]>1)return!0}return!1}),_updateLightUniforms:function(){var e=this.lights;e.sort(_i);var t=this._lightUniforms;for(var r in t)for(var i in t[r])t[r][i].value.length=0;for(var n=0;n=this._maxSize&&a>0){var s=r.head;r.remove(s),delete i[s.key],n=s.value,this._lastRemovedEntry=s}o?o.value=t:o=new yi(t),o.key=e,r.insertEntry(o),i[e]=o}return n},e.prototype.get=function(e){var t=this._map[e],r=this._list;if(null!=t)return t!==r.tail&&(r.remove(t),r.insertEntry(t)),t.value},e.prototype.clear=function(){this._list.clear(),this._map={}},e.prototype.len=function(){return this._list.len()},e}();var wi=Mr.isPowerOfTwo,Ti=[\"px\",\"nx\",\"py\",\"ny\",\"pz\",\"nz\"],Si=wr.extend((function(){return{image:{px:null,nx:null,py:null,ny:null,pz:null,nz:null},pixels:{px:null,nx:null,py:null,ny:null,pz:null,nz:null},mipmaps:[]}}),{textureType:\"textureCube\",update:function(e){var t=e.gl;t.bindTexture(t.TEXTURE_CUBE_MAP,this._cache.get(\"webgl_texture\")),this.updateCommon(e);var r=this.format,i=this.type;t.texParameteri(t.TEXTURE_CUBE_MAP,t.TEXTURE_WRAP_S,this.getAvailableWrapS()),t.texParameteri(t.TEXTURE_CUBE_MAP,t.TEXTURE_WRAP_T,this.getAvailableWrapT()),t.texParameteri(t.TEXTURE_CUBE_MAP,t.TEXTURE_MAG_FILTER,this.getAvailableMagFilter()),t.texParameteri(t.TEXTURE_CUBE_MAP,t.TEXTURE_MIN_FILTER,this.getAvailableMinFilter());var n=e.getGLExtension(\"EXT_texture_filter_anisotropic\");if(n&&this.anisotropic>1&&t.texParameterf(t.TEXTURE_CUBE_MAP,n.TEXTURE_MAX_ANISOTROPY_EXT,this.anisotropic),36193===i&&(e.getGLExtension(\"OES_texture_half_float\")||(i=S)),this.mipmaps.length)for(var a=this.width,o=this.height,s=0;s0&&e.height>0}Object.defineProperty(Si.prototype,\"width\",{get:function(){return this.image&&this.image.px?this.image.px.width:this._width},set:function(e){this.image&&this.image.px?console.warn(\"Texture from image can't set width\"):(this._width!==e&&this.dirty(),this._width=e)}}),Object.defineProperty(Si.prototype,\"height\",{get:function(){return this.image&&this.image.px?this.image.px.height:this._height},set:function(e){this.image&&this.image.px?console.warn(\"Texture from image can't set height\"):(this._height!==e&&this.dirty(),this._height=e)}});const Ai=Si,Ei=hi.extend({fov:50,aspect:1,near:.1,far:2e3},{updateProjectionMatrix:function(){var e=this.fov/180*Math.PI;this.projectionMatrix.perspective(e,this.aspect,this.near,this.far)},decomposeProjectionMatrix:function(){var e=this.projectionMatrix.array,t=2*Math.atan(1/e[5]);this.fov=t/Math.PI*180,this.aspect=e[5]/e[0],this.near=e[14]/(e[10]-1),this.far=e[14]/(e[10]+1)},clone:function(){var e=hi.prototype.clone.call(this);return e.fov=this.fov,e.aspect=this.aspect,e.near=this.near,e.far=this.far,e}});var Ci=\"framebuffer\",Di=\"renderbuffer\",Li=\"renderbuffer_width\",Pi=\"renderbuffer_height\",Oi=\"renderbuffer_attached\",Ni=\"depthtexture_attached\",Ii=36160,Ri=36161,Bi=36096,Fi=m.extend({depthBuffer:!0,viewport:null,_width:0,_height:0,_textures:null,_boundRenderer:null},(function(){this._cache=new xr,this._textures={}}),{getTextureWidth:function(){return this._width},getTextureHeight:function(){return this._height},bind:function(e){if(e.__currentFrameBuffer){if(e.__currentFrameBuffer===this)return;console.warn(\"Renderer already bound with another framebuffer. Unbind it first\")}e.__currentFrameBuffer=this;var t=e.gl;t.bindFramebuffer(Ii,this._getFrameBufferGL(e)),this._boundRenderer=e;var r=this._cache;r.put(\"viewport\",e.viewport);var i,n,a=!1;for(var o in this._textures){a=!0;var s=this._textures[o];s&&(i=s.texture.width,n=s.texture.height,this._doAttach(e,s.texture,o,s.target))}this._width=i,this._height=n,!a&&this.depthBuffer&&console.error(\"Must attach texture before bind, or renderbuffer may have incorrect width and height.\"),this.viewport?e.setViewport(this.viewport):e.setViewport(0,0,i,n,1);var l=r.get(\"attached_textures\");if(l)for(var o in l)if(!this._textures[o]){var h=l[o];this._doDetach(t,o,h)}if(!r.get(Ni)&&this.depthBuffer){r.miss(Di)&&r.put(Di,t.createRenderbuffer());var u=r.get(Di);i===r.get(Li)&&n===r.get(Pi)||(t.bindRenderbuffer(Ri,u),t.renderbufferStorage(Ri,t.DEPTH_COMPONENT16,i,n),r.put(Li,i),r.put(Pi,n),t.bindRenderbuffer(Ri,null)),r.get(Oi)||(t.framebufferRenderbuffer(Ii,Bi,Ri,u),r.put(Oi,!0))}},unbind:function(e){e.__currentFrameBuffer=null,e.gl.bindFramebuffer(Ii,null),this._boundRenderer=null,this._cache.use(e.__uid__);var t=this._cache.get(\"viewport\");t&&e.setViewport(t),this.updateMipmap(e)},updateMipmap:function(e){var t=e.gl;for(var r in this._textures){var i=this._textures[r];if(i){var n=i.texture;if(!n.NPOT&&n.useMipmap&&n.minFilter===wr.LINEAR_MIPMAP_LINEAR){var a=\"textureCube\"===n.textureType?34067:3553;t.bindTexture(a,n.getWebGLTexture(e)),t.generateMipmap(a),t.bindTexture(a,null)}}}},checkStatus:function(e){return e.checkFramebufferStatus(Ii)},_getFrameBufferGL:function(e){var t=this._cache;return t.use(e.__uid__),t.miss(Ci)&&t.put(Ci,e.gl.createFramebuffer()),t.get(Ci)},attach:function(e,t,r){if(!e.width)throw new Error(\"The texture attached to color buffer is not a valid.\");t=t||36064,r=r||3553;var i,n=this._boundRenderer;if(n&&n.gl){var a=this._cache;a.use(n.__uid__),i=a.get(\"attached_textures\")}var o=this._textures[t];if(!o||o.target!==r||o.texture!==e||!i||null==i[t]){var s=!0;n&&(s=this._doAttach(n,e,t,r),this.viewport||n.setViewport(0,0,e.width,e.height,1)),s&&(this._textures[t]=this._textures[t]||{},this._textures[t].texture=e,this._textures[t].target=r)}},_doAttach:function(e,t,r,i){var n=e.gl,a=t.getWebGLTexture(e),o=this._cache.get(\"attached_textures\");if(o&&o[r]){var s=o[r];if(s.texture===t&&s.target===i)return}var l=!0;if(((r=+r)===Bi||r===R)&&(e.getGLExtension(\"WEBGL_depth_texture\")||(console.error(\"Depth texture is not supported by the browser\"),l=!1),t.format!==M&&34041!==t.format&&(console.error(\"The texture attached to depth buffer is not a valid.\"),l=!1),l)){var h=this._cache.get(Di);h&&(n.framebufferRenderbuffer(Ii,Bi,Ri,null),n.deleteRenderbuffer(h),this._cache.put(Di,!1)),this._cache.put(Oi,!1),this._cache.put(Ni,!0)}return n.framebufferTexture2D(Ii,r,i,a,0),o||(o={},this._cache.put(\"attached_textures\",o)),o[r]=o[r]||{},o[r].texture=t,o[r].target=i,l},_doDetach:function(e,t,r){e.framebufferTexture2D(Ii,t,r,null,0);var i=this._cache.get(\"attached_textures\");i&&i[t]&&(i[t]=null),t!==Bi&&t!==R||this._cache.put(Ni,!1)},detach:function(e,t){this._textures[e]=null,this._boundRenderer&&(this._cache.use(this._boundRenderer.__uid__),this._doDetach(this._boundRenderer.gl,e,t))},dispose:function(e){var t=e.gl,r=this._cache;r.use(e.__uid__);var i=r.get(Di);i&&t.deleteRenderbuffer(i);var n=r.get(Ci);n&&t.deleteFramebuffer(n),r.deleteContext(e.__uid__),this._textures={}}});Fi.DEPTH_ATTACHMENT=Bi,Fi.COLOR_ATTACHMENT0=36064,Fi.STENCIL_ATTACHMENT=36128,Fi.DEPTH_STENCIL_ATTACHMENT=R;const zi=Fi;var Gi=[\"px\",\"nx\",\"py\",\"ny\",\"pz\",\"nz\"];const Ui=m.extend((function(){var e={position:new vt,far:1e3,near:.1,texture:null,shadowMapPass:null},t=e._cameras={px:new Ei({fov:90}),nx:new Ei({fov:90}),py:new Ei({fov:90}),ny:new Ei({fov:90}),pz:new Ei({fov:90}),nz:new Ei({fov:90})};return t.px.lookAt(vt.POSITIVE_X,vt.NEGATIVE_Y),t.nx.lookAt(vt.NEGATIVE_X,vt.NEGATIVE_Y),t.py.lookAt(vt.POSITIVE_Y,vt.POSITIVE_Z),t.ny.lookAt(vt.NEGATIVE_Y,vt.NEGATIVE_Z),t.pz.lookAt(vt.POSITIVE_Z,vt.NEGATIVE_Y),t.nz.lookAt(vt.NEGATIVE_Z,vt.NEGATIVE_Y),e._frameBuffer=new zi,e}),{getCamera:function(e){return this._cameras[e]},render:function(e,t,r){var i=e.gl;r||t.update();for(var n=this.texture.width,a=2*Math.atan(n/(n-.5))/Math.PI*180,o=0;o<6;o++){var s=Gi[o],l=this._cameras[s];if(vt.copy(l.position,this.position),l.far=this.far,l.near=this.near,l.fov=a,this.shadowMapPass){l.update();var h=t.getBoundingBox();h.applyTransform(l.viewMatrix),t.viewBoundingBoxLastFrame.copy(h),this.shadowMapPass.render(e,t,l,!0)}this._frameBuffer.attach(this.texture,i.COLOR_ATTACHMENT0,i.TEXTURE_CUBE_MAP_POSITIVE_X+o),this._frameBuffer.bind(e),e.render(t,l,!0),this._frameBuffer.unbind(e)}},dispose:function(e){this._frameBuffer.dispose(e)}}),ki=Vr.extend({dynamic:!1,widthSegments:1,heightSegments:1},(function(){this.build()}),{build:function(){for(var e=this.heightSegments,t=this.widthSegments,r=this.attributes,i=[],n=[],a=[],o=[],s=0;s<=e;s++)for(var l=s/e,h=0;h<=t;h++){var u=h/t;if(i.push([2*u-1,2*l-1,0]),n&&n.push([u,l]),a&&a.push([0,0,1]),h0?this.material.define(\"fragment\",\"LOD\"):this.material.undefine(\"fragment\",\"LOD\"),e.renderPass([this],r)}}),Xi=ji;function qi(e){return e.charCodeAt(0)+(e.charCodeAt(1)<<8)+(e.charCodeAt(2)<<16)+(e.charCodeAt(3)<<24)}var Zi=qi(\"DXT1\"),Yi=qi(\"DXT3\"),Ki=qi(\"DXT5\");const Qi=function(e,t){var r=new Int32Array(e,0,31);if(542327876!==r[0])return null;if(4&!r(20))return null;var i,n,a=r(21),o=r[4],s=r[3],l=512&r[28],h=131072&r[2];switch(a){case Zi:i=8,n=wr.COMPRESSED_RGB_S3TC_DXT1_EXT;break;case Yi:i=16,n=wr.COMPRESSED_RGBA_S3TC_DXT3_EXT;break;case Ki:i=16,n=wr.COMPRESSED_RGBA_S3TC_DXT5_EXT;break;default:return null}var u=r[1]+4,c=l?6:1,d=1;h&&(d=Math.max(1,r[7]));for(var f=[],p=0;p0){var n=Math.pow(2,e[3]-128-8+i);t[r+0]=e[0]*n,t[r+1]=e[1]*n,t[r+2]=e[2]*n}else t[r+0]=0,t[r+1]=0,t[r+2]=0;return t[r+3]=1,t}function en(e,t,r,i){for(var n,a,o=0,s=0,l=i;l>0;)if(e[s][0]=t[r++],e[s][1]=t[r++],e[s][2]=t[r++],e[s][3]=t[r++],1===e[s][0]&&1===e[s][1]&&1===e[s][2]){for(var h=e[s][3]<>>0;h>0;h--)n=e[s-1],(a=e[s])[0]=n[0],a[1]=n[1],a[2]=n[2],a[3]=n[3],s++,l--;o+=8}else s++,l--,o=0;return r}function tn(e,t,r,i){if(i<8|i>32767)return en(e,t,r,i);if(2!=(n=t[r++]))return en(e,t,r-1,i);if(e[0][1]=t[r++],e[0][2]=t[r++],n=t[r++],(e[0][2]<<8>>>0|n)>>>0!==i)return null;for(var n=0;n<4;n++)for(var a=0;a128){o=(127&o)>>>0;for(var s=t[r++];o--;)e[a++][n]=s}else for(;o--;)e[a++][n]=t[r++]}return r}const rn=function(e,t,r){null==r&&(r=0);var i=new Uint8Array(e),n=i.length;if(\"#?\"===function(e,t,r){for(var i=\"\",n=0;n<2;n++)i+=Ji(e[n]);return i}(i)){for(var a=2;a=n)){a+=2;for(var o=\"\";a20)return console.warn(\"Given image is not a height map\"),e}var d,f,p,m;l%(4*i)==0?(d=o.data[l],p=o.data[l+4]):l%(4*i)==4*(i-1)?(d=o.data[l-4],p=o.data[l]):(d=o.data[l-4],p=o.data[l+4]),l<4*i?(f=o.data[l],m=o.data[l+4*i]):l>i*(n-1)*4?(f=o.data[l-4*i],m=o.data[l]):(f=o.data[l-4*i],m=o.data[l+4*i]),s.data[l]=d-p+127,s.data[l+1]=f-m+127,s.data[l+2]=255,s.data[l+3]=255}return a.putImageData(s,0,0),r},isHeightImage:function(e,t,r){if(!e||!e.width||!e.height)return!1;var i=document.createElement(\"canvas\"),n=i.getContext(\"2d\"),a=t||32;r=r||20,i.width=i.height=a,n.drawImage(e,0,0,a,a);for(var o=n.getImageData(0,0,a,a),s=0;sr)return!1}return!0},_fetchTexture:function(e,t,r){U.request.get({url:e,responseType:\"arraybuffer\",onload:t,onerror:r})},createChessboard:function(e,t,r,i){e=e||512,t=t||64,r=r||\"black\",i=i||\"white\";var n=Math.ceil(e/t),a=document.createElement(\"canvas\");a.width=e,a.height=e;var o=a.getContext(\"2d\");o.fillStyle=i,o.fillRect(0,0,e,e),o.fillStyle=r;for(var s=0;s=0||(on.forEach((function(t){e.on(t,this[sn(t)],this)}),this),this._meshes.push(e))},detachFromMesh:function(e){var t=this._meshes.indexOf(e);t>=0&&this._meshes.splice(t,1),on.forEach((function(t){e.off(t,this[sn(t)])}),this)},dispose:function(){this._meshes.forEach((function(e){this.detachFromMesh(e)}),this)}};const hn=ln,un=hi.extend({left:-1,right:1,near:-1,far:1,top:1,bottom:-1},{updateProjectionMatrix:function(){this.projectionMatrix.ortho(this.left,this.right,this.bottom,this.top,this.near,this.far)},decomposeProjectionMatrix:function(){var e=this.projectionMatrix.array;this.left=(-1-e[12])/e[0],this.right=(1-e[12])/e[0],this.top=(1-e[13])/e[5],this.bottom=(-1-e[13])/e[5],this.near=-(-1-e[14])/e[10],this.far=-(1-e[14])/e[10]},clone:function(){var e=hi.prototype.clone.call(this);return e.left=this.left,e.right=this.right,e.near=this.near,e.far=this.far,e.top=this.top,e.bottom=this.bottom,e}});Xe.import(\"\\n@export clay.compositor.vertex\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\nattribute vec3 position : POSITION;\\nattribute vec2 texcoord : TEXCOORD_0;\\nvarying vec2 v_Texcoord;\\nvoid main()\\n{\\n v_Texcoord = texcoord;\\n gl_Position = worldViewProjection * vec4(position, 1.0);\\n}\\n@end\");var cn=new ki,dn=new Sr({geometry:cn,frustumCulling:!1}),fn=new un;const pn=m.extend((function(){return{fragment:\"\",outputs:null,material:null,blendWithPrevious:!1,clearColor:!1,clearDepth:!0}}),(function(){var e=new Xe(Xe.source(\"clay.compositor.vertex\"),this.fragment),t=new le({shader:e});t.enableTexturesAll(),this.material=t}),{setUniform:function(e,t){this.material.setUniform(e,t)},getUniform:function(e){var t=this.material.uniforms[e];if(t)return t.value},attachOutput:function(e,t){this.outputs||(this.outputs={}),t=t||36064,this.outputs[t]=e},detachOutput:function(e){for(var t in this.outputs)this.outputs[t]===e&&(this.outputs[t]=null)},bind:function(e,t){if(this.outputs)for(var r in this.outputs){var i=this.outputs[r];i&&t.attach(i,r)}t&&t.bind(e)},unbind:function(e,t){t.unbind(e)},render:function(e,t){var r=e.gl;if(t){this.bind(e,t);var i=e.getGLExtension(\"EXT_draw_buffers\");if(i&&this.outputs){var n=[];for(var a in this.outputs)(a=+a)>=r.COLOR_ATTACHMENT0&&a<=r.COLOR_ATTACHMENT0+8&&n.push(a);i.drawBuffersEXT(n)}}this.trigger(\"beforerender\",this,e);var o=this.clearDepth?r.DEPTH_BUFFER_BIT:0;if(r.depthMask(!0),this.clearColor){o|=r.COLOR_BUFFER_BIT,r.colorMask(!0,!0,!0,!0);var s=this.clearColor;Array.isArray(s)&&r.clearColor(s[0],s[1],s[2],s[3])}r.clear(o),this.blendWithPrevious?(r.enable(r.BLEND),this.material.transparent=!0):(r.disable(r.BLEND),this.material.transparent=!1),this.renderQuad(e),this.trigger(\"afterrender\",this,e),t&&this.unbind(e,t)},renderQuad:function(e){dn.material=this.material,e.renderPass([dn],fn)},dispose:function(e){}});var mn={},gn=[\"px\",\"nx\",\"py\",\"ny\",\"pz\",\"nz\"];mn.prefilterEnvironmentMap=function(e,t,r,i,n){n&&i||(i=mn.generateNormalDistribution(),n=mn.integrateBRDF(e,i));var a=(r=r||{}).width||64,o=r.height||64,s=r.type||t.type,l=new Ai({width:a,height:o,type:s,flipY:!1,mipmaps:[]});l.isPowerOfTwo()||console.warn(\"Width and height must be power of two to enable mipmap.\");var h=Math.min(a,o),u=Math.log(h)/Math.log(2)+1,c=new le({shader:new Xe({vertex:Xe.source(\"clay.skybox.vertex\"),fragment:\"#define SHADER_NAME prefilter\\n#define SAMPLE_NUMBER 1024\\n#define PI 3.14159265358979\\nuniform mat4 viewInverse : VIEWINVERSE;\\nuniform samplerCube environmentMap;\\nuniform sampler2D normalDistribution;\\nuniform float roughness : 0.5;\\nvarying vec2 v_Texcoord;\\nvarying vec3 v_WorldPosition;\\n@import clay.util.rgbm\\nvec3 importanceSampleNormal(float i, float roughness, vec3 N) {\\n vec3 H = texture2D(normalDistribution, vec2(roughness, i)).rgb;\\n vec3 upVector = abs(N.y) > 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\\n vec3 tangentX = normalize(cross(N, upVector));\\n vec3 tangentZ = cross(N, tangentX);\\n return normalize(tangentX * H.x + N * H.y + tangentZ * H.z);\\n}\\nvoid main() {\\n vec3 eyePos = viewInverse[3].xyz;\\n vec3 V = normalize(v_WorldPosition - eyePos);\\n vec3 N = V;\\n vec3 prefilteredColor = vec3(0.0);\\n float totalWeight = 0.0;\\n float fMaxSampleNumber = float(SAMPLE_NUMBER);\\n for (int i = 0; i < SAMPLE_NUMBER; i++) {\\n vec3 H = importanceSampleNormal(float(i) / fMaxSampleNumber, roughness, N);\\n vec3 L = reflect(-V, H);\\n float NoL = clamp(dot(N, L), 0.0, 1.0);\\n if (NoL > 0.0) {\\n prefilteredColor += decodeHDR(textureCube(environmentMap, L)).rgb * NoL;\\n totalWeight += NoL;\\n }\\n }\\n gl_FragColor = encodeHDR(vec4(prefilteredColor / totalWeight, 1.0));\\n}\\n\"})});c.set(\"normalDistribution\",i),r.encodeRGBM&&c.define(\"fragment\",\"RGBM_ENCODE\"),r.decodeRGBM&&c.define(\"fragment\",\"RGBM_DECODE\");var d,f=new vi;if(\"texture2D\"===t.textureType){var p=new Ai({width:a,height:o,type:s===wr.FLOAT?wr.HALF_FLOAT:s});an.panoramaToCubeMap(e,t,p,{encodeRGBM:r.decodeRGBM}),t=p}(d=new ji({scene:f,material:c})).material.set(\"environmentMap\",t);var m=new Ui({texture:l});r.encodeRGBM&&(s=l.type=wr.UNSIGNED_BYTE);for(var g=new Dr({width:a,height:o,type:s}),_=new zi({depthBuffer:!1}),v=U[s===wr.UNSIGNED_BYTE?\"Uint8Array\":\"Float32Array\"],y=0;y 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\\n vec3 tangentX = normalize(cross(N, upVector));\\n vec3 tangentZ = cross(N, tangentX);\\n return normalize(tangentX * H.x + N * H.y + tangentZ * H.z);\\n}\\nfloat G_Smith(float roughness, float NoV, float NoL) {\\n float k = roughness * roughness / 2.0;\\n float G1V = NoV / (NoV * (1.0 - k) + k);\\n float G1L = NoL / (NoL * (1.0 - k) + k);\\n return G1L * G1V;\\n}\\nvoid main() {\\n vec2 uv = gl_FragCoord.xy / viewportSize;\\n float NoV = uv.x;\\n float roughness = uv.y;\\n vec3 V;\\n V.x = sqrt(1.0 - NoV * NoV);\\n V.y = 0.0;\\n V.z = NoV;\\n float A = 0.0;\\n float B = 0.0;\\n for (int i = 0; i < SAMPLE_NUMBER; i++) {\\n vec3 H = importanceSampleNormal(float(i) / fSampleNumber, roughness, N);\\n vec3 L = reflect(-V, H);\\n float NoL = clamp(L.z, 0.0, 1.0);\\n float NoH = clamp(H.z, 0.0, 1.0);\\n float VoH = clamp(dot(V, H), 0.0, 1.0);\\n if (NoL > 0.0) {\\n float G = G_Smith(roughness, NoV, NoL);\\n float G_Vis = G * VoH / (NoH * NoV);\\n float Fc = pow(1.0 - VoH, 5.0);\\n A += (1.0 - Fc) * G_Vis;\\n B += Fc * G_Vis;\\n }\\n }\\n gl_FragColor = vec4(vec2(A, B) / fSampleNumber, 0.0, 1.0);\\n}\\n\"}),n=new Dr({width:512,height:256,type:wr.HALF_FLOAT,wrapS:wr.CLAMP_TO_EDGE,wrapT:wr.CLAMP_TO_EDGE,minFilter:wr.NEAREST,magFilter:wr.NEAREST,useMipmap:!1});return i.setUniform(\"normalDistribution\",t),i.setUniform(\"viewportSize\",[512,256]),i.attachOutput(n),i.render(e,r),r.dispose(e),n},mn.generateNormalDistribution=function(e,t){for(var r=new Dr({width:e=e||256,height:t=t||1024,type:wr.FLOAT,minFilter:wr.NEAREST,magFilter:wr.NEAREST,wrapS:wr.CLAMP_TO_EDGE,wrapT:wr.CLAMP_TO_EDGE,useMipmap:!1}),i=new Float32Array(t*e*4),n=[],a=0;a>>16)>>>0;h=(((16711935&(h=((252645135&(h=((858993459&(h=((1431655765&h)<<1|(2863311530&h)>>>1)>>>0))<<2|(3435973836&h)>>>2)>>>0))<<4|(4042322160&h)>>>4)>>>0))<<8|(4278255360&h)>>>8)>>>0)/4294967296;var u=Math.sqrt((1-h)/(1+(s*s-1)*h));n[l]=u}for(l=0;l65535?Uint32Array:Uint16Array,v=this.indices=new _(t*e*6),y=this.radius,x=this.phiStart,b=this.phiLength,w=this.thetaStart,T=this.thetaLength,S=[],M=[],A=0,E=1/(y=this.radius);for(d=0;d<=e;d++)for(c=0;c<=t;c++)h=c/t,u=d/e,o=-y*Math.cos(x+h*b)*Math.sin(w+u*T),s=y*Math.cos(w+u*T),l=y*Math.sin(x+h*b)*Math.sin(w+u*T),S[0]=o,S[1]=s,S[2]=l,M[0]=h,M[1]=u,r.set(A,S),i.set(A,M),S[0]*=E,S[1]*=E,S[2]*=E,n.set(A,S),A++;var C=t+1,D=0;for(d=0;d255?255:e}function Qn(e){return e<0?0:e>1?1:e}function Jn(e){var t=e;return t.length&&\"%\"===t.charAt(t.length-1)?Kn(parseFloat(t)/100*255):Kn(parseInt(t,10))}function $n(e){var t=e;return t.length&&\"%\"===t.charAt(t.length-1)?Qn(parseFloat(t)/100):Qn(parseFloat(t))}function ea(e,t,r){return r<0?r+=1:r>1&&(r-=1),6*r<1?e+(t-e)*r*6:2*r<1?t:3*r<2?e+(t-e)*(2/3-r)*6:e}function ta(e,t,r,i,n){return e[0]=t,e[1]=r,e[2]=i,e[3]=n,e}function ra(e,t){return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e}var ia=new bi(20),na=null;function aa(e,t){na&&ra(na,t),na=ia.put(e,na||t.slice())}function oa(e,t){if(e){t=t||[];var r=ia.get(e);if(r)return ra(t,r);var i=(e+=\"\").replace(/ /g,\"\").toLowerCase();if(i in Yn)return ra(t,Yn[i]),aa(e,t),t;var n,a=i.length;if(\"#\"===i.charAt(0))return 4===a||5===a?(n=parseInt(i.slice(1,4),16))>=0&&n<=4095?(ta(t,(3840&n)>>4|(3840&n)>>8,240&n|(240&n)>>4,15&n|(15&n)<<4,5===a?parseInt(i.slice(4),16)/15:1),aa(e,t),t):void ta(t,0,0,0,1):7===a||9===a?(n=parseInt(i.slice(1,7),16))>=0&&n<=16777215?(ta(t,(16711680&n)>>16,(65280&n)>>8,255&n,9===a?parseInt(i.slice(7),16)/255:1),aa(e,t),t):void ta(t,0,0,0,1):void 0;var o=i.indexOf(\"(\"),s=i.indexOf(\")\");if(-1!==o&&s+1===a){var l=i.substr(0,o),h=i.substr(o+1,s-(o+1)).split(\",\"),u=1;switch(l){case\"rgba\":if(4!==h.length)return 3===h.length?ta(t,+h[0],+h[1],+h[2],1):ta(t,0,0,0,1);u=$n(h.pop());case\"rgb\":return 3!==h.length?void ta(t,0,0,0,1):(ta(t,Jn(h[0]),Jn(h[1]),Jn(h[2]),u),aa(e,t),t);case\"hsla\":return 4!==h.length?void ta(t,0,0,0,1):(h[3]=$n(h[3]),sa(h,t),aa(e,t),t);case\"hsl\":return 3!==h.length?void ta(t,0,0,0,1):(sa(h,t),aa(e,t),t);default:return}}ta(t,0,0,0,1)}}function sa(e,t){var r=(parseFloat(e[0])%360+360)%360/360,i=$n(e[1]),n=$n(e[2]),a=n<=.5?n*(i+1):n+i-n*i,o=2*n-a;return ta(t=t||[],Kn(255*ea(o,a,r+1/3)),Kn(255*ea(o,a,r)),Kn(255*ea(o,a,r-1/3)),1),4===e.length&&(t[3]=e[3]),t}var la=Object.prototype.toString,ha=Array.prototype,ua=ha.forEach,ca=ha.filter,da=ha.slice,fa=ha.map,pa=function(){}.constructor,ma=pa?pa.prototype:null;function ga(e,t){if(Object.assign)Object.assign(e,t);else for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r]);return e}function _a(e,t,r){if(e=\"prototype\"in e?e.prototype:e,t=\"prototype\"in t?t.prototype:t,Object.getOwnPropertyNames)for(var i=Object.getOwnPropertyNames(t),n=0;no)i.length=o;else for(var s=a;s=2&&this.interpolable},e.prototype.getAdditiveTrack=function(){return this._additiveTrack},e.prototype.addKeyframe=function(e,t){e>=this.maxTime?this.maxTime=e:this._needsSort=!0;var r=this.keyframes,i=r.length;if(this.interpolable)if(va(t)){var n=function(e){return va(e&&e[0])?2:1}(t);if(i>0&&this.arrDim!==n)return void(this.interpolable=!1);if(1===n&&\"number\"!=typeof t[0]||2===n&&\"number\"!=typeof t[0][0])return void(this.interpolable=!1);if(i>0){var a=r[i-1];this._isAllValueEqual&&(1===n&&Na(t,a.value)||(this._isAllValueEqual=!1))}this.arrDim=n}else{if(this.arrDim>0)return void(this.interpolable=!1);if(\"string\"==typeof t){var o=oa(t);o?(t=o,this.isValueColor=!0):this.interpolable=!1}else if(\"number\"!=typeof t||isNaN(t))return void(this.interpolable=!1);this._isAllValueEqual&&i>0&&(a=r[i-1],(this.isValueColor&&!Na(a.value,t)||a.value!==t)&&(this._isAllValueEqual=!1))}var s={time:e,value:t,percent:0};return this.keyframes.push(s),s},e.prototype.prepare=function(e){var t=this.keyframes;this._needsSort&&t.sort((function(e,t){return e.time-t.time}));for(var r=this.arrDim,i=t.length,n=t[i-1],a=0;a0&&a!==i-1&&Oa(t[a].value,n.value,r);if(e&&this.needsAnimate()&&e.needsAnimate()&&r===e.arrDim&&this.isValueColor===e.isValueColor&&!e._finished){this._additiveTrack=e;var o=t[0].value;for(a=0;a=0&&!(a[r].percent<=t);r--);r=Math.min(r,o-2)}else{for(r=this._lastFrame;rt);r++);r=Math.min(r-1,o-2)}var u=a[r+1],c=a[r];if(c&&u){this._lastFrame=r,this._lastFramePercent=t;var d=u.percent-c.percent;if(0!==d){var f=(t-c.percent)/d,p=i?this._additiveValue:h?za:e[s];if((l>0||h)&&!p&&(p=this._additiveValue=[]),this.useSpline){var m=a[r][n],g=a[0===r?r:r-1][n],_=a[r>o-2?o-1:r+1][n],v=a[r>o-3?o-1:r+2][n];if(l>0)1===l?Ra(p,g,m,_,v,f,f*f,f*f*f):function(e,t,r,i,n,a,o,s){for(var l=t.length,h=t[0].length,u=0;u0?1===l?Da(p,c[n],u[n],f):function(e,t,r,i){for(var n=t.length,a=n&&t[0].length,o=0;o.5?t:e}(c[n],u[n],f),i?this._additiveValue=y:e[s]=y);i&&this._addToTarget(e)}}}},e.prototype._addToTarget=function(e){var t=this.arrDim,r=this.propName,i=this._additiveValue;0===t?this.isValueColor?(oa(e[r],za),La(za,za,i,1),e[r]=Fa(za)):e[r]=e[r]+i:1===t?La(e[r],e[r],i,1):2===t&&Pa(e[r],e[r],i,1)},e}();const Ua=function(){function e(e,t,r){this._tracks={},this._trackKeys=[],this._delay=0,this._maxTime=0,this._paused=!1,this._started=0,this._clip=null,this._target=e,this._loop=t,t&&r?function(){for(var e=[],t=0;t0)){this._started=1;for(var r=this,i=[],n=0;n1){var o=a.pop();n.addKeyframe(o.time,e[i]),n.prepare(n.getAdditiveTrack())}}}},e}(),ka={_animators:null,getAnimators:function(){return this._animators=this._animators||[],this._animators},animate:function(e,t){var r;if(this._animators=this._animators||[],e){for(var i=e.split(\".\"),n=this,a=0,o=i.length;a=0&&s.splice(e,1)})),s.push(l),this.__zr&&this.__zr.animation.addAnimator(l),l},stopAnimation:function(e){this._animators=this._animators||[];for(var t=this._animators,r=t.length,i=0;i 1e-4)\\n{\\n skinMatrixWS += getSkinMatrix(joint.y) * weight.y;\\n}\\nif (weight.z > 1e-4)\\n{\\n skinMatrixWS += getSkinMatrix(joint.z) * weight.z;\\n}\\nfloat weightW = 1.0-weight.x-weight.y-weight.z;\\nif (weightW > 1e-4)\\n{\\n skinMatrixWS += getSkinMatrix(joint.w) * weightW;\\n}\\n@end\\n@export clay.chunk.instancing_header\\n#ifdef INSTANCING\\nattribute vec4 instanceMat1;\\nattribute vec4 instanceMat2;\\nattribute vec4 instanceMat3;\\n#endif\\n@end\\n@export clay.chunk.instancing_matrix\\nmat4 instanceMat = mat4(\\n vec4(instanceMat1.xyz, 0.0),\\n vec4(instanceMat2.xyz, 0.0),\\n vec4(instanceMat3.xyz, 0.0),\\n vec4(instanceMat1.w, instanceMat2.w, instanceMat3.w, 1.0)\\n);\\n@end\\n@export clay.util.parallax_correct\\nvec3 parallaxCorrect(in vec3 dir, in vec3 pos, in vec3 boxMin, in vec3 boxMax) {\\n vec3 first = (boxMax - pos) / dir;\\n vec3 second = (boxMin - pos) / dir;\\n vec3 further = max(first, second);\\n float dist = min(further.x, min(further.y, further.z));\\n vec3 fixedPos = pos + dir * dist;\\n vec3 boxCenter = (boxMax + boxMin) * 0.5;\\n return normalize(fixedPos - boxCenter);\\n}\\n@end\\n@export clay.util.clamp_sample\\nvec4 clampSample(const in sampler2D texture, const in vec2 coord)\\n{\\n#ifdef STEREO\\n float eye = step(0.5, coord.x) * 0.5;\\n vec2 coordClamped = clamp(coord, vec2(eye, 0.0), vec2(0.5 + eye, 1.0));\\n#else\\n vec2 coordClamped = clamp(coord, vec2(0.0), vec2(1.0));\\n#endif\\n return texture2D(texture, coordClamped);\\n}\\n@end\\n@export clay.util.ACES\\nvec3 ACESToneMapping(vec3 color)\\n{\\n const float A = 2.51;\\n const float B = 0.03;\\n const float C = 2.43;\\n const float D = 0.59;\\n const float E = 0.14;\\n return (color * (A * color + B)) / (color * (C * color + D) + E);\\n}\\n@end\";function Ha(e){return e instanceof HTMLCanvasElement||e instanceof HTMLImageElement||e instanceof Image}Object.assign(sr.prototype,ka),Xe.import(Va),Xe.import(qe),Xe.import(\"\\n@export ecgl.common.transformUniforms\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\\nuniform mat4 world : WORLD;\\n@end\\n\\n@export ecgl.common.attributes\\nattribute vec3 position : POSITION;\\nattribute vec2 texcoord : TEXCOORD_0;\\nattribute vec3 normal : NORMAL;\\n@end\\n\\n@export ecgl.common.uv.header\\nuniform vec2 uvRepeat : [1.0, 1.0];\\nuniform vec2 uvOffset : [0.0, 0.0];\\nuniform vec2 detailUvRepeat : [1.0, 1.0];\\nuniform vec2 detailUvOffset : [0.0, 0.0];\\n\\nvarying vec2 v_Texcoord;\\nvarying vec2 v_DetailTexcoord;\\n@end\\n\\n@export ecgl.common.uv.main\\nv_Texcoord = texcoord * uvRepeat + uvOffset;\\nv_DetailTexcoord = texcoord * detailUvRepeat + detailUvOffset;\\n@end\\n\\n@export ecgl.common.uv.fragmentHeader\\nvarying vec2 v_Texcoord;\\nvarying vec2 v_DetailTexcoord;\\n@end\\n\\n\\n@export ecgl.common.albedo.main\\n\\n vec4 albedoTexel = vec4(1.0);\\n#ifdef DIFFUSEMAP_ENABLED\\n albedoTexel = texture2D(diffuseMap, v_Texcoord);\\n #ifdef SRGB_DECODE\\n albedoTexel = sRGBToLinear(albedoTexel);\\n #endif\\n#endif\\n\\n#ifdef DETAILMAP_ENABLED\\n vec4 detailTexel = texture2D(detailMap, v_DetailTexcoord);\\n #ifdef SRGB_DECODE\\n detailTexel = sRGBToLinear(detailTexel);\\n #endif\\n albedoTexel.rgb = mix(albedoTexel.rgb, detailTexel.rgb, detailTexel.a);\\n albedoTexel.a = detailTexel.a + (1.0 - detailTexel.a) * albedoTexel.a;\\n#endif\\n\\n@end\\n\\n@export ecgl.common.wireframe.vertexHeader\\n\\n#ifdef WIREFRAME_QUAD\\nattribute vec4 barycentric;\\nvarying vec4 v_Barycentric;\\n#elif defined(WIREFRAME_TRIANGLE)\\nattribute vec3 barycentric;\\nvarying vec3 v_Barycentric;\\n#endif\\n\\n@end\\n\\n@export ecgl.common.wireframe.vertexMain\\n\\n#if defined(WIREFRAME_QUAD) || defined(WIREFRAME_TRIANGLE)\\n v_Barycentric = barycentric;\\n#endif\\n\\n@end\\n\\n\\n@export ecgl.common.wireframe.fragmentHeader\\n\\nuniform float wireframeLineWidth : 1;\\nuniform vec4 wireframeLineColor: [0, 0, 0, 0.5];\\n\\n#ifdef WIREFRAME_QUAD\\nvarying vec4 v_Barycentric;\\nfloat edgeFactor () {\\n vec4 d = fwidth(v_Barycentric);\\n vec4 a4 = smoothstep(vec4(0.0), d * wireframeLineWidth, v_Barycentric);\\n return min(min(min(a4.x, a4.y), a4.z), a4.w);\\n}\\n#elif defined(WIREFRAME_TRIANGLE)\\nvarying vec3 v_Barycentric;\\nfloat edgeFactor () {\\n vec3 d = fwidth(v_Barycentric);\\n vec3 a3 = smoothstep(vec3(0.0), d * wireframeLineWidth, v_Barycentric);\\n return min(min(a3.x, a3.y), a3.z);\\n}\\n#endif\\n\\n@end\\n\\n\\n@export ecgl.common.wireframe.fragmentMain\\n\\n#if defined(WIREFRAME_QUAD) || defined(WIREFRAME_TRIANGLE)\\n if (wireframeLineWidth > 0.) {\\n vec4 lineColor = wireframeLineColor;\\n#ifdef SRGB_DECODE\\n lineColor = sRGBToLinear(lineColor);\\n#endif\\n\\n gl_FragColor.rgb = mix(gl_FragColor.rgb, lineColor.rgb, (1.0 - edgeFactor()) * lineColor.a);\\n }\\n#endif\\n@end\\n\\n\\n\\n\\n@export ecgl.common.bumpMap.header\\n\\n#ifdef BUMPMAP_ENABLED\\nuniform sampler2D bumpMap;\\nuniform float bumpScale : 1.0;\\n\\n\\nvec3 bumpNormal(vec3 surfPos, vec3 surfNormal, vec3 baseNormal)\\n{\\n vec2 dSTdx = dFdx(v_Texcoord);\\n vec2 dSTdy = dFdy(v_Texcoord);\\n\\n float Hll = bumpScale * texture2D(bumpMap, v_Texcoord).x;\\n float dHx = bumpScale * texture2D(bumpMap, v_Texcoord + dSTdx).x - Hll;\\n float dHy = bumpScale * texture2D(bumpMap, v_Texcoord + dSTdy).x - Hll;\\n\\n vec3 vSigmaX = dFdx(surfPos);\\n vec3 vSigmaY = dFdy(surfPos);\\n vec3 vN = surfNormal;\\n\\n vec3 R1 = cross(vSigmaY, vN);\\n vec3 R2 = cross(vN, vSigmaX);\\n\\n float fDet = dot(vSigmaX, R1);\\n\\n vec3 vGrad = sign(fDet) * (dHx * R1 + dHy * R2);\\n return normalize(abs(fDet) * baseNormal - vGrad);\\n\\n}\\n#endif\\n\\n@end\\n\\n@export ecgl.common.normalMap.vertexHeader\\n\\n#ifdef NORMALMAP_ENABLED\\nattribute vec4 tangent : TANGENT;\\nvarying vec3 v_Tangent;\\nvarying vec3 v_Bitangent;\\n#endif\\n\\n@end\\n\\n@export ecgl.common.normalMap.vertexMain\\n\\n#ifdef NORMALMAP_ENABLED\\n if (dot(tangent, tangent) > 0.0) {\\n v_Tangent = normalize((worldInverseTranspose * vec4(tangent.xyz, 0.0)).xyz);\\n v_Bitangent = normalize(cross(v_Normal, v_Tangent) * tangent.w);\\n }\\n#endif\\n\\n@end\\n\\n\\n@export ecgl.common.normalMap.fragmentHeader\\n\\n#ifdef NORMALMAP_ENABLED\\nuniform sampler2D normalMap;\\nvarying vec3 v_Tangent;\\nvarying vec3 v_Bitangent;\\n#endif\\n\\n@end\\n\\n@export ecgl.common.normalMap.fragmentMain\\n#ifdef NORMALMAP_ENABLED\\n if (dot(v_Tangent, v_Tangent) > 0.0) {\\n vec3 normalTexel = texture2D(normalMap, v_DetailTexcoord).xyz;\\n if (dot(normalTexel, normalTexel) > 0.0) { N = normalTexel * 2.0 - 1.0;\\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\\n N = normalize(tbn * N);\\n }\\n }\\n#endif\\n@end\\n\\n\\n\\n@export ecgl.common.vertexAnimation.header\\n\\n#ifdef VERTEX_ANIMATION\\nattribute vec3 prevPosition;\\nattribute vec3 prevNormal;\\nuniform float percent;\\n#endif\\n\\n@end\\n\\n@export ecgl.common.vertexAnimation.main\\n\\n#ifdef VERTEX_ANIMATION\\n vec3 pos = mix(prevPosition, position, percent);\\n vec3 norm = mix(prevNormal, normal, percent);\\n#else\\n vec3 pos = position;\\n vec3 norm = normal;\\n#endif\\n\\n@end\\n\\n\\n@export ecgl.common.ssaoMap.header\\n#ifdef SSAOMAP_ENABLED\\nuniform sampler2D ssaoMap;\\nuniform vec4 viewport : VIEWPORT;\\n#endif\\n@end\\n\\n@export ecgl.common.ssaoMap.main\\n float ao = 1.0;\\n#ifdef SSAOMAP_ENABLED\\n ao = texture2D(ssaoMap, (gl_FragCoord.xy - viewport.xy) / viewport.zw).r;\\n#endif\\n@end\\n\\n\\n\\n\\n@export ecgl.common.diffuseLayer.header\\n\\n#if (LAYER_DIFFUSEMAP_COUNT > 0)\\nuniform float layerDiffuseIntensity[LAYER_DIFFUSEMAP_COUNT];\\nuniform sampler2D layerDiffuseMap[LAYER_DIFFUSEMAP_COUNT];\\n#endif\\n\\n@end\\n\\n@export ecgl.common.emissiveLayer.header\\n\\n#if (LAYER_EMISSIVEMAP_COUNT > 0)\\nuniform float layerEmissionIntensity[LAYER_EMISSIVEMAP_COUNT];\\nuniform sampler2D layerEmissiveMap[LAYER_EMISSIVEMAP_COUNT];\\n#endif\\n\\n@end\\n\\n@export ecgl.common.layers.header\\n@import ecgl.common.diffuseLayer.header\\n@import ecgl.common.emissiveLayer.header\\n@end\\n\\n@export ecgl.common.diffuseLayer.main\\n\\n#if (LAYER_DIFFUSEMAP_COUNT > 0)\\n for (int _idx_ = 0; _idx_ < LAYER_DIFFUSEMAP_COUNT; _idx_++) {{\\n float intensity = layerDiffuseIntensity[_idx_];\\n vec4 texel2 = texture2D(layerDiffuseMap[_idx_], v_Texcoord);\\n #ifdef SRGB_DECODE\\n texel2 = sRGBToLinear(texel2);\\n #endif\\n albedoTexel.rgb = mix(albedoTexel.rgb, texel2.rgb * intensity, texel2.a);\\n albedoTexel.a = texel2.a + (1.0 - texel2.a) * albedoTexel.a;\\n }}\\n#endif\\n\\n@end\\n\\n@export ecgl.common.emissiveLayer.main\\n\\n#if (LAYER_EMISSIVEMAP_COUNT > 0)\\n for (int _idx_ = 0; _idx_ < LAYER_EMISSIVEMAP_COUNT; _idx_++)\\n {{\\n vec4 texel2 = texture2D(layerEmissiveMap[_idx_], v_Texcoord) * layerEmissionIntensity[_idx_];\\n #ifdef SRGB_DECODE\\n texel2 = sRGBToLinear(texel2);\\n #endif\\n float intensity = layerEmissionIntensity[_idx_];\\n gl_FragColor.rgb += texel2.rgb * texel2.a * intensity;\\n }}\\n#endif\\n\\n@end\\n\"),Xe.import(\"@export ecgl.color.vertex\\n\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\n\\n@import ecgl.common.uv.header\\n\\nattribute vec2 texcoord : TEXCOORD_0;\\nattribute vec3 position: POSITION;\\n\\n@import ecgl.common.wireframe.vertexHeader\\n\\n#ifdef VERTEX_COLOR\\nattribute vec4 a_Color : COLOR;\\nvarying vec4 v_Color;\\n#endif\\n\\n#ifdef VERTEX_ANIMATION\\nattribute vec3 prevPosition;\\nuniform float percent : 1.0;\\n#endif\\n\\n#ifdef ATMOSPHERE_ENABLED\\nattribute vec3 normal: NORMAL;\\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\\nvarying vec3 v_Normal;\\n#endif\\n\\nvoid main()\\n{\\n#ifdef VERTEX_ANIMATION\\n vec3 pos = mix(prevPosition, position, percent);\\n#else\\n vec3 pos = position;\\n#endif\\n\\n gl_Position = worldViewProjection * vec4(pos, 1.0);\\n\\n @import ecgl.common.uv.main\\n\\n#ifdef VERTEX_COLOR\\n v_Color = a_Color;\\n#endif\\n\\n#ifdef ATMOSPHERE_ENABLED\\n v_Normal = normalize((worldInverseTranspose * vec4(normal, 0.0)).xyz);\\n#endif\\n\\n @import ecgl.common.wireframe.vertexMain\\n\\n}\\n\\n@end\\n\\n@export ecgl.color.fragment\\n\\n#define LAYER_DIFFUSEMAP_COUNT 0\\n#define LAYER_EMISSIVEMAP_COUNT 0\\n\\nuniform sampler2D diffuseMap;\\nuniform sampler2D detailMap;\\n\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\n\\n#ifdef ATMOSPHERE_ENABLED\\nuniform mat4 viewTranspose: VIEWTRANSPOSE;\\nuniform vec3 glowColor;\\nuniform float glowPower;\\nvarying vec3 v_Normal;\\n#endif\\n\\n#ifdef VERTEX_COLOR\\nvarying vec4 v_Color;\\n#endif\\n\\n@import ecgl.common.layers.header\\n\\n@import ecgl.common.uv.fragmentHeader\\n\\n@import ecgl.common.wireframe.fragmentHeader\\n\\n@import clay.util.srgb\\n\\nvoid main()\\n{\\n#ifdef SRGB_DECODE\\n gl_FragColor = sRGBToLinear(color);\\n#else\\n gl_FragColor = color;\\n#endif\\n\\n#ifdef VERTEX_COLOR\\n gl_FragColor *= v_Color;\\n#endif\\n\\n @import ecgl.common.albedo.main\\n\\n @import ecgl.common.diffuseLayer.main\\n\\n gl_FragColor *= albedoTexel;\\n\\n#ifdef ATMOSPHERE_ENABLED\\n float atmoIntensity = pow(1.0 - dot(v_Normal, (viewTranspose * vec4(0.0, 0.0, 1.0, 0.0)).xyz), glowPower);\\n gl_FragColor.rgb += glowColor * atmoIntensity;\\n#endif\\n\\n @import ecgl.common.emissiveLayer.main\\n\\n @import ecgl.common.wireframe.fragmentMain\\n\\n}\\n@end\"),Xe.import(\"/**\\n * http: */\\n\\n@export ecgl.lambert.vertex\\n\\n@import ecgl.common.transformUniforms\\n\\n@import ecgl.common.uv.header\\n\\n\\n@import ecgl.common.attributes\\n\\n@import ecgl.common.wireframe.vertexHeader\\n\\n#ifdef VERTEX_COLOR\\nattribute vec4 a_Color : COLOR;\\nvarying vec4 v_Color;\\n#endif\\n\\n\\n@import ecgl.common.vertexAnimation.header\\n\\n\\nvarying vec3 v_Normal;\\nvarying vec3 v_WorldPosition;\\n\\nvoid main()\\n{\\n @import ecgl.common.uv.main\\n\\n @import ecgl.common.vertexAnimation.main\\n\\n\\n gl_Position = worldViewProjection * vec4(pos, 1.0);\\n\\n v_Normal = normalize((worldInverseTranspose * vec4(norm, 0.0)).xyz);\\n v_WorldPosition = (world * vec4(pos, 1.0)).xyz;\\n\\n#ifdef VERTEX_COLOR\\n v_Color = a_Color;\\n#endif\\n\\n @import ecgl.common.wireframe.vertexMain\\n}\\n\\n@end\\n\\n\\n@export ecgl.lambert.fragment\\n\\n#define LAYER_DIFFUSEMAP_COUNT 0\\n#define LAYER_EMISSIVEMAP_COUNT 0\\n\\n#define NORMAL_UP_AXIS 1\\n#define NORMAL_FRONT_AXIS 2\\n\\n@import ecgl.common.uv.fragmentHeader\\n\\nvarying vec3 v_Normal;\\nvarying vec3 v_WorldPosition;\\n\\nuniform sampler2D diffuseMap;\\nuniform sampler2D detailMap;\\n\\n@import ecgl.common.layers.header\\n\\nuniform float emissionIntensity: 1.0;\\n\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\n\\nuniform mat4 viewInverse : VIEWINVERSE;\\n\\n#ifdef ATMOSPHERE_ENABLED\\nuniform mat4 viewTranspose: VIEWTRANSPOSE;\\nuniform vec3 glowColor;\\nuniform float glowPower;\\n#endif\\n\\n#ifdef AMBIENT_LIGHT_COUNT\\n@import clay.header.ambient_light\\n#endif\\n#ifdef AMBIENT_SH_LIGHT_COUNT\\n@import clay.header.ambient_sh_light\\n#endif\\n\\n#ifdef DIRECTIONAL_LIGHT_COUNT\\n@import clay.header.directional_light\\n#endif\\n\\n#ifdef VERTEX_COLOR\\nvarying vec4 v_Color;\\n#endif\\n\\n\\n@import ecgl.common.ssaoMap.header\\n\\n@import ecgl.common.bumpMap.header\\n\\n@import clay.util.srgb\\n\\n@import ecgl.common.wireframe.fragmentHeader\\n\\n@import clay.plugin.compute_shadow_map\\n\\nvoid main()\\n{\\n#ifdef SRGB_DECODE\\n gl_FragColor = sRGBToLinear(color);\\n#else\\n gl_FragColor = color;\\n#endif\\n\\n#ifdef VERTEX_COLOR\\n #ifdef SRGB_DECODE\\n gl_FragColor *= sRGBToLinear(v_Color);\\n #else\\n gl_FragColor *= v_Color;\\n #endif\\n#endif\\n\\n @import ecgl.common.albedo.main\\n\\n @import ecgl.common.diffuseLayer.main\\n\\n gl_FragColor *= albedoTexel;\\n\\n vec3 N = v_Normal;\\n#ifdef DOUBLE_SIDED\\n vec3 eyePos = viewInverse[3].xyz;\\n vec3 V = normalize(eyePos - v_WorldPosition);\\n\\n if (dot(N, V) < 0.0) {\\n N = -N;\\n }\\n#endif\\n\\n float ambientFactor = 1.0;\\n\\n#ifdef BUMPMAP_ENABLED\\n N = bumpNormal(v_WorldPosition, v_Normal, N);\\n ambientFactor = dot(v_Normal, N);\\n#endif\\n\\n vec3 N2 = vec3(N.x, N[NORMAL_UP_AXIS], N[NORMAL_FRONT_AXIS]);\\n\\n vec3 diffuseColor = vec3(0.0, 0.0, 0.0);\\n\\n @import ecgl.common.ssaoMap.main\\n\\n#ifdef AMBIENT_LIGHT_COUNT\\n for(int i = 0; i < AMBIENT_LIGHT_COUNT; i++)\\n {\\n diffuseColor += ambientLightColor[i] * ambientFactor * ao;\\n }\\n#endif\\n#ifdef AMBIENT_SH_LIGHT_COUNT\\n for(int _idx_ = 0; _idx_ < AMBIENT_SH_LIGHT_COUNT; _idx_++)\\n {{\\n diffuseColor += calcAmbientSHLight(_idx_, N2) * ambientSHLightColor[_idx_] * ao;\\n }}\\n#endif\\n#ifdef DIRECTIONAL_LIGHT_COUNT\\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\\n float shadowContribsDir[DIRECTIONAL_LIGHT_COUNT];\\n if(shadowEnabled)\\n {\\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribsDir);\\n }\\n#endif\\n for(int i = 0; i < DIRECTIONAL_LIGHT_COUNT; i++)\\n {\\n vec3 lightDirection = -directionalLightDirection[i];\\n vec3 lightColor = directionalLightColor[i];\\n\\n float shadowContrib = 1.0;\\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\\n if (shadowEnabled)\\n {\\n shadowContrib = shadowContribsDir[i];\\n }\\n#endif\\n\\n float ndl = dot(N, normalize(lightDirection)) * shadowContrib;\\n\\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0);\\n }\\n#endif\\n\\n gl_FragColor.rgb *= diffuseColor;\\n\\n#ifdef ATMOSPHERE_ENABLED\\n float atmoIntensity = pow(1.0 - dot(v_Normal, (viewTranspose * vec4(0.0, 0.0, 1.0, 0.0)).xyz), glowPower);\\n gl_FragColor.rgb += glowColor * atmoIntensity;\\n#endif\\n\\n @import ecgl.common.emissiveLayer.main\\n\\n @import ecgl.common.wireframe.fragmentMain\\n}\\n\\n@end\"),Xe.import(\"@export ecgl.realistic.vertex\\n\\n@import ecgl.common.transformUniforms\\n\\n@import ecgl.common.uv.header\\n\\n@import ecgl.common.attributes\\n\\n\\n@import ecgl.common.wireframe.vertexHeader\\n\\n#ifdef VERTEX_COLOR\\nattribute vec4 a_Color : COLOR;\\nvarying vec4 v_Color;\\n#endif\\n\\n#ifdef NORMALMAP_ENABLED\\nattribute vec4 tangent : TANGENT;\\nvarying vec3 v_Tangent;\\nvarying vec3 v_Bitangent;\\n#endif\\n\\n@import ecgl.common.vertexAnimation.header\\n\\nvarying vec3 v_Normal;\\nvarying vec3 v_WorldPosition;\\n\\nvoid main()\\n{\\n\\n @import ecgl.common.uv.main\\n\\n @import ecgl.common.vertexAnimation.main\\n\\n gl_Position = worldViewProjection * vec4(pos, 1.0);\\n\\n v_Normal = normalize((worldInverseTranspose * vec4(norm, 0.0)).xyz);\\n v_WorldPosition = (world * vec4(pos, 1.0)).xyz;\\n\\n#ifdef VERTEX_COLOR\\n v_Color = a_Color;\\n#endif\\n\\n#ifdef NORMALMAP_ENABLED\\n v_Tangent = normalize((worldInverseTranspose * vec4(tangent.xyz, 0.0)).xyz);\\n v_Bitangent = normalize(cross(v_Normal, v_Tangent) * tangent.w);\\n#endif\\n\\n @import ecgl.common.wireframe.vertexMain\\n\\n}\\n\\n@end\\n\\n\\n\\n@export ecgl.realistic.fragment\\n\\n#define LAYER_DIFFUSEMAP_COUNT 0\\n#define LAYER_EMISSIVEMAP_COUNT 0\\n#define PI 3.14159265358979\\n#define ROUGHNESS_CHANEL 0\\n#define METALNESS_CHANEL 1\\n\\n#define NORMAL_UP_AXIS 1\\n#define NORMAL_FRONT_AXIS 2\\n\\n#ifdef VERTEX_COLOR\\nvarying vec4 v_Color;\\n#endif\\n\\n@import ecgl.common.uv.fragmentHeader\\n\\nvarying vec3 v_Normal;\\nvarying vec3 v_WorldPosition;\\n\\nuniform sampler2D diffuseMap;\\n\\nuniform sampler2D detailMap;\\nuniform sampler2D metalnessMap;\\nuniform sampler2D roughnessMap;\\n\\n@import ecgl.common.layers.header\\n\\nuniform float emissionIntensity: 1.0;\\n\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\n\\nuniform float metalness : 0.0;\\nuniform float roughness : 0.5;\\n\\nuniform mat4 viewInverse : VIEWINVERSE;\\n\\n#ifdef ATMOSPHERE_ENABLED\\nuniform mat4 viewTranspose: VIEWTRANSPOSE;\\nuniform vec3 glowColor;\\nuniform float glowPower;\\n#endif\\n\\n#ifdef AMBIENT_LIGHT_COUNT\\n@import clay.header.ambient_light\\n#endif\\n\\n#ifdef AMBIENT_SH_LIGHT_COUNT\\n@import clay.header.ambient_sh_light\\n#endif\\n\\n#ifdef AMBIENT_CUBEMAP_LIGHT_COUNT\\n@import clay.header.ambient_cubemap_light\\n#endif\\n\\n#ifdef DIRECTIONAL_LIGHT_COUNT\\n@import clay.header.directional_light\\n#endif\\n\\n@import ecgl.common.normalMap.fragmentHeader\\n\\n@import ecgl.common.ssaoMap.header\\n\\n@import ecgl.common.bumpMap.header\\n\\n@import clay.util.srgb\\n\\n@import clay.util.rgbm\\n\\n@import ecgl.common.wireframe.fragmentHeader\\n\\n@import clay.plugin.compute_shadow_map\\n\\nvec3 F_Schlick(float ndv, vec3 spec) {\\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\\n}\\n\\nfloat D_Phong(float g, float ndh) {\\n float a = pow(8192.0, g);\\n return (a + 2.0) / 8.0 * pow(ndh, a);\\n}\\n\\nvoid main()\\n{\\n vec4 albedoColor = color;\\n\\n vec3 eyePos = viewInverse[3].xyz;\\n vec3 V = normalize(eyePos - v_WorldPosition);\\n#ifdef VERTEX_COLOR\\n #ifdef SRGB_DECODE\\n albedoColor *= sRGBToLinear(v_Color);\\n #else\\n albedoColor *= v_Color;\\n #endif\\n#endif\\n\\n @import ecgl.common.albedo.main\\n\\n @import ecgl.common.diffuseLayer.main\\n\\n albedoColor *= albedoTexel;\\n\\n float m = metalness;\\n\\n#ifdef METALNESSMAP_ENABLED\\n float m2 = texture2D(metalnessMap, v_DetailTexcoord)[METALNESS_CHANEL];\\n m = clamp(m2 + (m - 0.5) * 2.0, 0.0, 1.0);\\n#endif\\n\\n vec3 baseColor = albedoColor.rgb;\\n albedoColor.rgb = baseColor * (1.0 - m);\\n vec3 specFactor = mix(vec3(0.04), baseColor, m);\\n\\n float g = 1.0 - roughness;\\n\\n#ifdef ROUGHNESSMAP_ENABLED\\n float g2 = 1.0 - texture2D(roughnessMap, v_DetailTexcoord)[ROUGHNESS_CHANEL];\\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\\n#endif\\n\\n vec3 N = v_Normal;\\n\\n#ifdef DOUBLE_SIDED\\n if (dot(N, V) < 0.0) {\\n N = -N;\\n }\\n#endif\\n\\n float ambientFactor = 1.0;\\n\\n#ifdef BUMPMAP_ENABLED\\n N = bumpNormal(v_WorldPosition, v_Normal, N);\\n ambientFactor = dot(v_Normal, N);\\n#endif\\n\\n@import ecgl.common.normalMap.fragmentMain\\n\\n vec3 N2 = vec3(N.x, N[NORMAL_UP_AXIS], N[NORMAL_FRONT_AXIS]);\\n\\n vec3 diffuseTerm = vec3(0.0);\\n vec3 specularTerm = vec3(0.0);\\n\\n float ndv = clamp(dot(N, V), 0.0, 1.0);\\n vec3 fresnelTerm = F_Schlick(ndv, specFactor);\\n\\n @import ecgl.common.ssaoMap.main\\n\\n#ifdef AMBIENT_LIGHT_COUNT\\n for(int _idx_ = 0; _idx_ < AMBIENT_LIGHT_COUNT; _idx_++)\\n {{\\n diffuseTerm += ambientLightColor[_idx_] * ambientFactor * ao;\\n }}\\n#endif\\n\\n#ifdef AMBIENT_SH_LIGHT_COUNT\\n for(int _idx_ = 0; _idx_ < AMBIENT_SH_LIGHT_COUNT; _idx_++)\\n {{\\n diffuseTerm += calcAmbientSHLight(_idx_, N2) * ambientSHLightColor[_idx_] * ao;\\n }}\\n#endif\\n\\n#ifdef DIRECTIONAL_LIGHT_COUNT\\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\\n float shadowContribsDir[DIRECTIONAL_LIGHT_COUNT];\\n if(shadowEnabled)\\n {\\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribsDir);\\n }\\n#endif\\n for(int _idx_ = 0; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++)\\n {{\\n vec3 L = -directionalLightDirection[_idx_];\\n vec3 lc = directionalLightColor[_idx_];\\n\\n vec3 H = normalize(L + V);\\n float ndl = clamp(dot(N, normalize(L)), 0.0, 1.0);\\n float ndh = clamp(dot(N, H), 0.0, 1.0);\\n\\n float shadowContrib = 1.0;\\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\\n if (shadowEnabled)\\n {\\n shadowContrib = shadowContribsDir[_idx_];\\n }\\n#endif\\n\\n vec3 li = lc * ndl * shadowContrib;\\n\\n diffuseTerm += li;\\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\\n }}\\n#endif\\n\\n\\n#ifdef AMBIENT_CUBEMAP_LIGHT_COUNT\\n vec3 L = reflect(-V, N);\\n L = vec3(L.x, L[NORMAL_UP_AXIS], L[NORMAL_FRONT_AXIS]);\\n float rough2 = clamp(1.0 - g, 0.0, 1.0);\\n float bias2 = rough2 * 5.0;\\n vec2 brdfParam2 = texture2D(ambientCubemapLightBRDFLookup[0], vec2(rough2, ndv)).xy;\\n vec3 envWeight2 = specFactor * brdfParam2.x + brdfParam2.y;\\n vec3 envTexel2;\\n for(int _idx_ = 0; _idx_ < AMBIENT_CUBEMAP_LIGHT_COUNT; _idx_++)\\n {{\\n envTexel2 = RGBMDecode(textureCubeLodEXT(ambientCubemapLightCubemap[_idx_], L, bias2), 8.12);\\n specularTerm += ambientCubemapLightColor[_idx_] * envTexel2 * envWeight2 * ao;\\n }}\\n#endif\\n\\n gl_FragColor.rgb = albedoColor.rgb * diffuseTerm + specularTerm;\\n gl_FragColor.a = albedoColor.a;\\n\\n#ifdef ATMOSPHERE_ENABLED\\n float atmoIntensity = pow(1.0 - dot(v_Normal, (viewTranspose * vec4(0.0, 0.0, 1.0, 0.0)).xyz), glowPower);\\n gl_FragColor.rgb += glowColor * atmoIntensity;\\n#endif\\n\\n#ifdef SRGB_ENCODE\\n gl_FragColor = linearTosRGB(gl_FragColor);\\n#endif\\n\\n @import ecgl.common.emissiveLayer.main\\n\\n @import ecgl.common.wireframe.fragmentMain\\n}\\n\\n@end\"),Xe.import(\"@export ecgl.hatching.vertex\\n\\n@import ecgl.realistic.vertex\\n\\n@end\\n\\n\\n@export ecgl.hatching.fragment\\n\\n#define NORMAL_UP_AXIS 1\\n#define NORMAL_FRONT_AXIS 2\\n\\n@import ecgl.common.uv.fragmentHeader\\n\\nvarying vec3 v_Normal;\\nvarying vec3 v_WorldPosition;\\n\\nuniform vec4 color : [0.0, 0.0, 0.0, 1.0];\\nuniform vec4 paperColor : [1.0, 1.0, 1.0, 1.0];\\n\\nuniform mat4 viewInverse : VIEWINVERSE;\\n\\n#ifdef AMBIENT_LIGHT_COUNT\\n@import clay.header.ambient_light\\n#endif\\n#ifdef AMBIENT_SH_LIGHT_COUNT\\n@import clay.header.ambient_sh_light\\n#endif\\n\\n#ifdef DIRECTIONAL_LIGHT_COUNT\\n@import clay.header.directional_light\\n#endif\\n\\n#ifdef VERTEX_COLOR\\nvarying vec4 v_Color;\\n#endif\\n\\n\\n@import ecgl.common.ssaoMap.header\\n\\n@import ecgl.common.bumpMap.header\\n\\n@import clay.util.srgb\\n\\n@import ecgl.common.wireframe.fragmentHeader\\n\\n@import clay.plugin.compute_shadow_map\\n\\nuniform sampler2D hatch1;\\nuniform sampler2D hatch2;\\nuniform sampler2D hatch3;\\nuniform sampler2D hatch4;\\nuniform sampler2D hatch5;\\nuniform sampler2D hatch6;\\n\\nfloat shade(in float tone) {\\n vec4 c = vec4(1. ,1., 1., 1.);\\n float step = 1. / 6.;\\n vec2 uv = v_DetailTexcoord;\\n if (tone <= step / 2.0) {\\n c = mix(vec4(0.), texture2D(hatch6, uv), 12. * tone);\\n }\\n else if (tone <= step) {\\n c = mix(texture2D(hatch6, uv), texture2D(hatch5, uv), 6. * tone);\\n }\\n if(tone > step && tone <= 2. * step){\\n c = mix(texture2D(hatch5, uv), texture2D(hatch4, uv) , 6. * (tone - step));\\n }\\n if(tone > 2. * step && tone <= 3. * step){\\n c = mix(texture2D(hatch4, uv), texture2D(hatch3, uv), 6. * (tone - 2. * step));\\n }\\n if(tone > 3. * step && tone <= 4. * step){\\n c = mix(texture2D(hatch3, uv), texture2D(hatch2, uv), 6. * (tone - 3. * step));\\n }\\n if(tone > 4. * step && tone <= 5. * step){\\n c = mix(texture2D(hatch2, uv), texture2D(hatch1, uv), 6. * (tone - 4. * step));\\n }\\n if(tone > 5. * step){\\n c = mix(texture2D(hatch1, uv), vec4(1.), 6. * (tone - 5. * step));\\n }\\n\\n return c.r;\\n}\\n\\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\\n\\nvoid main()\\n{\\n#ifdef SRGB_DECODE\\n vec4 inkColor = sRGBToLinear(color);\\n#else\\n vec4 inkColor = color;\\n#endif\\n\\n#ifdef VERTEX_COLOR\\n #ifdef SRGB_DECODE\\n inkColor *= sRGBToLinear(v_Color);\\n #else\\n inkColor *= v_Color;\\n #endif\\n#endif\\n\\n vec3 N = v_Normal;\\n#ifdef DOUBLE_SIDED\\n vec3 eyePos = viewInverse[3].xyz;\\n vec3 V = normalize(eyePos - v_WorldPosition);\\n\\n if (dot(N, V) < 0.0) {\\n N = -N;\\n }\\n#endif\\n\\n float tone = 0.0;\\n\\n float ambientFactor = 1.0;\\n\\n#ifdef BUMPMAP_ENABLED\\n N = bumpNormal(v_WorldPosition, v_Normal, N);\\n ambientFactor = dot(v_Normal, N);\\n#endif\\n\\n vec3 N2 = vec3(N.x, N[NORMAL_UP_AXIS], N[NORMAL_FRONT_AXIS]);\\n\\n @import ecgl.common.ssaoMap.main\\n\\n#ifdef AMBIENT_LIGHT_COUNT\\n for(int i = 0; i < AMBIENT_LIGHT_COUNT; i++)\\n {\\n tone += dot(ambientLightColor[i], w) * ambientFactor * ao;\\n }\\n#endif\\n#ifdef AMBIENT_SH_LIGHT_COUNT\\n for(int _idx_ = 0; _idx_ < AMBIENT_SH_LIGHT_COUNT; _idx_++)\\n {{\\n tone += dot(calcAmbientSHLight(_idx_, N2) * ambientSHLightColor[_idx_], w) * ao;\\n }}\\n#endif\\n#ifdef DIRECTIONAL_LIGHT_COUNT\\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\\n float shadowContribsDir[DIRECTIONAL_LIGHT_COUNT];\\n if(shadowEnabled)\\n {\\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribsDir);\\n }\\n#endif\\n for(int i = 0; i < DIRECTIONAL_LIGHT_COUNT; i++)\\n {\\n vec3 lightDirection = -directionalLightDirection[i];\\n float lightTone = dot(directionalLightColor[i], w);\\n\\n float shadowContrib = 1.0;\\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\\n if (shadowEnabled)\\n {\\n shadowContrib = shadowContribsDir[i];\\n }\\n#endif\\n\\n float ndl = dot(N, normalize(lightDirection)) * shadowContrib;\\n\\n tone += lightTone * clamp(ndl, 0.0, 1.0);\\n }\\n#endif\\n\\n gl_FragColor = mix(inkColor, paperColor, shade(clamp(tone, 0.0, 1.0)));\\n }\\n@end\\n\"),Xe.import(\"@export ecgl.sm.depth.vertex\\n\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\n\\nattribute vec3 position : POSITION;\\nattribute vec2 texcoord : TEXCOORD_0;\\n\\n#ifdef VERTEX_ANIMATION\\nattribute vec3 prevPosition;\\nuniform float percent : 1.0;\\n#endif\\n\\nvarying vec4 v_ViewPosition;\\nvarying vec2 v_Texcoord;\\n\\nvoid main(){\\n\\n#ifdef VERTEX_ANIMATION\\n vec3 pos = mix(prevPosition, position, percent);\\n#else\\n vec3 pos = position;\\n#endif\\n\\n v_ViewPosition = worldViewProjection * vec4(pos, 1.0);\\n gl_Position = v_ViewPosition;\\n\\n v_Texcoord = texcoord;\\n\\n}\\n@end\\n\\n\\n\\n@export ecgl.sm.depth.fragment\\n\\n@import clay.sm.depth.fragment\\n\\n@end\");var Wa=vi.prototype.addToScene,ja=vi.prototype.removeFromScene;vi.prototype.addToScene=function(e){if(Wa.call(this,e),this.__zr){var t=this.__zr;e.traverse((function(e){e.__zr=t,e.addAnimatorsToZr&&e.addAnimatorsToZr(t)}))}},vi.prototype.removeFromScene=function(e){ja.call(this,e),e.traverse((function(e){var t=e.__zr;e.__zr=null,t&&e.removeAnimatorsFromZr&&e.removeAnimatorsFromZr(t)}))},le.prototype.setTextureImage=function(e,t,r,i){if(this.shader){var n,a=r.getZr(),o=this;return o.autoUpdateTextureStatus=!1,o.disableTexture(e),!(s=t)||\"none\"===s||(n=Xa.loadTexture(t,r,i,(function(t){o.enableTexture(e),a&&a.refresh()})),o.set(e,n)),n}var s};var Xa={};Xa.Renderer=ut,Xa.Node=sr,Xa.Mesh=Sr,Xa.Shader=Xe,Xa.Material=le,Xa.Texture=wr,Xa.Texture2D=Dr,Xa.Geometry=Vr,Xa.SphereGeometry=En,Xa.PlaneGeometry=ki,Xa.CubeGeometry=Wi,Xa.AmbientLight=Cn,Xa.DirectionalLight=Dn,Xa.PointLight=Ln,Xa.SpotLight=Pn,Xa.PerspectiveCamera=Ei,Xa.OrthographicCamera=un,Xa.Vector2=_e,Xa.Vector3=vt,Xa.Vector4=Rn,Xa.Quaternion=qt,Xa.Matrix2=Gn,Xa.Matrix2d=Hn,Xa.Matrix3=jn,Xa.Matrix4=Ht,Xa.Plane=$r,Xa.Ray=Mt,Xa.BoundingBox=ir,Xa.Frustum=si;var qa=null;function Za(e){return Math.pow(2,Math.round(Math.log(e)/Math.LN2))}function Ya(e){if((e.wrapS===wr.REPEAT||e.wrapT===wr.REPEAT)&&e.image){var t=Za(e.width),r=Za(e.height);if(t!==e.width||r!==e.height){var i=document.createElement(\"canvas\");i.width=t,i.height=r,i.getContext(\"2d\").drawImage(e.image,0,0,t,r),e.image=i}}}Xa.loadTexture=function(e,t,r,i){\"function\"==typeof r&&(i=r,r={}),r=r||{};for(var n=Object.keys(r).sort(),a=\"\",o=0;o3?t[3]=e[3]:t[3]=1,t):((t=i.color.parse(e||\"#000\",t)||[0,0,0,0])[0]/=255,t[1]/=255,t[2]/=255,t)},Xa.directionFromAlphaBeta=function(e,t){var r=e/180*Math.PI+Math.PI/2,i=-t/180*Math.PI+Math.PI/2,n=[],a=Math.sin(r);return n[0]=a*Math.cos(i),n[1]=-Math.cos(r),n[2]=a*Math.sin(i),n},Xa.getShadowResolution=function(e){var t=1024;switch(e){case\"low\":t=512;break;case\"medium\":break;case\"high\":t=2048;break;case\"ultra\":t=4096}return t},Xa.COMMON_SHADERS=[\"lambert\",\"color\",\"realistic\",\"hatching\",\"shadow\"],Xa.createShader=function(e){\"ecgl.shadow\"===e&&(e=\"ecgl.displayShadow\");var t=Xe.source(e+\".vertex\"),r=Xe.source(e+\".fragment\");t||console.error(\"Vertex shader of '%s' not exits\",e),r||console.error(\"Fragment shader of '%s' not exits\",e);var i=new Xe(t,r);return i.name=e,i},Xa.createMaterial=function(e,t){t instanceof Array||(t=[t]);var r=Xa.createShader(e),i=new le({shader:r});return t.forEach((function(e){\"string\"==typeof e&&i.define(e)})),i},Xa.setMaterialFromModel=function(e,t,r,i){t.autoUpdateTextureStatus=!1;var n=r.getModel(e+\"Material\"),a=n.get(\"detailTexture\"),o=Mn(n.get(\"textureTiling\"),1),s=Mn(n.get(\"textureOffset\"),0);\"number\"==typeof o&&(o=[o,o]),\"number\"==typeof s&&(s=[s,s]);var l=o[0]>1||o[1]>1?Xa.Texture.REPEAT:Xa.Texture.CLAMP_TO_EDGE,h={anisotropic:8,wrapS:l,wrapT:l};if(\"realistic\"===e){var u=n.get(\"roughness\"),c=n.get(\"metalness\");null!=c?isNaN(c)&&(t.setTextureImage(\"metalnessMap\",c,i,h),c=Mn(n.get(\"metalnessAdjust\"),.5)):c=0,null!=u?isNaN(u)&&(t.setTextureImage(\"roughnessMap\",u,i,h),u=Mn(n.get(\"roughnessAdjust\"),.5)):u=.5;var d=n.get(\"normalTexture\");t.setTextureImage(\"detailMap\",a,i,h),t.setTextureImage(\"normalMap\",d,i,h),t.set({roughness:u,metalness:c,detailUvRepeat:o,detailUvOffset:s})}else if(\"lambert\"===e)t.setTextureImage(\"detailMap\",a,i,h),t.set({detailUvRepeat:o,detailUvOffset:s});else if(\"color\"===e)t.setTextureImage(\"detailMap\",a,i,h),t.set({detailUvRepeat:o,detailUvOffset:s});else if(\"hatching\"===e){var f=n.get(\"hatchingTextures\")||[];f.length;for(var p=0;p<6;p++)t.setTextureImage(\"hatch\"+(p+1),f[p],i,{anisotropic:8,wrapS:Xa.Texture.REPEAT,wrapT:Xa.Texture.REPEAT});t.set({detailUvRepeat:o,detailUvOffset:s})}},Xa.updateVertexAnimation=function(e,t,r,i){var n=i.get(\"animation\"),a=i.get(\"animationDurationUpdate\"),o=i.get(\"animationEasingUpdate\"),s=r.shadowDepthMaterial;if(n&&t&&a>0&&t.geometry.vertexCount===r.geometry.vertexCount){r.material.define(\"vertex\",\"VERTEX_ANIMATION\"),r.ignorePreZ=!0,s&&s.define(\"vertex\",\"VERTEX_ANIMATION\");for(var l=0;l=0&&this._viewsToDispose.splice(t,1),this.views.push(e),e.layer=this;var r=this.zr;e.scene.traverse((function(e){e.__zr=r,e.addAnimatorsToZr&&e.addAnimatorsToZr(r)}))}},Ja.prototype.removeView=function(e){if(e.layer===this){var t=this.views.indexOf(e);t>=0&&(this.views.splice(t,1),e.scene.traverse($a,this),e.layer=null,this._viewsToDispose.push(e))}},Ja.prototype.removeViewsAll=function(){this.views.forEach((function(e){e.scene.traverse($a,this),e.layer=null,this._viewsToDispose.push(e)}),this),this.views.length=0},Ja.prototype.resize=function(e,t){this.renderer.resize(e,t)},Ja.prototype.clear=function(){var e=this.renderer.gl,t=this._backgroundColor||[0,0,0,0];e.clearColor(t[0],t[1],t[2],t[3]),e.depthMask(!0),e.colorMask(!0,!0,!0,!0),e.clear(e.DEPTH_BUFFER_BIT|e.COLOR_BUFFER_BIT)},Ja.prototype.clearDepth=function(){var e=this.renderer.gl;e.clear(e.DEPTH_BUFFER_BIT)},Ja.prototype.clearColor=function(){var e=this.renderer.gl;e.clearColor(0,0,0,0),e.clear(e.COLOR_BUFFER_BIT)},Ja.prototype.needsRefresh=function(){this.zr.refresh()},Ja.prototype.refresh=function(e){this._backgroundColor=e?Ka.parseColor(e):[0,0,0,0],this.renderer.clearColor=this._backgroundColor;for(var t=0;t20)){e=e.event;var i=this.pickObject(e.offsetX,e.offsetY);i&&(this._dispatchEvent(e.type,e,i),this._dispatchDataEvent(e.type,e,i));var n=this._clickToSetFocusPoint(e);n&&n.view.setDOFFocusOnPoint(n.distance)&&this.zr.refresh()}}},Ja.prototype._clickToSetFocusPoint=function(e){for(var t=this.renderer,r=t.viewport,i=this.views.length-1;i>=0;i--){var n=this.views[i];if(n.hasDOF()&&n.containPoint(e.offsetX,e.offsetY)){this._picking.scene=n.scene,this._picking.camera=n.camera,t.viewport=n.viewport;var a=this._picking.pick(e.offsetX,e.offsetY,!0);if(a)return a.view=n,a}}t.viewport=r},Ja.prototype.onglobalout=function(e){var t=this._hovered;t&&this._dispatchEvent(\"mouseout\",e,{target:t.target})},Ja.prototype.pickObject=function(e,t){for(var r=[],i=this.renderer,n=i.viewport,a=0;a=0&&(c.dataIndex=this._lastDataIndex,c.seriesIndex=this._lastSeriesIndex,this.zr.handler.dispatchToElement(u,\"mouseout\",t)),l=!0):null!=s&&s!==this._lastEventData&&(null!=this._lastEventData&&(c.eventData=this._lastEventData,this.zr.handler.dispatchToElement(u,\"mouseout\",t)),l=!0),this._lastEventData=s,this._lastDataIndex=a,this._lastSeriesIndex=o),c.eventData=s,c.dataIndex=a,c.seriesIndex=o,(null!=s||parseInt(a,10)>=0&&parseInt(o,10)>=0)&&(this.zr.handler.dispatchToElement(u,e,t),l&&this.zr.handler.dispatchToElement(u,\"mouseover\",t))},Ja.prototype._dispatchToView=function(e,t){for(var r=0;re&&o=0&&(function(e){so(e,\"itemStyle\"),so(e,\"lineStyle\"),so(e,\"areaStyle\"),so(e,\"label\")}(t),\"mapbox\"===t.coordinateSystem&&(t.coordinateSystem=\"mapbox3D\",e.mapbox3D=e.mapbox))})),lo(e.xAxis3D),lo(e.yAxis3D),lo(e.zAxis3D),lo(e.grid3D),so(e.geo3D)}));const uo={defaultOption:{viewControl:{projection:\"perspective\",autoRotate:!1,autoRotateDirection:\"cw\",autoRotateSpeed:10,autoRotateAfterStill:3,damping:.8,rotateSensitivity:1,zoomSensitivity:1,panSensitivity:1,panMouseButton:\"middle\",rotateMouseButton:\"left\",distance:150,minDistance:40,maxDistance:400,orthographicSize:150,maxOrthographicSize:400,minOrthographicSize:20,center:[0,0,0],alpha:0,beta:0,minAlpha:-90,maxAlpha:90}},setView:function(e){e=e||{},this.option.viewControl=this.option.viewControl||{},null!=e.alpha&&(this.option.viewControl.alpha=e.alpha),null!=e.beta&&(this.option.viewControl.beta=e.beta),null!=e.distance&&(this.option.viewControl.distance=e.distance),null!=e.center&&(this.option.viewControl.center=e.center)}},co={defaultOption:{postEffect:{enable:!1,bloom:{enable:!0,intensity:.1},depthOfField:{enable:!1,focalRange:20,focalDistance:50,blurRadius:10,fstop:2.8,quality:\"medium\"},screenSpaceAmbientOcclusion:{enable:!1,radius:2,quality:\"medium\",intensity:1},screenSpaceReflection:{enable:!1,quality:\"medium\",maxRoughness:.8},colorCorrection:{enable:!0,exposure:0,brightness:0,contrast:1,saturation:1,lookupTexture:\"\"},edge:{enable:!1},FXAA:{enable:!1}},temporalSuperSampling:{enable:\"auto\"}}},fo={defaultOption:{light:{main:{shadow:!1,shadowQuality:\"high\",color:\"#fff\",intensity:1,alpha:0,beta:0},ambient:{color:\"#fff\",intensity:.2},ambientCubemap:{texture:null,exposure:1,diffuseIntensity:.5,specularIntensity:.5}}}};var po=i.ComponentModel.extend({type:\"grid3D\",dependencies:[\"xAxis3D\",\"yAxis3D\",\"zAxis3D\"],defaultOption:{show:!0,zlevel:-10,left:0,top:0,width:\"100%\",height:\"100%\",environment:\"auto\",boxWidth:100,boxHeight:100,boxDepth:100,axisPointer:{show:!0,lineStyle:{color:\"rgba(0, 0, 0, 0.8)\",width:1},label:{show:!0,formatter:null,margin:8,textStyle:{fontSize:14,color:\"#fff\",backgroundColor:\"rgba(0,0,0,0.5)\",padding:3,borderRadius:3}}},axisLine:{show:!0,lineStyle:{color:\"#333\",width:2,type:\"solid\"}},axisTick:{show:!0,inside:!1,length:3,lineStyle:{width:1}},axisLabel:{show:!0,inside:!1,rotate:0,margin:8,textStyle:{fontSize:12}},splitLine:{show:!0,lineStyle:{color:[\"#ccc\"],width:1,type:\"solid\"}},splitArea:{show:!1,areaStyle:{color:[\"rgba(250,250,250,0.3)\",\"rgba(200,200,200,0.3)\"]}},light:{main:{alpha:30,beta:40},ambient:{intensity:.4}},viewControl:{alpha:20,beta:40,autoRotate:!1,distance:200,minDistance:40,maxDistance:400}}});i.util.merge(po.prototype,uo),i.util.merge(po.prototype,co),i.util.merge(po.prototype,fo);const mo=po;function go(e,t){switch(e){case\"center\":case\"middle\":e=\"50%\";break;case\"left\":case\"top\":e=\"0%\";break;case\"right\":case\"bottom\":e=\"100%\"}return\"string\"==typeof e?(r=e,r.replace(/^\\s+|\\s+$/g,\"\")).match(/%$/)?parseFloat(e)/100*t:parseFloat(e):null==e?NaN:+e;var r}function _o(){var e=\"__ec_inner_\"+vo++;return function(t){return t[e]||(t[e]={})}}var vo=Math.round(9*Math.random()),yo={};function xo(e,t,r,i,n){var a={};return function(e,t,r,i,n){r=r||yo;var a,o=t.ecModel,s=o&&o.option.textStyle,l=function(e){for(var t;e&&e!==e.ecModel;){var r=(e.option||yo).rich;if(r){t=t||{};for(var i=ba(r),n=0;n0&&this._notFirst?this.animateTo({alpha:h,beta:u,center:c,distance:a,orthographicSize:o,easing:l.animationEasingUpdate,duration:l.animationDurationUpdate}):(this.setDistance(a),this.setAlpha(h),this.setBeta(u),this.setCenter(c),this.setOrthographicSize(o)),this._notFirst=!0,this._validateProperties()},_validateProperties:function(){},animateTo:function(e){var t=this.zr,r=this,i={},n={};return null!=e.distance&&(i.distance=this.getDistance(),n.distance=e.distance),null!=e.orthographicSize&&(i.orthographicSize=this.getOrthographicSize(),n.orthographicSize=e.orthographicSize),null!=e.alpha&&(i.alpha=this.getAlpha(),n.alpha=e.alpha),null!=e.beta&&(i.beta=this.getBeta(),n.beta=e.beta),null!=e.center&&(i.center=this.getCenter(),n.center=e.center),this._addAnimator(t.animation.animate(i).when(e.duration||1e3,n).during((function(){null!=i.alpha&&r.setAlpha(i.alpha),null!=i.beta&&r.setBeta(i.beta),null!=i.distance&&r.setDistance(i.distance),null!=i.center&&r.setCenter(i.center),null!=i.orthographicSize&&r.setOrthographicSize(i.orthographicSize),r._needsUpdate=!0}))).start(e.easing||\"linear\")},stopAllAnimation:function(){for(var e=0;e0},_update:function(e){if(this._rotating){var t=(\"cw\"===this.autoRotateDirection?1:-1)*this.autoRotateSpeed/180*Math.PI;this._phi-=t*e/1e3,this._needsUpdate=!0}else this._rotateVelocity.len()>0&&(this._needsUpdate=!0);(Math.abs(this._zoomSpeed)>.1||this._panVelocity.len()>0)&&(this._needsUpdate=!0),this._needsUpdate&&(e=Math.min(e,50),this._updateDistanceOrSize(e),this._updatePan(e),this._updateRotate(e),this._updateTransform(),this.getCamera().update(),this.zr&&this.zr.refresh(),this.trigger(\"update\"),this._needsUpdate=!1)},_updateRotate:function(e){var t=this._rotateVelocity;this._phi=t.y*e/20+this._phi,this._theta=t.x*e/20+this._theta,this.setAlpha(this.getAlpha()),this.setBeta(this.getBeta()),this._vectorDamping(t,Math.pow(this.damping,e/16))},_updateDistanceOrSize:function(e){\"perspective\"===this._projection?this._setDistance(this._distance+this._zoomSpeed*e/20):this._setOrthoSize(this._orthoSize+this._zoomSpeed*e/20),this._zoomSpeed*=Math.pow(this.damping,e/16)},_setDistance:function(e){this._distance=Math.max(Math.min(e,this.maxDistance),this.minDistance)},_setOrthoSize:function(e){this._orthoSize=Math.max(Math.min(e,this.maxOrthographicSize),this.minOrthographicSize);var t=this.getCamera(),r=this._orthoSize,i=r/this.viewGL.viewport.height*this.viewGL.viewport.width;t.left=-i/2,t.right=i/2,t.top=r/2,t.bottom=-r/2},_updatePan:function(e){var t=this._panVelocity,r=this._distance,i=this.getCamera(),n=i.worldTransform.y,a=i.worldTransform.x;this._center.scaleAndAdd(a,-t.x*r/200).scaleAndAdd(n,-t.y*r/200),this._vectorDamping(t,0)},_updateTransform:function(){var e=this.getCamera(),t=new vt,r=this._theta+Math.PI/2,i=this._phi+Math.PI/2,n=Math.sin(r);t.x=n*Math.cos(i),t.y=-Math.cos(r),t.z=n*Math.sin(i),e.position.copy(this._center).scaleAndAdd(t,this._distance),e.rotation.identity().rotateY(-this._phi).rotateX(-this._theta)},_startCountingStill:function(){clearTimeout(this._stillTimeout);var e=this.autoRotateAfterStill,t=this;!isNaN(e)&&e>0&&(this._stillTimeout=setTimeout((function(){t._rotating=!0}),1e3*e))},_vectorDamping:function(e,t){var r=e.len();(r*=t)<1e-4&&(r=0),e.normalize().scale(r)},_decomposeTransform:function(){if(this.getCamera()){this.getCamera().updateWorldTransform();var e=this.getCamera().worldTransform.z,t=Math.asin(e.y),r=Math.atan2(e.x,e.z);this._theta=t,this._phi=-r,this.setBeta(this.getBeta()),this.setAlpha(this.getAlpha()),this.getCamera().aspect?this._setDistance(this.getCamera().position.dist(this._center)):this._setOrthoSize(this.getCamera().top-this.getCamera().bottom)}},_mouseDownHandler:function(e){if(!e.target&&!this._isAnimating()){var t=e.offsetX,r=e.offsetY;this.viewGL&&!this.viewGL.containPoint(t,r)||(this.zr.on(\"mousemove\",this._mouseMoveHandler),this.zr.on(\"mouseup\",this._mouseUpHandler),e.event.targetTouches?1===e.event.targetTouches.length&&(this._mode=\"rotate\"):e.event.button===Ao[this.rotateMouseButton]?this._mode=\"rotate\":e.event.button===Ao[this.panMouseButton]?this._mode=\"pan\":this._mode=\"\",this._rotateVelocity.set(0,0),this._rotating=!1,this.autoRotate&&this._startCountingStill(),this._mouseX=e.offsetX,this._mouseY=e.offsetY)}},_mouseMoveHandler:function(e){if(!(e.target&&e.target.__isGLToZRProxy||this._isAnimating())){var t=Eo(this.panSensitivity),r=Eo(this.rotateSensitivity);\"rotate\"===this._mode?(this._rotateVelocity.y=(e.offsetX-this._mouseX)/this.zr.getHeight()*2*r[0],this._rotateVelocity.x=(e.offsetY-this._mouseY)/this.zr.getWidth()*2*r[1]):\"pan\"===this._mode&&(this._panVelocity.x=(e.offsetX-this._mouseX)/this.zr.getWidth()*t[0]*400,this._panVelocity.y=(-e.offsetY+this._mouseY)/this.zr.getHeight()*t[1]*400),this._mouseX=e.offsetX,this._mouseY=e.offsetY,e.event.preventDefault()}},_mouseWheelHandler:function(e){if(!this._isAnimating()){var t=e.event.wheelDelta||-e.event.detail;this._zoomHandler(e,t)}},_pinchHandler:function(e){this._isAnimating()||(this._zoomHandler(e,e.pinchScale>1?1:-1),this._mode=\"\")},_zoomHandler:function(e,t){if(0!==t){var r,i=e.offsetX,n=e.offsetY;this.viewGL&&!this.viewGL.containPoint(i,n)||(r=\"perspective\"===this._projection?Math.max(Math.max(Math.min(this._distance-this.minDistance,this.maxDistance-this._distance))/20,.5):Math.max(Math.max(Math.min(this._orthoSize-this.minOrthographicSize,this.maxOrthographicSize-this._orthoSize))/20,.5),this._zoomSpeed=(t>0?-1:1)*r*this.zoomSensitivity,this._rotating=!1,this.autoRotate&&\"rotate\"===this._mode&&this._startCountingStill(),e.event.preventDefault())}},_mouseUpHandler:function(){this.zr.off(\"mousemove\",this._mouseMoveHandler),this.zr.off(\"mouseup\",this._mouseUpHandler)},_isRightMouseButtonUsed:function(){return\"right\"===this.rotateMouseButton||\"right\"===this.panMouseButton},_contextMenuHandler:function(e){this._isRightMouseButtonUsed()&&e.preventDefault()},_addAnimator:function(e){var t=this._animators;return t.push(e),e.done((function(){var r=t.indexOf(e);r>=0&&t.splice(r,1)})),e}});Object.defineProperty(Co.prototype,\"autoRotate\",{get:function(e){return this._autoRotate},set:function(e){this._autoRotate=e,this._rotating=e}});const Do=Co,Lo={convertToDynamicArray:function(e){e&&this.resetOffset();var t=this.attributes;for(var r in t)e||!t[r].value?t[r].value=[]:t[r].value=Array.prototype.slice.call(t[r].value);e||!this.indices?this.indices=[]:this.indices=Array.prototype.slice.call(this.indices)},convertToTypedArray:function(){var e=this.attributes;for(var t in e)e[t].value&&e[t].value.length>0?e[t].value=new Float32Array(e[t].value):e[t].value=null;this.indices&&this.indices.length>0&&(this.indices=this.vertexCount>65535?new Uint32Array(this.indices):new Uint16Array(this.indices)),this.dirty()}},Po={vec2:pe,vec3:Qe,vec4:Et,mat2:Fn,mat2d:kn,mat3:Dt,mat4:Ye,quat:Rt};var Oo=Po.vec3,No=[[0,0],[1,1]],Io=Vr.extend((function(){return{segmentScale:1,dynamic:!0,useNativeLine:!0,attributes:{position:new Vr.Attribute(\"position\",\"float\",3,\"POSITION\"),positionPrev:new Vr.Attribute(\"positionPrev\",\"float\",3),positionNext:new Vr.Attribute(\"positionNext\",\"float\",3),prevPositionPrev:new Vr.Attribute(\"prevPositionPrev\",\"float\",3),prevPosition:new Vr.Attribute(\"prevPosition\",\"float\",3),prevPositionNext:new Vr.Attribute(\"prevPositionNext\",\"float\",3),offset:new Vr.Attribute(\"offset\",\"float\",1),color:new Vr.Attribute(\"color\",\"float\",4,\"COLOR\")}}}),{resetOffset:function(){this._vertexOffset=0,this._triangleOffset=0,this._itemVertexOffsets=[]},setVertexCount:function(e){var t=this.attributes;this.vertexCount!==e&&(t.position.init(e),t.color.init(e),this.useNativeLine||(t.positionPrev.init(e),t.positionNext.init(e),t.offset.init(e)),e>65535?this.indices instanceof Uint16Array&&(this.indices=new Uint32Array(this.indices)):this.indices instanceof Uint32Array&&(this.indices=new Uint16Array(this.indices)))},setTriangleCount:function(e){this.triangleCount!==e&&(this.indices=0===e?null:this.vertexCount>65535?new Uint32Array(3*e):new Uint16Array(3*e))},_getCubicCurveApproxStep:function(e,t,r,i){return 1/(Oo.dist(e,t)+Oo.dist(r,t)+Oo.dist(i,r)+1)*this.segmentScale},getCubicCurveVertexCount:function(e,t,r,i){var n=this._getCubicCurveApproxStep(e,t,r,i),a=Math.ceil(1/n);return this.useNativeLine?2*a:2*a+2},getCubicCurveTriangleCount:function(e,t,r,i){var n=this._getCubicCurveApproxStep(e,t,r,i),a=Math.ceil(1/n);return this.useNativeLine?0:2*a},getLineVertexCount:function(){return this.getPolylineVertexCount(No)},getLineTriangleCount:function(){return this.getPolylineTriangleCount(No)},getPolylineVertexCount:function(e){var t;return t=\"number\"==typeof e?e:\"number\"!=typeof e[0]?e.length:e.length/3,this.useNativeLine?2*(t-1):2*(t-1)+2},getPolylineTriangleCount:function(e){var t;return t=\"number\"==typeof e?e:\"number\"!=typeof e[0]?e.length:e.length/3,this.useNativeLine?0:2*Math.max(t-1,0)},addCubicCurve:function(e,t,r,i,n,a){null==a&&(a=1);var o=e[0],s=e[1],l=e[2],h=t[0],u=t[1],c=t[2],d=r[0],f=r[1],p=r[2],m=i[0],g=i[1],_=i[2],v=this._getCubicCurveApproxStep(e,t,r,i),y=v*v,x=y*v,b=3*v,w=3*y,T=6*y,S=6*x,M=o-2*h+d,A=s-2*u+f,E=l-2*c+p,C=3*(h-d)-o+m,D=3*(u-f)-s+g,L=3*(c-p)-l+_,P=o,O=s,N=l,I=(h-o)*b+M*w+C*x,R=(u-s)*b+A*w+D*x,B=(c-l)*b+E*w+L*x,F=M*T+C*S,z=A*T+D*S,G=E*T+L*S,U=C*S,k=D*S,V=L*S,H=0,W=0,j=Math.ceil(1/v),X=new Float32Array(3*(j+1)),q=(X=[],0);for(W=0;W1&&(P=I>0?Math.min(P,m):Math.max(P,m),O=R>0?Math.min(O,g):Math.max(O,g),N=B>0?Math.min(N,_):Math.max(N,_));return this.addPolyline(X,n,a)},addLine:function(e,t,r,i){return this.addPolyline([e,t],r,i)},addPolyline:function(e,t,r,i,n){if(e.length){var a=\"number\"!=typeof e[0];if(null==n&&(n=a?e.length:e.length/3),!(n<2)){null==i&&(i=0),null==r&&(r=1),this._itemVertexOffsets.push(this._vertexOffset);var o,s,l=(a=\"number\"!=typeof e[0])?\"number\"!=typeof t[0]:t.length/4===n,h=this.attributes.position,u=this.attributes.positionPrev,c=this.attributes.positionNext,d=this.attributes.color,f=this.attributes.offset,p=this.indices,m=this._vertexOffset;r=Math.max(r,.01);for(var g=i;g1&&(h.copy(m,m-1),d.copy(m,m-1),m++):(g0&&(c.set(m-2,o),c.set(m-1,o)),h.set(m,o),h.set(m+1,o),d.set(m,s),d.set(m+1,s),f.set(m,r/2),f.set(m+1,-r/2),m+=2),this.useNativeLine)d.set(m,s),h.set(m,o),m++;else if(g>0){var y=3*this._triangleOffset;(p=this.indices)[y]=m-4,p[y+1]=m-3,p[y+2]=m-2,p[y+3]=m-3,p[y+4]=m-1,p[y+5]=m-2,this._triangleOffset+=2}}if(!this.useNativeLine){var x=this._vertexOffset,b=this._vertexOffset+2*n;u.copy(x,x+2),u.copy(x+1,x+3),c.copy(b-1,b-3),c.copy(b-2,b-4)}return this._vertexOffset=m,this._vertexOffset}}},setItemColor:function(e,t){for(var r=this._itemVertexOffsets[e],i=eo&&(n=this._x=0,a+=this._rowHeight+l,this._y=a,this._rowHeight=0),this._x+=t+l,this._rowHeight=Math.max(this._rowHeight,r),a+r+l>s)return null;e.x+=this.offsetX*this.dpr+n,e.y+=this.offsetY*this.dpr+a,this._zr.add(e);var h=[this.offsetX/this.width,this.offsetY/this.height];return[[n/o+h[0],a/s+h[1]],[(n+t)/o+h[0],(a+r)/s+h[1]]]},_fitElement:function(e,t,r){var i=e.getBoundingRect(),n=t/i.width,a=r/i.height;e.x=-i.x*n,e.y=-i.y*a,e.scaleX=n,e.scaleY=a,e.update()}},Fo.prototype={clear:function(){for(var e=0;e=e)){var n=(r+this._nodeWidth)*this._dpr,a=(i+this._nodeHeight)*this._dpr;try{this._zr.resize({width:n,height:a})}catch(e){this._canvas.width=n,this._canvas.height=a}var o=new Bo(this._zr,r,i,this._nodeWidth,this._nodeHeight,this._gap,this._dpr);return this._textureAtlasNodes.push(o),o}},add:function(e,t,r){if(this._coords[e.id])return this._coords[e.id];var i=this._getCurrentNode().add(e,t,r);if(!i){var n=this._expand();if(!n)return;i=n.add(e,t,r)}return this._coords[e.id]=i,i},getCoordsScale:function(){var e=this._dpr;return[this._nodeWidth/this._canvas.width*e,this._nodeHeight/this._canvas.height*e]},getCoords:function(e){return this._coords[e]},dispose:function(){this._zr.dispose()}};const zo=Fo;function Go(){}Go.prototype={constructor:Go,setScene:function(e){this._scene=e,this._skybox&&this._skybox.attachScene(this._scene)},initLight:function(e){this._lightRoot=e,this.mainLight=new Ka.DirectionalLight({shadowBias:.005}),this.ambientLight=new Ka.AmbientLight,e.add(this.mainLight),e.add(this.ambientLight)},dispose:function(){this._lightRoot&&(this._lightRoot.remove(this.mainLight),this._lightRoot.remove(this.ambientLight))},updateLight:function(e){var t=this.mainLight,r=this.ambientLight,i=e.getModel(\"light\"),n=i.getModel(\"main\"),a=i.getModel(\"ambient\");t.intensity=n.get(\"intensity\"),r.intensity=a.get(\"intensity\"),t.color=Ka.parseColor(n.get(\"color\")).slice(0,3),r.color=Ka.parseColor(a.get(\"color\")).slice(0,3);var o=n.get(\"alpha\")||0,s=n.get(\"beta\")||0;t.position.setArray(Ka.directionFromAlphaBeta(o,s)),t.lookAt(Ka.Vector3.ZERO),t.castShadow=n.get(\"shadow\"),t.shadowResolution=Ka.getShadowResolution(n.get(\"shadowQuality\"))},updateAmbientCubemap:function(e,t,r){var i=t.getModel(\"light.ambientCubemap\"),n=i.get(\"texture\");if(n){this._cubemapLightsCache=this._cubemapLightsCache||{};var a=this._cubemapLightsCache[n];if(!a){var o=this;a=this._cubemapLightsCache[n]=Ka.createAmbientCubemap(i.option,e,r,(function(){o._isSkyboxFromAmbientCubemap&&o._skybox.setEnvironmentMap(a.specular.cubemap),r.getZr().refresh()}))}this._lightRoot.add(a.diffuse),this._lightRoot.add(a.specular),this._currentCubemapLights=a}else this._currentCubemapLights&&(this._lightRoot.remove(this._currentCubemapLights.diffuse),this._lightRoot.remove(this._currentCubemapLights.specular),this._currentCubemapLights=null)},updateSkybox:function(e,t,r){var n=t.get(\"environment\"),a=this,o=(a._skybox=a._skybox||new ji,a._skybox);if(n&&\"none\"!==n)if(\"auto\"===n)if(this._isSkyboxFromAmbientCubemap=!0,this._currentCubemapLights){var s=this._currentCubemapLights.specular.cubemap;o.setEnvironmentMap(s),this._scene&&o.attachScene(this._scene),o.material.set(\"lod\",3)}else this._skybox&&this._skybox.detachScene();else if(\"object\"==typeof n&&n.colorStops||\"string\"==typeof n&&i.color.parse(n)){this._isSkyboxFromAmbientCubemap=!1;var l=new Ka.Texture2D({anisotropic:8,flipY:!1});o.setEnvironmentMap(l);var h=l.image=document.createElement(\"canvas\");h.width=h.height=16;var u=h.getContext(\"2d\"),c=new i.graphic.Rect({shape:{x:0,y:0,width:16,height:16},style:{fill:n}});i.innerDrawElementOnCanvas(u,c),o.attachScene(this._scene)}else this._isSkyboxFromAmbientCubemap=!1,l=Ka.loadTexture(n,r,{anisotropic:8,flipY:!1}),o.setEnvironmentMap(l),o.attachScene(this._scene);else this._skybox&&this._skybox.detachScene(this._scene),this._skybox=null;var d=t.coordinateSystem;if(this._skybox)if(!d||!d.viewGL||\"auto\"===n||n.match&&n.match(/.hdr$/))this._skybox.material.undefine(\"fragment\",\"SRGB_DECODE\");else{var f=d.viewGL.isLinearSpace()?\"define\":\"undefine\";this._skybox.material[f](\"fragment\",\"SRGB_DECODE\")}}};const Uo=Go;var ko=Po.vec3,Vo=Vr.extend((function(){return{segmentScale:1,useNativeLine:!0,attributes:{position:new Vr.Attribute(\"position\",\"float\",3,\"POSITION\"),normal:new Vr.Attribute(\"normal\",\"float\",3,\"NORMAL\"),color:new Vr.Attribute(\"color\",\"float\",4,\"COLOR\")}}}),{resetOffset:function(){this._vertexOffset=0,this._faceOffset=0},setQuadCount:function(e){var t=this.attributes,r=this.getQuadVertexCount()*e,i=this.getQuadTriangleCount()*e;this.vertexCount!==r&&(t.position.init(r),t.normal.init(r),t.color.init(r)),this.triangleCount!==i&&(this.indices=r>65535?new Uint32Array(3*i):new Uint16Array(3*i))},getQuadVertexCount:function(){return 4},getQuadTriangleCount:function(){return 2},addQuad:function(){var e=ko.create(),t=ko.create(),r=ko.create(),i=[0,3,1,3,2,1];return function(n,a){var o=this.attributes.position,s=this.attributes.normal,l=this.attributes.color;ko.sub(e,n[1],n[0]),ko.sub(t,n[2],n[1]),ko.cross(r,e,t),ko.normalize(r,r);for(var h=0;h<4;h++)o.set(this._vertexOffset+h,n[h]),l.set(this._vertexOffset+h,a),s.set(this._vertexOffset+h,r);var u=3*this._faceOffset;for(h=0;h<6;h++)this.indices[u+h]=i[h]+this._vertexOffset;this._vertexOffset+=4,this._faceOffset+=2}}()});i.util.defaults(Vo.prototype,Lo);const Ho=Vo;var Wo=Mn,jo={x:0,y:2,z:1};function Xo(e,t,r){this.rootNode=new Ka.Node;var i=new Ka.Mesh({geometry:new Ro({useNativeLine:!1}),material:t,castShadow:!1,ignorePicking:!0,$ignorePicking:!0,renderOrder:1}),n=new Ka.Mesh({geometry:new Ho,material:r,castShadow:!1,culling:!1,ignorePicking:!0,$ignorePicking:!0,renderOrder:0});this.rootNode.add(n),this.rootNode.add(i),this.faceInfo=e,this.plane=new Ka.Plane,this.linesMesh=i,this.quadsMesh=n}Xo.prototype.update=function(e,t,r){var i=e.coordinateSystem,n=[i.getAxis(this.faceInfo[0]),i.getAxis(this.faceInfo[1])],a=this.linesMesh.geometry,o=this.quadsMesh.geometry;a.convertToDynamicArray(!0),o.convertToDynamicArray(!0),this._updateSplitLines(a,n,e,r),this._udpateSplitAreas(o,n,e,r),a.convertToTypedArray(),o.convertToTypedArray();var s=i.getAxis(this.faceInfo[2]);!function(e,t,r,i){var n=[0,0,0],a=i<0?r.getExtentMin():r.getExtentMax();n[jo[r.dim]]=a,e.position.setArray(n),e.rotation.identity(),t.distance=-Math.abs(a),t.normal.set(0,0,0),\"x\"===r.dim?(e.rotation.rotateY(i*Math.PI/2),t.normal.x=-i):\"z\"===r.dim?(e.rotation.rotateX(-i*Math.PI/2),t.normal.y=-i):(i>0&&e.rotation.rotateY(Math.PI),t.normal.z=-i)}(this.rootNode,this.plane,s,this.faceInfo[3])},Xo.prototype._updateSplitLines=function(e,t,r,n){var a=n.getDevicePixelRatio();t.forEach((function(n,o){var s=n.model,l=t[1-o].getExtent();if(!n.scale.isBlank()){var h=s.getModel(\"splitLine\",r.getModel(\"splitLine\"));if(h.get(\"show\")){var u=h.getModel(\"lineStyle\"),c=u.get(\"color\"),d=Wo(u.get(\"opacity\"),1),f=Wo(u.get(\"width\"),1);c=i.util.isArray(c)?c:[c];for(var p=n.getTicksCoords({tickModel:h}),m=0,g=0;g65535?new Uint32Array(3*r):new Uint16Array(3*r))},setSpriteAlign:function(e,t,r,i,n){var a,o,s,l;switch(null==r&&(r=\"left\"),null==i&&(i=\"top\"),n=n||0,r){case\"left\":a=n,s=t[0]+n;break;case\"center\":case\"middle\":a=-t[0]/2,s=t[0]/2;break;case\"right\":a=-t[0]-n,s=-n}switch(i){case\"bottom\":o=n,l=t[1]+n;break;case\"middle\":o=-t[1]/2,l=t[1]/2;break;case\"top\":o=-t[1]-n,l=-n}var h=4*e,u=this.attributes.offset;u.set(h,[a,l]),u.set(h+1,[s,l]),u.set(h+2,[s,o]),u.set(h+3,[a,o])},addSprite:function(e,t,r,i,n,a){var o=this._vertexOffset;this.setSprite(this._vertexOffset/4,e,t,r,i,n,a);for(var s=0;s 0.0) {\\n currProj = clipNear(currProj, nextProj);\\n }\\n else if (prevProj.w > 0.0) {\\n currProj = clipNear(currProj, prevProj);\\n }\\n }\\n\\n vec2 prevScreen = (prevProj.xy / abs(prevProj.w) + 1.0) * 0.5 * viewport.zw;\\n vec2 currScreen = (currProj.xy / abs(currProj.w) + 1.0) * 0.5 * viewport.zw;\\n vec2 nextScreen = (nextProj.xy / abs(nextProj.w) + 1.0) * 0.5 * viewport.zw;\\n\\n vec2 dir;\\n float len = offset;\\n if (position == positionPrev) {\\n dir = normalize(nextScreen - currScreen);\\n }\\n else if (position == positionNext) {\\n dir = normalize(currScreen - prevScreen);\\n }\\n else {\\n vec2 dirA = normalize(currScreen - prevScreen);\\n vec2 dirB = normalize(nextScreen - currScreen);\\n\\n vec2 tanget = normalize(dirA + dirB);\\n\\n float miter = 1.0 / max(dot(tanget, dirA), 0.5);\\n len *= miter;\\n dir = tanget;\\n }\\n\\n dir = vec2(-dir.y, dir.x) * len;\\n currScreen += dir;\\n\\n currProj.xy = (currScreen / viewport.zw - 0.5) * 2.0 * abs(currProj.w);\\n@end\\n\\n\\n@export ecgl.meshLines3D.vertex\\n\\nattribute vec3 position: POSITION;\\nattribute vec3 positionPrev;\\nattribute vec3 positionNext;\\nattribute float offset;\\nattribute vec4 a_Color : COLOR;\\n\\n#ifdef VERTEX_ANIMATION\\nattribute vec3 prevPosition;\\nattribute vec3 prevPositionPrev;\\nattribute vec3 prevPositionNext;\\nuniform float percent : 1.0;\\n#endif\\n\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\nuniform vec4 viewport : VIEWPORT;\\nuniform float near : NEAR;\\n\\nvarying vec4 v_Color;\\n\\n@import ecgl.common.wireframe.vertexHeader\\n\\n@import ecgl.lines3D.clipNear\\n\\nvoid main()\\n{\\n @import ecgl.lines3D.expandLine\\n\\n gl_Position = currProj;\\n\\n v_Color = a_Color;\\n\\n @import ecgl.common.wireframe.vertexMain\\n}\\n@end\\n\\n\\n@export ecgl.meshLines3D.fragment\\n\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\n\\nvarying vec4 v_Color;\\n\\n@import ecgl.common.wireframe.fragmentHeader\\n\\n@import clay.util.srgb\\n\\nvoid main()\\n{\\n#ifdef SRGB_DECODE\\n gl_FragColor = sRGBToLinear(color * v_Color);\\n#else\\n gl_FragColor = color * v_Color;\\n#endif\\n\\n @import ecgl.common.wireframe.fragmentMain\\n}\\n\\n@end\";var ns=Mn;Ka.Shader.import(is);var as={x:0,y:2,z:1};const os=i.ComponentView.extend({type:\"grid3D\",__ecgl__:!0,init:function(e,t){var r=new Ka.Material({shader:Ka.createShader(\"ecgl.color\"),depthMask:!1,transparent:!0}),i=new Ka.Material({shader:Ka.createShader(\"ecgl.meshLines3D\"),depthMask:!1,transparent:!0});r.define(\"fragment\",\"DOUBLE_SIDED\"),r.define(\"both\",\"VERTEX_COLOR\"),this.groupGL=new Ka.Node,this._control=new Do({zr:t.getZr()}),this._control.init(),this._faces=[[\"y\",\"z\",\"x\",-1,\"left\"],[\"y\",\"z\",\"x\",1,\"right\"],[\"x\",\"y\",\"z\",-1,\"bottom\"],[\"x\",\"y\",\"z\",1,\"top\"],[\"x\",\"z\",\"y\",-1,\"far\"],[\"x\",\"z\",\"y\",1,\"near\"]].map((function(e){var t=new qo(e,i,r);return this.groupGL.add(t.rootNode),t}),this),this._axes=[\"x\",\"y\",\"z\"].map((function(e){var t=new rs(e,i);return this.groupGL.add(t.rootNode),t}),this);var n=t.getDevicePixelRatio();this._axisLabelSurface=new zo({width:256,height:256,devicePixelRatio:n}),this._axisLabelSurface.onupdate=function(){t.getZr().refresh()},this._axisPointerLineMesh=new Ka.Mesh({geometry:new Ro({useNativeLine:!1}),material:i,castShadow:!1,ignorePicking:!0,renderOrder:3}),this.groupGL.add(this._axisPointerLineMesh),this._axisPointerLabelsSurface=new zo({width:128,height:128,devicePixelRatio:n}),this._axisPointerLabelsMesh=new Qo({ignorePicking:!0,renderOrder:4,castShadow:!1}),this._axisPointerLabelsMesh.material.set(\"textureAtlas\",this._axisPointerLabelsSurface.getTexture()),this.groupGL.add(this._axisPointerLabelsMesh),this._lightRoot=new Ka.Node,this._sceneHelper=new Uo,this._sceneHelper.initLight(this._lightRoot)},render:function(e,t,r){this._model=e,this._api=r;var i=e.coordinateSystem;i.viewGL.add(this._lightRoot),e.get(\"show\")?i.viewGL.add(this.groupGL):i.viewGL.remove(this.groupGL);var n=this._control;n.setViewGL(i.viewGL);var a=e.getModel(\"viewControl\");n.setFromViewControlModel(a,0),this._axisLabelSurface.clear(),n.off(\"update\"),e.get(\"show\")&&(this._faces.forEach((function(i){i.update(e,t,r)}),this),this._axes.forEach((function(t){t.update(e,this._axisLabelSurface,r)}),this)),n.on(\"update\",this._onCameraChange.bind(this,e,r),this),this._sceneHelper.setScene(i.viewGL.scene),this._sceneHelper.updateLight(e),i.viewGL.setPostEffect(e.getModel(\"postEffect\"),r),i.viewGL.setTemporalSuperSampling(e.getModel(\"temporalSuperSampling\")),this._initMouseHandler(e)},afterRender:function(e,t,r,i){var n=i.renderer;this._sceneHelper.updateAmbientCubemap(n,e,r),this._sceneHelper.updateSkybox(n,e,r)},showAxisPointer:function(e,t,r,i){this._doShowAxisPointer(),this._updateAxisPointer(i.value)},hideAxisPointer:function(e,t,r,i){this._doHideAxisPointer()},_initMouseHandler:function(e){var t=e.coordinateSystem.viewGL;e.get(\"show\")&&e.get(\"axisPointer.show\")?t.on(\"mousemove\",this._updateAxisPointerOnMousePosition,this):t.off(\"mousemove\",this._updateAxisPointerOnMousePosition)},_updateAxisPointerOnMousePosition:function(e){if(!e.target){for(var t,r=this._model.coordinateSystem,i=r.viewGL,n=i.castRay(e.offsetX,e.offsetY,new Ka.Ray),a=0;ai[1]?0:1,o=this._faces[2*r+a],s=this._faces[2*r+1-a];o.rootNode.invisible=!0,s.rootNode.invisible=!1}},_updateAxisLinePosition:function(){var e=this._model.coordinateSystem,t=e.getAxis(\"x\"),r=e.getAxis(\"y\"),i=e.getAxis(\"z\"),n=i.getExtentMax(),a=i.getExtentMin(),o=t.getExtentMin(),s=t.getExtentMax(),l=r.getExtentMax(),h=r.getExtentMin(),u=this._axes[0].rootNode,c=this._axes[1].rootNode,d=this._axes[2].rootNode,f=this._faces,p=f[4].rootNode.invisible?h:l,m=f[2].rootNode.invisible?n:a,g=f[0].rootNode.invisible?o:s,_=f[2].rootNode.invisible?n:a,v=f[0].rootNode.invisible?s:o,y=f[4].rootNode.invisible?h:l;u.rotation.identity(),c.rotation.identity(),d.rotation.identity(),f[4].rootNode.invisible&&(this._axes[0].flipped=!0,u.rotation.rotateX(Math.PI)),f[0].rootNode.invisible&&(this._axes[1].flipped=!0,c.rotation.rotateZ(Math.PI)),f[4].rootNode.invisible&&(this._axes[2].flipped=!0,d.rotation.rotateY(Math.PI)),u.position.set(0,m,p),c.position.set(g,_,0),d.position.set(v,0,y),u.update(),c.update(),d.update(),this._updateAxisLabelAlign()},_updateAxisLabelAlign:function(){var e=this._control.getCamera(),t=[new Ka.Vector4,new Ka.Vector4],r=new Ka.Vector4;this.groupGL.getWorldPosition(r),r.w=1,r.transformMat4(e.viewMatrix).transformMat4(e.projectionMatrix),r.x/=r.w,r.y/=r.w,this._axes.forEach((function(i){for(var n=i.axisLineCoords,a=(i.labelsMesh.geometry,0);ar.y?\"bottom\":\"top\"):(s=\"middle\",o=u>r.x?\"left\":\"right\"),i.setSpriteAlign(o,s,this._api)}),this)},_doShowAxisPointer:function(){this._axisPointerLineMesh.invisible&&(this._axisPointerLineMesh.invisible=!1,this._axisPointerLabelsMesh.invisible=!1,this._api.getZr().refresh())},_doHideAxisPointer:function(){this._axisPointerLineMesh.invisible||(this._axisPointerLineMesh.invisible=!0,this._axisPointerLabelsMesh.invisible=!0,this._api.getZr().refresh())},_updateAxisPointer:function(e){var t=this._model.coordinateSystem,r=t.dataToPoint(e),i=this._axisPointerLineMesh.geometry,n=this._model.getModel(\"axisPointer\"),a=this._api.getDevicePixelRatio();function o(e){return Mn(e.model.get(\"axisPointer.show\"),n.get(\"show\"))}function s(e){var t=e.model.getModel(\"axisPointer\",n).getModel(\"lineStyle\"),r=Ka.parseColor(t.get(\"color\")),i=ns(t.get(\"width\"),1),a=ns(t.get(\"opacity\"),1);return r[3]*=a,{color:r,lineWidth:i}}i.convertToDynamicArray(!0);for(var l=0;lp&&(p=y,mp&&(p=x,_=r.x&&e<=r.x+r.width&&t>=r.y&&t<=r.y+r.height},e.prototype.clone=function(){return new e(this.x,this.y,this.width,this.height)},e.prototype.copy=function(t){e.copy(this,t)},e.prototype.plain=function(){return{x:this.x,y:this.y,width:this.width,height:this.height}},e.prototype.isFinite=function(){return isFinite(this.x)&&isFinite(this.y)&&isFinite(this.width)&&isFinite(this.height)},e.prototype.isZero=function(){return 0===this.width||0===this.height},e.create=function(t){return new e(t.x,t.y,t.width,t.height)},e.copy=function(e,t){e.x=t.x,e.y=t.y,e.width=t.width,e.height=t.height},e.applyTransform=function(t,r,i){if(i){if(i[1]<1e-5&&i[1]>-1e-5&&i[2]<1e-5&&i[2]>-1e-5){var n=i[0],a=i[3],o=i[4],s=i[5];return t.x=r.x*n+o,t.y=r.y*a+s,t.width=r.width*n,t.height=r.height*a,t.width<0&&(t.x+=t.width,t.width=-t.width),void(t.height<0&&(t.y+=t.height,t.height=-t.height))}gs.x=vs.x=r.x,gs.y=ys.y=r.y,_s.x=ys.x=r.x+r.width,_s.y=vs.y=r.y+r.height,gs.transform(i),ys.transform(i),_s.transform(i),vs.transform(i),t.x=ps(gs.x,_s.x,vs.x,ys.x),t.y=ps(gs.y,_s.y,vs.y,ys.y);var l=ms(gs.x,_s.x,vs.x,ys.x),h=ms(gs.y,_s.y,vs.y,ys.y);t.width=l-t.x,t.height=h-t.y}else t!==r&&e.copy(t,r)},e}();function Ts(e,t,r,i,n){var a=0,o=0;null==i&&(i=1/0),null==n&&(n=1/0);var s=0;t.eachChild((function(l,h){var u,c,d=l.getBoundingRect(),f=t.childAt(h+1),p=f&&f.getBoundingRect();if(\"horizontal\"===e){var m=d.width+(p?-p.x+d.x:0);(u=a+m)>i||l.newline?(a=0,u=m,o+=s+r,s=d.height):s=Math.max(s,d.height)}else{var g=d.height+(p?-p.y+d.y:0);(c=o+g)>n||l.newline?(a+=s+r,o=0,c=g,s=d.width):s=Math.max(s,d.width)}l.newline||(l.x=a,l.y=o,l.markRedraw(),\"horizontal\"===e?a=u+r:o=c+r)}))}function Ss(e,t,r){r=function(e){if(\"number\"==typeof e)return[e,e,e,e];var t=e.length;return 2===t?[e[0],e[1],e[0],e[1]]:3===t?[e[0],e[1],e[2],e[1]]:e}(r||0);var i=t.width,n=t.height,a=go(e.left,i),o=go(e.top,n),s=go(e.right,i),l=go(e.bottom,n),h=go(e.width,i),u=go(e.height,n),c=r[2]+r[0],d=r[1]+r[3],f=e.aspect;switch(isNaN(h)&&(h=i-s-d-a),isNaN(u)&&(u=n-l-c-o),null!=f&&(isNaN(h)&&isNaN(u)&&(f>i/n?h=.8*i:u=.8*n),isNaN(h)&&(h=f*u),isNaN(u)&&(u=h/f)),isNaN(a)&&(a=i-s-h-d),isNaN(o)&&(o=n-l-u-c),e.left||e.right){case\"center\":a=i/2-h/2-r[3];break;case\"right\":a=i-h-d}switch(e.top||e.bottom){case\"middle\":case\"center\":o=n/2-u/2-r[0];break;case\"bottom\":o=n-u-c}a=a||0,o=o||0,isNaN(h)&&(h=i-d-a-(s||0)),isNaN(u)&&(u=n-c-o-(l||0));var p=new ws(a+r[3],o+r[0],h,u);return p.margin=r,p}wa(Ts,\"vertical\"),wa(Ts,\"horizontal\");var Ms=function(){this._pool={},this._allocatedTextures=[]};Ms.prototype={constructor:Ms,get:function(e){var t=Cs(e);this._pool.hasOwnProperty(t)||(this._pool[t]=[]);var r=this._pool[t];if(!r.length){var i=new Dr(e);return this._allocatedTextures.push(i),i}return r.pop()},put:function(e){var t=Cs(e);this._pool.hasOwnProperty(t)||(this._pool[t]=[]),this._pool[t].push(e)},clear:function(e){for(var t=0;t 0.0) {\\n if (texture2D(alphaMap, v_Texcoord).a <= alphaCutoff) {\\n discard;\\n }\\n }\\n#ifdef USE_VSM\\n depth = depth * 0.5 + 0.5;\\n float moment1 = depth;\\n float moment2 = depth * depth;\\n #ifdef SUPPORT_STANDARD_DERIVATIVES\\n float dx = dFdx(depth);\\n float dy = dFdy(depth);\\n moment2 += 0.25*(dx*dx+dy*dy);\\n #endif\\n gl_FragColor = vec4(moment1, moment2, 0.0, 1.0);\\n#else\\n #ifdef SUPPORT_STANDARD_DERIVATIVES\\n float dx = dFdx(depth);\\n float dy = dFdy(depth);\\n depth += sqrt(dx*dx + dy*dy) * slopeScale + bias;\\n #else\\n depth += bias;\\n #endif\\n gl_FragColor = encodeFloat(depth * 0.5 + 0.5);\\n#endif\\n}\\n@end\\n@export clay.sm.debug_depth\\nuniform sampler2D depthMap;\\nvarying vec2 v_Texcoord;\\n@import clay.util.decode_float\\nvoid main() {\\n vec4 tex = texture2D(depthMap, v_Texcoord);\\n#ifdef USE_VSM\\n gl_FragColor = vec4(tex.rgb, 1.0);\\n#else\\n float depth = decodeFloat(tex);\\n gl_FragColor = vec4(depth, depth, depth, 1.0);\\n#endif\\n}\\n@end\\n@export clay.sm.distance.vertex\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\nuniform mat4 world : WORLD;\\nattribute vec3 position : POSITION;\\n@import clay.chunk.skinning_header\\nvarying vec3 v_WorldPosition;\\nvoid main (){\\n vec4 P = vec4(position, 1.0);\\n#ifdef SKINNING\\n @import clay.chunk.skin_matrix\\n P = skinMatrixWS * P;\\n#endif\\n#ifdef INSTANCING\\n @import clay.chunk.instancing_matrix\\n P = instanceMat * P;\\n#endif\\n gl_Position = worldViewProjection * P;\\n v_WorldPosition = (world * P).xyz;\\n}\\n@end\\n@export clay.sm.distance.fragment\\nuniform vec3 lightPosition;\\nuniform float range : 100;\\nvarying vec3 v_WorldPosition;\\n@import clay.util.encode_float\\nvoid main(){\\n float dist = distance(lightPosition, v_WorldPosition);\\n#ifdef USE_VSM\\n gl_FragColor = vec4(dist, dist * dist, 0.0, 0.0);\\n#else\\n dist = dist / range;\\n gl_FragColor = encodeFloat(dist);\\n#endif\\n}\\n@end\\n@export clay.plugin.shadow_map_common\\n@import clay.util.decode_float\\nfloat tapShadowMap(sampler2D map, vec2 uv, float z){\\n vec4 tex = texture2D(map, uv);\\n return step(z, decodeFloat(tex) * 2.0 - 1.0);\\n}\\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize, vec2 scale) {\\n float shadowContrib = tapShadowMap(map, uv, z);\\n vec2 offset = vec2(1.0 / textureSize) * scale;\\n#ifdef PCF_KERNEL_SIZE\\n for (int _idx_ = 0; _idx_ < PCF_KERNEL_SIZE; _idx_++) {{\\n shadowContrib += tapShadowMap(map, uv + offset * pcfKernel[_idx_], z);\\n }}\\n return shadowContrib / float(PCF_KERNEL_SIZE + 1);\\n#else\\n shadowContrib += tapShadowMap(map, uv+vec2(offset.x, 0.0), z);\\n shadowContrib += tapShadowMap(map, uv+vec2(offset.x, offset.y), z);\\n shadowContrib += tapShadowMap(map, uv+vec2(-offset.x, offset.y), z);\\n shadowContrib += tapShadowMap(map, uv+vec2(0.0, offset.y), z);\\n shadowContrib += tapShadowMap(map, uv+vec2(-offset.x, 0.0), z);\\n shadowContrib += tapShadowMap(map, uv+vec2(-offset.x, -offset.y), z);\\n shadowContrib += tapShadowMap(map, uv+vec2(offset.x, -offset.y), z);\\n shadowContrib += tapShadowMap(map, uv+vec2(0.0, -offset.y), z);\\n return shadowContrib / 9.0;\\n#endif\\n}\\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize) {\\n return pcf(map, uv, z, textureSize, vec2(1.0));\\n}\\nfloat chebyshevUpperBound(vec2 moments, float z){\\n float p = 0.0;\\n z = z * 0.5 + 0.5;\\n if (z <= moments.x) {\\n p = 1.0;\\n }\\n float variance = moments.y - moments.x * moments.x;\\n variance = max(variance, 0.0000001);\\n float mD = moments.x - z;\\n float pMax = variance / (variance + mD * mD);\\n pMax = clamp((pMax-0.4)/(1.0-0.4), 0.0, 1.0);\\n return max(p, pMax);\\n}\\nfloat computeShadowContrib(\\n sampler2D map, mat4 lightVPM, vec3 position, float textureSize, vec2 scale, vec2 offset\\n) {\\n vec4 posInLightSpace = lightVPM * vec4(position, 1.0);\\n posInLightSpace.xyz /= posInLightSpace.w;\\n float z = posInLightSpace.z;\\n if(all(greaterThan(posInLightSpace.xyz, vec3(-0.99, -0.99, -1.0))) &&\\n all(lessThan(posInLightSpace.xyz, vec3(0.99, 0.99, 1.0)))){\\n vec2 uv = (posInLightSpace.xy+1.0) / 2.0;\\n #ifdef USE_VSM\\n vec2 moments = texture2D(map, uv * scale + offset).xy;\\n return chebyshevUpperBound(moments, z);\\n #else\\n return pcf(map, uv * scale + offset, z, textureSize, scale);\\n #endif\\n }\\n return 1.0;\\n}\\nfloat computeShadowContrib(sampler2D map, mat4 lightVPM, vec3 position, float textureSize) {\\n return computeShadowContrib(map, lightVPM, position, textureSize, vec2(1.0), vec2(0.0));\\n}\\nfloat computeShadowContribOmni(samplerCube map, vec3 direction, float range)\\n{\\n float dist = length(direction);\\n vec4 shadowTex = textureCube(map, direction);\\n#ifdef USE_VSM\\n vec2 moments = shadowTex.xy;\\n float variance = moments.y - moments.x * moments.x;\\n float mD = moments.x - dist;\\n float p = variance / (variance + mD * mD);\\n if(moments.x + 0.001 < dist){\\n return clamp(p, 0.0, 1.0);\\n }else{\\n return 1.0;\\n }\\n#else\\n return step(dist, (decodeFloat(shadowTex) + 0.0002) * range);\\n#endif\\n}\\n@end\\n@export clay.plugin.compute_shadow_map\\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT) || defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT) || defined(POINT_LIGHT_SHADOWMAP_COUNT)\\n#ifdef SPOT_LIGHT_SHADOWMAP_COUNT\\nuniform sampler2D spotLightShadowMaps[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\\nuniform mat4 spotLightMatrices[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\\nuniform float spotLightShadowMapSizes[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\\n#endif\\n#ifdef DIRECTIONAL_LIGHT_SHADOWMAP_COUNT\\n#if defined(SHADOW_CASCADE)\\nuniform sampler2D directionalLightShadowMaps[1]:unconfigurable;\\nuniform mat4 directionalLightMatrices[SHADOW_CASCADE]:unconfigurable;\\nuniform float directionalLightShadowMapSizes[1]:unconfigurable;\\nuniform float shadowCascadeClipsNear[SHADOW_CASCADE]:unconfigurable;\\nuniform float shadowCascadeClipsFar[SHADOW_CASCADE]:unconfigurable;\\n#else\\nuniform sampler2D directionalLightShadowMaps[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\\nuniform mat4 directionalLightMatrices[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\\nuniform float directionalLightShadowMapSizes[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\\n#endif\\n#endif\\n#ifdef POINT_LIGHT_SHADOWMAP_COUNT\\nuniform samplerCube pointLightShadowMaps[POINT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\\n#endif\\nuniform bool shadowEnabled : true;\\n#ifdef PCF_KERNEL_SIZE\\nuniform vec2 pcfKernel[PCF_KERNEL_SIZE];\\n#endif\\n@import clay.plugin.shadow_map_common\\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\\nvoid computeShadowOfSpotLights(vec3 position, inout float shadowContribs[SPOT_LIGHT_COUNT] ) {\\n float shadowContrib;\\n for(int _idx_ = 0; _idx_ < SPOT_LIGHT_SHADOWMAP_COUNT; _idx_++) {{\\n shadowContrib = computeShadowContrib(\\n spotLightShadowMaps[_idx_], spotLightMatrices[_idx_], position,\\n spotLightShadowMapSizes[_idx_]\\n );\\n shadowContribs[_idx_] = shadowContrib;\\n }}\\n for(int _idx_ = SPOT_LIGHT_SHADOWMAP_COUNT; _idx_ < SPOT_LIGHT_COUNT; _idx_++){{\\n shadowContribs[_idx_] = 1.0;\\n }}\\n}\\n#endif\\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\\n#ifdef SHADOW_CASCADE\\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_COUNT]){\\n float depth = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far)\\n / (gl_DepthRange.far - gl_DepthRange.near);\\n float shadowContrib;\\n shadowContribs[0] = 1.0;\\n for (int _idx_ = 0; _idx_ < SHADOW_CASCADE; _idx_++) {{\\n if (\\n depth >= shadowCascadeClipsNear[_idx_] &&\\n depth <= shadowCascadeClipsFar[_idx_]\\n ) {\\n shadowContrib = computeShadowContrib(\\n directionalLightShadowMaps[0], directionalLightMatrices[_idx_], position,\\n directionalLightShadowMapSizes[0],\\n vec2(1.0 / float(SHADOW_CASCADE), 1.0),\\n vec2(float(_idx_) / float(SHADOW_CASCADE), 0.0)\\n );\\n shadowContribs[0] = shadowContrib;\\n }\\n }}\\n for(int _idx_ = DIRECTIONAL_LIGHT_SHADOWMAP_COUNT; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++) {{\\n shadowContribs[_idx_] = 1.0;\\n }}\\n}\\n#else\\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_COUNT]){\\n float shadowContrib;\\n for(int _idx_ = 0; _idx_ < DIRECTIONAL_LIGHT_SHADOWMAP_COUNT; _idx_++) {{\\n shadowContrib = computeShadowContrib(\\n directionalLightShadowMaps[_idx_], directionalLightMatrices[_idx_], position,\\n directionalLightShadowMapSizes[_idx_]\\n );\\n shadowContribs[_idx_] = shadowContrib;\\n }}\\n for(int _idx_ = DIRECTIONAL_LIGHT_SHADOWMAP_COUNT; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++) {{\\n shadowContribs[_idx_] = 1.0;\\n }}\\n}\\n#endif\\n#endif\\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\\nvoid computeShadowOfPointLights(vec3 position, inout float shadowContribs[POINT_LIGHT_COUNT] ){\\n vec3 lightPosition;\\n vec3 direction;\\n for(int _idx_ = 0; _idx_ < POINT_LIGHT_SHADOWMAP_COUNT; _idx_++) {{\\n lightPosition = pointLightPosition[_idx_];\\n direction = position - lightPosition;\\n shadowContribs[_idx_] = computeShadowContribOmni(pointLightShadowMaps[_idx_], direction, pointLightRange[_idx_]);\\n }}\\n for(int _idx_ = POINT_LIGHT_SHADOWMAP_COUNT; _idx_ < POINT_LIGHT_COUNT; _idx_++) {{\\n shadowContribs[_idx_] = 1.0;\\n }}\\n}\\n#endif\\n#endif\\n@end\");var Ns,Is,Rs,Bs,Fs,zs,Gs,Us=m.extend((function(){return{softShadow:Us.PCF,shadowBlur:1,lightFrustumBias:\"auto\",kernelPCF:new Float32Array([1,0,1,1,-1,1,0,1,-1,0,-1,-1,1,-1,0,-1]),precision:\"highp\",_lastRenderNotCastShadow:!1,_frameBuffer:new zi,_textures:{},_shadowMapNumber:{POINT_LIGHT:0,DIRECTIONAL_LIGHT:0,SPOT_LIGHT:0},_depthMaterials:{},_distanceMaterials:{},_receivers:[],_lightsCastShadow:[],_lightCameras:{},_lightMaterials:{},_texturePool:new Ds}}),(function(){this._gaussianPassH=new pn({fragment:Xe.source(\"clay.compositor.gaussian_blur\")}),this._gaussianPassV=new pn({fragment:Xe.source(\"clay.compositor.gaussian_blur\")}),this._gaussianPassH.setUniform(\"blurSize\",this.shadowBlur),this._gaussianPassH.setUniform(\"blurDir\",0),this._gaussianPassV.setUniform(\"blurSize\",this.shadowBlur),this._gaussianPassV.setUniform(\"blurDir\",1),this._outputDepthPass=new pn({fragment:Xe.source(\"clay.sm.debug_depth\")})}),{render:function(e,t,r,i){r||(r=t.getMainCamera()),this.trigger(\"beforerender\",this,e,t,r),this._renderShadowPass(e,t,r,i),this.trigger(\"afterrender\",this,e,t,r)},renderDebug:function(e,t){e.saveClear();var r=e.viewport,i=0,n=t||r.width/4,a=n;for(var o in this.softShadow===Us.VSM?this._outputDepthPass.material.define(\"fragment\",\"USE_VSM\"):this._outputDepthPass.material.undefine(\"fragment\",\"USE_VSM\"),this._textures){var s=this._textures[o];e.setViewport(i,0,n*s.width/s.height,a),this._outputDepthPass.setUniform(\"depthMap\",s),this._outputDepthPass.render(e),i+=n*s.width/s.height}e.setViewport(r),e.restoreClear()},_updateReceivers:function(e,t){if(t.receiveShadow?(this._receivers.push(t),t.material.set(\"shadowEnabled\",1),t.material.set(\"pcfKernel\",this.kernelPCF)):t.material.set(\"shadowEnabled\",0),this.softShadow===Us.VSM)t.material.define(\"fragment\",\"USE_VSM\"),t.material.undefine(\"fragment\",\"PCF_KERNEL_SIZE\");else{t.material.undefine(\"fragment\",\"USE_VSM\");var r=this.kernelPCF;r&&r.length?t.material.define(\"fragment\",\"PCF_KERNEL_SIZE\",r.length/2):t.material.undefine(\"fragment\",\"PCF_KERNEL_SIZE\")}},_update:function(e,t){var r=this;t.traverse((function(t){t.isRenderable()&&r._updateReceivers(e,t)}));for(var i=0;i4){console.warn(\"Support at most 4 cascade\");continue}p.shadowCascade>1&&(o=p),this.renderDirectionalLightShadow(e,t,r,p,c,u,h)}else\"SPOT_LIGHT\"===p.type?this.renderSpotLightShadow(e,t,p,l,s):\"POINT_LIGHT\"===p.type&&this.renderPointLightShadow(e,t,p,d);this._shadowMapNumber[p.type]++}for(var m in this._shadowMapNumber){var g=this._shadowMapNumber[m],_=m+\"_SHADOWMAP_COUNT\";for(f=0;f0?v.define(\"fragment\",_,g):v.isDefined(\"fragment\",_)&&v.undefine(\"fragment\",_))}for(f=0;f0){var x=h.map(S);if(y.directionalLightShadowMaps={value:h,type:\"tv\"},y.directionalLightMatrices={value:u,type:\"m4v\"},y.directionalLightShadowMapSizes={value:x,type:\"1fv\"},o){var b=c.slice(),w=c.slice();b.pop(),w.shift(),b.reverse(),w.reverse(),u.reverse(),y.shadowCascadeClipsNear={value:b,type:\"1fv\"},y.shadowCascadeClipsFar={value:w,type:\"1fv\"}}}if(s.length>0){var T=s.map(S);(y=t.shadowUniforms).spotLightShadowMaps={value:s,type:\"tv\"},y.spotLightMatrices={value:l,type:\"m4v\"},y.spotLightShadowMapSizes={value:T,type:\"1fv\"}}d.length>0&&(y.pointLightShadowMaps={value:d,type:\"tv\"})}function S(e){return e.height}},renderDirectionalLightShadow:(Ns=new si,Is=new Ht,Rs=new ir,Bs=new Ht,Fs=new Ht,zs=new Ht,Gs=new Ht,function(e,t,r,i,n,a,o){var s=this._getDepthMaterial(i),l={getMaterial:function(e){return e.shadowDepthMaterial||s},isMaterialChanged:Os,getUniform:Ps,ifRender:function(e){return e.castShadow},sortCompare:ut.opaqueSortCompare};if(!t.viewBoundingBoxLastFrame.isFinite()){var h=t.getBoundingBox();t.viewBoundingBoxLastFrame.copy(h).applyTransform(r.viewMatrix)}var u=Math.min(-t.viewBoundingBoxLastFrame.min.z,r.far),c=Math.max(-t.viewBoundingBoxLastFrame.max.z,r.near),d=this._getDirectionalLightCamera(i,t,r),f=zs.array;Gs.copy(d.projectionMatrix),Ye.invert(Fs.array,d.worldTransform.array),Ye.multiply(Fs.array,Fs.array,r.worldTransform.array),Ye.multiply(f,Gs.array,Fs.array);for(var p=[],m=r instanceof Ei,g=(r.near+r.far)/(r.near-r.far),_=2*r.near*r.far/(r.near-r.far),v=0;v<=i.shadowCascade;v++){var y=c*Math.pow(u/c,v/i.shadowCascade),x=c+(u-c)*v/i.shadowCascade,b=y*i.cascadeSplitLogFactor+x*(1-i.cascadeSplitLogFactor);p.push(b),n.push(-(-b*g+_)/-b)}var w=this._getTexture(i,i.shadowCascade);o.push(w);var T=e.viewport,S=e.gl;for(this._frameBuffer.attach(w),this._frameBuffer.bind(e),S.clear(S.COLOR_BUFFER_BIT|S.DEPTH_BUFFER_BIT),v=0;vd?s>f?p[n>0?\"px\":\"nx\"]=!0:p[o>0?\"pz\":\"nz\"]=!0:d>f?p[a>0?\"py\":\"ny\"]=!0:p[o>0?\"pz\":\"nz\"]=!0}for(r=0;r0&&(this.outputs[e].keepLastFrame?(this._prevOutputTextures[e]&&this._compositor.releaseTexture(this._prevOutputTextures[e]),this._prevOutputTextures[e]=this._outputTextures[e]):this._compositor.releaseTexture(this._outputTextures[e]))}}),Hs=m.extend((function(){return{nodes:[]}}),{dirty:function(){this._dirty=!0},addNode:function(e){this.nodes.indexOf(e)>=0||(this.nodes.push(e),this._dirty=!0)},removeNode:function(e){\"string\"==typeof e&&(e=this.getNodeByName(e));var t=this.nodes.indexOf(e);t>=0&&(this.nodes.splice(t,1),this._dirty=!0)},getNodeByName:function(e){for(var t=0;t=r.COLOR_ATTACHMENT0&&u<=r.COLOR_ATTACHMENT0+8&&h.push(u);l.drawBuffersEXT(h)}e.saveClear(),e.clearBit=16640,t=e.render(this.scene,this.camera,!this.autoUpdateScene,this.preZ),e.restoreClear(),i.unbind(e)}else t=e.render(this.scene,this.camera,!this.autoUpdateScene,this.preZ);this.trigger(\"afterrender\",t),this._rendering=!1,this._rendered=!0}}),Xs=Vs.extend((function(){return{texture:null,outputs:{color:{}}}}),(function(){}),{getOutput:function(e,t){return this.texture},beforeFrame:function(){},afterFrame:function(){}}),qs=Vs.extend((function(){return{name:\"\",inputs:{},outputs:null,shader:\"\",inputLinks:{},outputLinks:{},pass:null,_prevOutputTextures:{},_outputTextures:{},_outputReferences:{},_rendering:!1,_rendered:!1,_compositor:null}}),(function(){var e=new pn({fragment:this.shader});this.pass=e}),{render:function(e,t){this.trigger(\"beforerender\",e),this._rendering=!0;var r=e.gl;for(var i in this.inputLinks){var n=(c=this.inputLinks[i]).node.getOutput(e,c.pin);this.pass.setUniform(i,n)}if(this.outputs){this.pass.outputs={};var a={};for(var o in this.outputs){var s=this.updateParameter(o,e);isNaN(s.width)&&this.updateParameter(o,e);var l=this.outputs[o],h=this._compositor.allocateTexture(s);this._outputTextures[o]=h,\"string\"==typeof(u=l.attachment||r.COLOR_ATTACHMENT0)&&(u=r[u]),a[u]=h}for(var u in this._compositor.getFrameBuffer().bind(e),a)this._compositor.getFrameBuffer().attach(a[u],u);this.pass.render(e),this._compositor.getFrameBuffer().updateMipmap(e)}else this.pass.outputs=null,this._compositor.getFrameBuffer().unbind(e),this.pass.render(e,t);for(var i in this.inputLinks){var c;(c=this.inputLinks[i]).node.removeReference(c.pin)}this._rendering=!1,this._rendered=!0,this.trigger(\"afterrender\",e)},updateParameter:function(e,t){var r,i,n=this.outputs[e],a=n.parameters,o=n._parametersCopy;if(o||(o=n._parametersCopy={}),a)for(var s in a)\"width\"!==s&&\"height\"!==s&&(o[s]=a[s]);return r=\"function\"==typeof a.width?a.width.call(this,t):a.width,i=\"function\"==typeof a.height?a.height.call(this,t):a.height,r=Math.ceil(r),i=Math.ceil(i),o.width===r&&o.height===i||this._outputTextures[e]&&this._outputTextures[e].dispose(t),o.width=r,o.height=i,o},setParameter:function(e,t){this.pass.setUniform(e,t)},getParameter:function(e){return this.pass.getUniform(e)},setParameters:function(e){for(var t in e)this.setParameter(t,e[t])},define:function(e,t){this.pass.material.define(\"fragment\",e,t)},undefine:function(e){this.pass.material.undefine(\"fragment\",e)},removeReference:function(e){this._outputReferences[e]--,0===this._outputReferences[e]&&(this.outputs[e].keepLastFrame?(this._prevOutputTextures[e]&&this._compositor.releaseTexture(this._prevOutputTextures[e]),this._prevOutputTextures[e]=this._outputTextures[e]):this._compositor.releaseTexture(this._outputTextures[e]))},clear:function(){Vs.prototype.clear.call(this),this.pass.material.disableTexturesAll()}}),Zs=\"@export clay.compositor.kernel.gaussian_9\\nfloat gaussianKernel[9];\\ngaussianKernel[0] = 0.07;\\ngaussianKernel[1] = 0.09;\\ngaussianKernel[2] = 0.12;\\ngaussianKernel[3] = 0.14;\\ngaussianKernel[4] = 0.16;\\ngaussianKernel[5] = 0.14;\\ngaussianKernel[6] = 0.12;\\ngaussianKernel[7] = 0.09;\\ngaussianKernel[8] = 0.07;\\n@end\\n@export clay.compositor.kernel.gaussian_13\\nfloat gaussianKernel[13];\\ngaussianKernel[0] = 0.02;\\ngaussianKernel[1] = 0.03;\\ngaussianKernel[2] = 0.06;\\ngaussianKernel[3] = 0.08;\\ngaussianKernel[4] = 0.11;\\ngaussianKernel[5] = 0.13;\\ngaussianKernel[6] = 0.14;\\ngaussianKernel[7] = 0.13;\\ngaussianKernel[8] = 0.11;\\ngaussianKernel[9] = 0.08;\\ngaussianKernel[10] = 0.06;\\ngaussianKernel[11] = 0.03;\\ngaussianKernel[12] = 0.02;\\n@end\\n@export clay.compositor.gaussian_blur\\n#define SHADER_NAME gaussian_blur\\nuniform sampler2D texture;varying vec2 v_Texcoord;\\nuniform float blurSize : 2.0;\\nuniform vec2 textureSize : [512.0, 512.0];\\nuniform float blurDir : 0.0;\\n@import clay.util.rgbm\\n@import clay.util.clamp_sample\\nvoid main (void)\\n{\\n @import clay.compositor.kernel.gaussian_9\\n vec2 off = blurSize / textureSize;\\n off *= vec2(1.0 - blurDir, blurDir);\\n vec4 sum = vec4(0.0);\\n float weightAll = 0.0;\\n for (int i = 0; i < 9; i++) {\\n float w = gaussianKernel[i];\\n vec4 texel = decodeHDR(clampSample(texture, v_Texcoord + float(i - 4) * off));\\n sum += texel * w;\\n weightAll += w;\\n }\\n gl_FragColor = encodeHDR(sum / max(weightAll, 0.01));\\n}\\n@end\\n\",Ys=\"\\n@export clay.compositor.lut\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nuniform sampler2D lookup;\\nvoid main()\\n{\\n vec4 tex = texture2D(texture, v_Texcoord);\\n float blueColor = tex.b * 63.0;\\n vec2 quad1;\\n quad1.y = floor(floor(blueColor) / 8.0);\\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\\n vec2 quad2;\\n quad2.y = floor(ceil(blueColor) / 8.0);\\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\\n vec2 texPos1;\\n texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.r);\\n texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.g);\\n vec2 texPos2;\\n texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.r);\\n texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.g);\\n vec4 newColor1 = texture2D(lookup, texPos1);\\n vec4 newColor2 = texture2D(lookup, texPos2);\\n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\\n gl_FragColor = vec4(newColor.rgb, tex.w);\\n}\\n@end\",Ks=\"@export clay.compositor.output\\n#define OUTPUT_ALPHA\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\n@import clay.util.rgbm\\nvoid main()\\n{\\n vec4 tex = decodeHDR(texture2D(texture, v_Texcoord));\\n gl_FragColor.rgb = tex.rgb;\\n#ifdef OUTPUT_ALPHA\\n gl_FragColor.a = tex.a;\\n#else\\n gl_FragColor.a = 1.0;\\n#endif\\n gl_FragColor = encodeHDR(gl_FragColor);\\n#ifdef PREMULTIPLY_ALPHA\\n gl_FragColor.rgb *= gl_FragColor.a;\\n#endif\\n}\\n@end\",Qs=\"@export clay.compositor.bright\\nuniform sampler2D texture;\\nuniform float threshold : 1;\\nuniform float scale : 1.0;\\nuniform vec2 textureSize: [512, 512];\\nvarying vec2 v_Texcoord;\\nconst vec3 lumWeight = vec3(0.2125, 0.7154, 0.0721);\\n@import clay.util.rgbm\\nvec4 median(vec4 a, vec4 b, vec4 c)\\n{\\n return a + b + c - min(min(a, b), c) - max(max(a, b), c);\\n}\\nvoid main()\\n{\\n vec4 texel = decodeHDR(texture2D(texture, v_Texcoord));\\n#ifdef ANTI_FLICKER\\n vec3 d = 1.0 / textureSize.xyx * vec3(1.0, 1.0, 0.0);\\n vec4 s1 = decodeHDR(texture2D(texture, v_Texcoord - d.xz));\\n vec4 s2 = decodeHDR(texture2D(texture, v_Texcoord + d.xz));\\n vec4 s3 = decodeHDR(texture2D(texture, v_Texcoord - d.zy));\\n vec4 s4 = decodeHDR(texture2D(texture, v_Texcoord + d.zy));\\n texel = median(median(texel, s1, s2), s3, s4);\\n#endif\\n float lum = dot(texel.rgb , lumWeight);\\n vec4 color;\\n if (lum > threshold && texel.a > 0.0)\\n {\\n color = vec4(texel.rgb * scale, texel.a * scale);\\n }\\n else\\n {\\n color = vec4(0.0);\\n }\\n gl_FragColor = encodeHDR(color);\\n}\\n@end\\n\",Js=\"@export clay.compositor.downsample\\nuniform sampler2D texture;\\nuniform vec2 textureSize : [512, 512];\\nvarying vec2 v_Texcoord;\\n@import clay.util.rgbm\\nfloat brightness(vec3 c)\\n{\\n return max(max(c.r, c.g), c.b);\\n}\\n@import clay.util.clamp_sample\\nvoid main()\\n{\\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\\n#ifdef ANTI_FLICKER\\n vec3 s1 = decodeHDR(clampSample(texture, v_Texcoord + d.xy)).rgb;\\n vec3 s2 = decodeHDR(clampSample(texture, v_Texcoord + d.zy)).rgb;\\n vec3 s3 = decodeHDR(clampSample(texture, v_Texcoord + d.xw)).rgb;\\n vec3 s4 = decodeHDR(clampSample(texture, v_Texcoord + d.zw)).rgb;\\n float s1w = 1.0 / (brightness(s1) + 1.0);\\n float s2w = 1.0 / (brightness(s2) + 1.0);\\n float s3w = 1.0 / (brightness(s3) + 1.0);\\n float s4w = 1.0 / (brightness(s4) + 1.0);\\n float oneDivideSum = 1.0 / (s1w + s2w + s3w + s4w);\\n vec4 color = vec4(\\n (s1 * s1w + s2 * s2w + s3 * s3w + s4 * s4w) * oneDivideSum,\\n 1.0\\n );\\n#else\\n vec4 color = decodeHDR(clampSample(texture, v_Texcoord + d.xy));\\n color += decodeHDR(clampSample(texture, v_Texcoord + d.zy));\\n color += decodeHDR(clampSample(texture, v_Texcoord + d.xw));\\n color += decodeHDR(clampSample(texture, v_Texcoord + d.zw));\\n color *= 0.25;\\n#endif\\n gl_FragColor = encodeHDR(color);\\n}\\n@end\",$s=\"\\n@export clay.compositor.upsample\\n#define HIGH_QUALITY\\nuniform sampler2D texture;\\nuniform vec2 textureSize : [512, 512];\\nuniform float sampleScale: 0.5;\\nvarying vec2 v_Texcoord;\\n@import clay.util.rgbm\\n@import clay.util.clamp_sample\\nvoid main()\\n{\\n#ifdef HIGH_QUALITY\\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\\n vec4 s;\\n s = decodeHDR(clampSample(texture, v_Texcoord - d.xy));\\n s += decodeHDR(clampSample(texture, v_Texcoord - d.wy)) * 2.0;\\n s += decodeHDR(clampSample(texture, v_Texcoord - d.zy));\\n s += decodeHDR(clampSample(texture, v_Texcoord + d.zw)) * 2.0;\\n s += decodeHDR(clampSample(texture, v_Texcoord )) * 4.0;\\n s += decodeHDR(clampSample(texture, v_Texcoord + d.xw)) * 2.0;\\n s += decodeHDR(clampSample(texture, v_Texcoord + d.zy));\\n s += decodeHDR(clampSample(texture, v_Texcoord + d.wy)) * 2.0;\\n s += decodeHDR(clampSample(texture, v_Texcoord + d.xy));\\n gl_FragColor = encodeHDR(s / 16.0);\\n#else\\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\\n vec4 s;\\n s = decodeHDR(clampSample(texture, v_Texcoord + d.xy));\\n s += decodeHDR(clampSample(texture, v_Texcoord + d.zy));\\n s += decodeHDR(clampSample(texture, v_Texcoord + d.xw));\\n s += decodeHDR(clampSample(texture, v_Texcoord + d.zw));\\n gl_FragColor = encodeHDR(s / 4.0);\\n#endif\\n}\\n@end\",el=\"@export clay.compositor.hdr.composite\\n#define TONEMAPPING\\nuniform sampler2D texture;\\n#ifdef BLOOM_ENABLED\\nuniform sampler2D bloom;\\n#endif\\n#ifdef LENSFLARE_ENABLED\\nuniform sampler2D lensflare;\\nuniform sampler2D lensdirt;\\n#endif\\n#ifdef LUM_ENABLED\\nuniform sampler2D lum;\\n#endif\\n#ifdef LUT_ENABLED\\nuniform sampler2D lut;\\n#endif\\n#ifdef COLOR_CORRECTION\\nuniform float brightness : 0.0;\\nuniform float contrast : 1.0;\\nuniform float saturation : 1.0;\\n#endif\\n#ifdef VIGNETTE\\nuniform float vignetteDarkness: 1.0;\\nuniform float vignetteOffset: 1.0;\\n#endif\\nuniform float exposure : 1.0;\\nuniform float bloomIntensity : 0.25;\\nuniform float lensflareIntensity : 1;\\nvarying vec2 v_Texcoord;\\n@import clay.util.srgb\\nvec3 ACESToneMapping(vec3 color)\\n{\\n const float A = 2.51;\\n const float B = 0.03;\\n const float C = 2.43;\\n const float D = 0.59;\\n const float E = 0.14;\\n return (color * (A * color + B)) / (color * (C * color + D) + E);\\n}\\nfloat eyeAdaption(float fLum)\\n{\\n return mix(0.2, fLum, 0.5);\\n}\\n#ifdef LUT_ENABLED\\nvec3 lutTransform(vec3 color) {\\n float blueColor = color.b * 63.0;\\n vec2 quad1;\\n quad1.y = floor(floor(blueColor) / 8.0);\\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\\n vec2 quad2;\\n quad2.y = floor(ceil(blueColor) / 8.0);\\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\\n vec2 texPos1;\\n texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);\\n texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);\\n vec2 texPos2;\\n texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);\\n texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);\\n vec4 newColor1 = texture2D(lut, texPos1);\\n vec4 newColor2 = texture2D(lut, texPos2);\\n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\\n return newColor.rgb;\\n}\\n#endif\\n@import clay.util.rgbm\\nvoid main()\\n{\\n vec4 texel = vec4(0.0);\\n vec4 originalTexel = vec4(0.0);\\n#ifdef TEXTURE_ENABLED\\n texel = decodeHDR(texture2D(texture, v_Texcoord));\\n originalTexel = texel;\\n#endif\\n#ifdef BLOOM_ENABLED\\n vec4 bloomTexel = decodeHDR(texture2D(bloom, v_Texcoord));\\n texel.rgb += bloomTexel.rgb * bloomIntensity;\\n texel.a += bloomTexel.a * bloomIntensity;\\n#endif\\n#ifdef LENSFLARE_ENABLED\\n texel += decodeHDR(texture2D(lensflare, v_Texcoord)) * texture2D(lensdirt, v_Texcoord) * lensflareIntensity;\\n#endif\\n texel.a = min(texel.a, 1.0);\\n#ifdef LUM_ENABLED\\n float fLum = texture2D(lum, vec2(0.5, 0.5)).r;\\n float adaptedLumDest = 3.0 / (max(0.1, 1.0 + 10.0*eyeAdaption(fLum)));\\n float exposureBias = adaptedLumDest * exposure;\\n#else\\n float exposureBias = exposure;\\n#endif\\n#ifdef TONEMAPPING\\n texel.rgb *= exposureBias;\\n texel.rgb = ACESToneMapping(texel.rgb);\\n#endif\\n texel = linearTosRGB(texel);\\n#ifdef LUT_ENABLED\\n texel.rgb = lutTransform(clamp(texel.rgb,vec3(0.0),vec3(1.0)));\\n#endif\\n#ifdef COLOR_CORRECTION\\n texel.rgb = clamp(texel.rgb + vec3(brightness), 0.0, 1.0);\\n texel.rgb = clamp((texel.rgb - vec3(0.5))*contrast+vec3(0.5), 0.0, 1.0);\\n float lum = dot(texel.rgb, vec3(0.2125, 0.7154, 0.0721));\\n texel.rgb = mix(vec3(lum), texel.rgb, saturation);\\n#endif\\n#ifdef VIGNETTE\\n vec2 uv = (v_Texcoord - vec2(0.5)) * vec2(vignetteOffset);\\n texel.rgb = mix(texel.rgb, vec3(1.0 - vignetteDarkness), dot(uv, uv));\\n#endif\\n gl_FragColor = encodeHDR(texel);\\n#ifdef DEBUG\\n #if DEBUG == 1\\n gl_FragColor = encodeHDR(decodeHDR(texture2D(texture, v_Texcoord)));\\n #elif DEBUG == 2\\n gl_FragColor = encodeHDR(decodeHDR(texture2D(bloom, v_Texcoord)) * bloomIntensity);\\n #elif DEBUG == 3\\n gl_FragColor = encodeHDR(decodeHDR(texture2D(lensflare, v_Texcoord) * lensflareIntensity));\\n #endif\\n#endif\\n if (originalTexel.a <= 0.01 && gl_FragColor.a > 1e-5) {\\n gl_FragColor.a = dot(gl_FragColor.rgb, vec3(0.2125, 0.7154, 0.0721));\\n }\\n#ifdef PREMULTIPLY_ALPHA\\n gl_FragColor.rgb *= gl_FragColor.a;\\n#endif\\n}\\n@end\",tl=\"@export clay.compositor.blend\\n#define SHADER_NAME blend\\n#ifdef TEXTURE1_ENABLED\\nuniform sampler2D texture1;\\nuniform float weight1 : 1.0;\\n#endif\\n#ifdef TEXTURE2_ENABLED\\nuniform sampler2D texture2;\\nuniform float weight2 : 1.0;\\n#endif\\n#ifdef TEXTURE3_ENABLED\\nuniform sampler2D texture3;\\nuniform float weight3 : 1.0;\\n#endif\\n#ifdef TEXTURE4_ENABLED\\nuniform sampler2D texture4;\\nuniform float weight4 : 1.0;\\n#endif\\n#ifdef TEXTURE5_ENABLED\\nuniform sampler2D texture5;\\nuniform float weight5 : 1.0;\\n#endif\\n#ifdef TEXTURE6_ENABLED\\nuniform sampler2D texture6;\\nuniform float weight6 : 1.0;\\n#endif\\nvarying vec2 v_Texcoord;\\n@import clay.util.rgbm\\nvoid main()\\n{\\n vec4 tex = vec4(0.0);\\n#ifdef TEXTURE1_ENABLED\\n tex += decodeHDR(texture2D(texture1, v_Texcoord)) * weight1;\\n#endif\\n#ifdef TEXTURE2_ENABLED\\n tex += decodeHDR(texture2D(texture2, v_Texcoord)) * weight2;\\n#endif\\n#ifdef TEXTURE3_ENABLED\\n tex += decodeHDR(texture2D(texture3, v_Texcoord)) * weight3;\\n#endif\\n#ifdef TEXTURE4_ENABLED\\n tex += decodeHDR(texture2D(texture4, v_Texcoord)) * weight4;\\n#endif\\n#ifdef TEXTURE5_ENABLED\\n tex += decodeHDR(texture2D(texture5, v_Texcoord)) * weight5;\\n#endif\\n#ifdef TEXTURE6_ENABLED\\n tex += decodeHDR(texture2D(texture6, v_Texcoord)) * weight6;\\n#endif\\n gl_FragColor = encodeHDR(tex);\\n}\\n@end\",rl=\"@export clay.compositor.fxaa\\nuniform sampler2D texture;\\nuniform vec4 viewport : VIEWPORT;\\nvarying vec2 v_Texcoord;\\n#define FXAA_REDUCE_MIN (1.0/128.0)\\n#define FXAA_REDUCE_MUL (1.0/8.0)\\n#define FXAA_SPAN_MAX 8.0\\n@import clay.util.rgbm\\nvoid main()\\n{\\n vec2 resolution = 1.0 / viewport.zw;\\n vec3 rgbNW = decodeHDR( texture2D( texture, ( gl_FragCoord.xy + vec2( -1.0, -1.0 ) ) * resolution ) ).xyz;\\n vec3 rgbNE = decodeHDR( texture2D( texture, ( gl_FragCoord.xy + vec2( 1.0, -1.0 ) ) * resolution ) ).xyz;\\n vec3 rgbSW = decodeHDR( texture2D( texture, ( gl_FragCoord.xy + vec2( -1.0, 1.0 ) ) * resolution ) ).xyz;\\n vec3 rgbSE = decodeHDR( texture2D( texture, ( gl_FragCoord.xy + vec2( 1.0, 1.0 ) ) * resolution ) ).xyz;\\n vec4 rgbaM = decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution ) );\\n vec3 rgbM = rgbaM.xyz;\\n float opacity = rgbaM.w;\\n vec3 luma = vec3( 0.299, 0.587, 0.114 );\\n float lumaNW = dot( rgbNW, luma );\\n float lumaNE = dot( rgbNE, luma );\\n float lumaSW = dot( rgbSW, luma );\\n float lumaSE = dot( rgbSE, luma );\\n float lumaM = dot( rgbM, luma );\\n float lumaMin = min( lumaM, min( min( lumaNW, lumaNE ), min( lumaSW, lumaSE ) ) );\\n float lumaMax = max( lumaM, max( max( lumaNW, lumaNE) , max( lumaSW, lumaSE ) ) );\\n vec2 dir;\\n dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\\n dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\\n float dirReduce = max( ( lumaNW + lumaNE + lumaSW + lumaSE ) * ( 0.25 * FXAA_REDUCE_MUL ), FXAA_REDUCE_MIN );\\n float rcpDirMin = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + dirReduce );\\n dir = min( vec2( FXAA_SPAN_MAX, FXAA_SPAN_MAX),\\n max( vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),\\n dir * rcpDirMin)) * resolution;\\n vec3 rgbA = decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution + dir * ( 1.0 / 3.0 - 0.5 ) ) ).xyz;\\n rgbA += decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution + dir * ( 2.0 / 3.0 - 0.5 ) ) ).xyz;\\n rgbA *= 0.5;\\n vec3 rgbB = decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution + dir * -0.5 ) ).xyz;\\n rgbB += decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution + dir * 0.5 ) ).xyz;\\n rgbB *= 0.25;\\n rgbB += rgbA * 0.5;\\n float lumaB = dot( rgbB, luma );\\n if ( ( lumaB < lumaMin ) || ( lumaB > lumaMax ) )\\n {\\n gl_FragColor = vec4( rgbA, opacity );\\n }\\n else {\\n gl_FragColor = vec4( rgbB, opacity );\\n }\\n}\\n@end\";!function(e){e.import(\"@export clay.compositor.coloradjust\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nuniform float brightness : 0.0;\\nuniform float contrast : 1.0;\\nuniform float exposure : 0.0;\\nuniform float gamma : 1.0;\\nuniform float saturation : 1.0;\\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\\nvoid main()\\n{\\n vec4 tex = texture2D( texture, v_Texcoord);\\n vec3 color = clamp(tex.rgb + vec3(brightness), 0.0, 1.0);\\n color = clamp( (color-vec3(0.5))*contrast+vec3(0.5), 0.0, 1.0);\\n color = clamp( color * pow(2.0, exposure), 0.0, 1.0);\\n color = clamp( pow(color, vec3(gamma)), 0.0, 1.0);\\n float luminance = dot( color, w );\\n color = mix(vec3(luminance), color, saturation);\\n gl_FragColor = vec4(color, tex.a);\\n}\\n@end\\n@export clay.compositor.brightness\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nuniform float brightness : 0.0;\\nvoid main()\\n{\\n vec4 tex = texture2D( texture, v_Texcoord);\\n vec3 color = tex.rgb + vec3(brightness);\\n gl_FragColor = vec4(color, tex.a);\\n}\\n@end\\n@export clay.compositor.contrast\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nuniform float contrast : 1.0;\\nvoid main()\\n{\\n vec4 tex = texture2D( texture, v_Texcoord);\\n vec3 color = (tex.rgb-vec3(0.5))*contrast+vec3(0.5);\\n gl_FragColor = vec4(color, tex.a);\\n}\\n@end\\n@export clay.compositor.exposure\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nuniform float exposure : 0.0;\\nvoid main()\\n{\\n vec4 tex = texture2D(texture, v_Texcoord);\\n vec3 color = tex.rgb * pow(2.0, exposure);\\n gl_FragColor = vec4(color, tex.a);\\n}\\n@end\\n@export clay.compositor.gamma\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nuniform float gamma : 1.0;\\nvoid main()\\n{\\n vec4 tex = texture2D(texture, v_Texcoord);\\n vec3 color = pow(tex.rgb, vec3(gamma));\\n gl_FragColor = vec4(color, tex.a);\\n}\\n@end\\n@export clay.compositor.saturation\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nuniform float saturation : 1.0;\\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\\nvoid main()\\n{\\n vec4 tex = texture2D(texture, v_Texcoord);\\n vec3 color = tex.rgb;\\n float luminance = dot(color, w);\\n color = mix(vec3(luminance), color, saturation);\\n gl_FragColor = vec4(color, tex.a);\\n}\\n@end\"),e.import(Zs),e.import(\"@export clay.compositor.hdr.log_lum\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\\n@import clay.util.rgbm\\nvoid main()\\n{\\n vec4 tex = decodeHDR(texture2D(texture, v_Texcoord));\\n float luminance = dot(tex.rgb, w);\\n luminance = log(luminance + 0.001);\\n gl_FragColor = encodeHDR(vec4(vec3(luminance), 1.0));\\n}\\n@end\\n@export clay.compositor.hdr.lum_adaption\\nvarying vec2 v_Texcoord;\\nuniform sampler2D adaptedLum;\\nuniform sampler2D currentLum;\\nuniform float frameTime : 0.02;\\n@import clay.util.rgbm\\nvoid main()\\n{\\n float fAdaptedLum = decodeHDR(texture2D(adaptedLum, vec2(0.5, 0.5))).r;\\n float fCurrentLum = exp(encodeHDR(texture2D(currentLum, vec2(0.5, 0.5))).r);\\n fAdaptedLum += (fCurrentLum - fAdaptedLum) * (1.0 - pow(0.98, 30.0 * frameTime));\\n gl_FragColor = encodeHDR(vec4(vec3(fAdaptedLum), 1.0));\\n}\\n@end\\n@export clay.compositor.lum\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\\nvoid main()\\n{\\n vec4 tex = texture2D( texture, v_Texcoord );\\n float luminance = dot(tex.rgb, w);\\n gl_FragColor = vec4(vec3(luminance), 1.0);\\n}\\n@end\"),e.import(Ys),e.import(\"@export clay.compositor.vignette\\n#define OUTPUT_ALPHA\\nvarying vec2 v_Texcoord;\\nuniform sampler2D texture;\\nuniform float darkness: 1;\\nuniform float offset: 1;\\n@import clay.util.rgbm\\nvoid main()\\n{\\n vec4 texel = decodeHDR(texture2D(texture, v_Texcoord));\\n gl_FragColor.rgb = texel.rgb;\\n vec2 uv = (v_Texcoord - vec2(0.5)) * vec2(offset);\\n gl_FragColor = encodeHDR(vec4(mix(texel.rgb, vec3(1.0 - darkness), dot(uv, uv)), texel.a));\\n}\\n@end\"),e.import(Ks),e.import(Qs),e.import(Js),e.import($s),e.import(el),e.import(\"@export clay.compositor.lensflare\\n#define SAMPLE_NUMBER 8\\nuniform sampler2D texture;\\nuniform sampler2D lenscolor;\\nuniform vec2 textureSize : [512, 512];\\nuniform float dispersal : 0.3;\\nuniform float haloWidth : 0.4;\\nuniform float distortion : 1.0;\\nvarying vec2 v_Texcoord;\\n@import clay.util.rgbm\\nvec4 textureDistorted(\\n in vec2 texcoord,\\n in vec2 direction,\\n in vec3 distortion\\n) {\\n return vec4(\\n decodeHDR(texture2D(texture, texcoord + direction * distortion.r)).r,\\n decodeHDR(texture2D(texture, texcoord + direction * distortion.g)).g,\\n decodeHDR(texture2D(texture, texcoord + direction * distortion.b)).b,\\n 1.0\\n );\\n}\\nvoid main()\\n{\\n vec2 texcoord = -v_Texcoord + vec2(1.0); vec2 textureOffset = 1.0 / textureSize;\\n vec2 ghostVec = (vec2(0.5) - texcoord) * dispersal;\\n vec2 haloVec = normalize(ghostVec) * haloWidth;\\n vec3 distortion = vec3(-textureOffset.x * distortion, 0.0, textureOffset.x * distortion);\\n vec4 result = vec4(0.0);\\n for (int i = 0; i < SAMPLE_NUMBER; i++)\\n {\\n vec2 offset = fract(texcoord + ghostVec * float(i));\\n float weight = length(vec2(0.5) - offset) / length(vec2(0.5));\\n weight = pow(1.0 - weight, 10.0);\\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\\n }\\n result *= texture2D(lenscolor, vec2(length(vec2(0.5) - texcoord)) / length(vec2(0.5)));\\n float weight = length(vec2(0.5) - fract(texcoord + haloVec)) / length(vec2(0.5));\\n weight = pow(1.0 - weight, 10.0);\\n vec2 offset = fract(texcoord + haloVec);\\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\\n gl_FragColor = result;\\n}\\n@end\"),e.import(tl),e.import(rl)}(Xe);var il=/^#source\\((.*?)\\)/;function nl(e,t,r){var i,n,a,o,s=e.type||\"filter\";if(\"filter\"===s){var l=e.shader.trim(),h=il.exec(l);if(h?i=Xe.source(h[1].trim()):\"#\"===l.charAt(0)&&(i=t.shaders[l.substr(1)]),i||(i=l),!i)return}if(e.inputs)for(var u in n={},e.inputs)\"string\"==typeof e.inputs[u]?n[u]=e.inputs[u]:n[u]={node:e.inputs[u].node,pin:e.inputs[u].pin};if(e.outputs)for(var u in a={},e.outputs){var c=e.outputs[u];a[u]={},null!=c.attachment&&(a[u].attachment=c.attachment),null!=c.keepLastFrame&&(a[u].keepLastFrame=c.keepLastFrame),null!=c.outputLastFrame&&(a[u].outputLastFrame=c.outputLastFrame),c.parameters&&(a[u].parameters=sl(c.parameters))}if(o=\"scene\"===s?new js({name:e.name,scene:r.scene,camera:r.camera,outputs:a}):\"texture\"===s?new Xs({name:e.name,outputs:a}):new qs({name:e.name,shader:i,inputs:n,outputs:a})){if(e.parameters)for(var u in e.parameters)\"string\"==typeof(d=e.parameters[u])?\"#\"===(d=d.trim()).charAt(0)?d=t.textures[d.substr(1)]:o.on(\"beforerender\",ll(u,hl(d))):\"function\"==typeof d&&o.on(\"beforerender\",d),o.setParameter(u,d);if(e.defines&&o.pass)for(var u in e.defines){var d=e.defines[u];o.pass.material.define(\"fragment\",u,d)}}return o}function al(e,t){return e}function ol(e,t){return t}function sl(e){var t={};if(!e)return t;[\"type\",\"minFilter\",\"magFilter\",\"wrapS\",\"wrapT\",\"flipY\",\"useMipmap\"].forEach((function(r){var i=e[r];null!=i&&(\"string\"==typeof i&&(i=wr[i]),t[r]=i)}));var r=e.scale||1;return[\"width\",\"height\"].forEach((function(i){if(null!=e[i]){var n=e[i];\"string\"==typeof n?(n=n.trim(),t[i]=(a=hl(n),o=(o=r)||1,function(e){var t=e.getDevicePixelRatio(),r=e.getWidth()*o,i=e.getHeight()*o;return a(r,i,t)})):t[i]=n}var a,o})),t.width||(t.width=al),t.height||(t.height=ol),null!=e.useMipmap&&(t.useMipmap=e.useMipmap),t}function ll(e,t){return function(r){var i=r.getDevicePixelRatio(),n=r.getWidth(),a=r.getHeight(),o=t(n,a,i);this.setParameter(e,o)}}function hl(e){var t=/^expr\\((.*)\\)$/.exec(e);if(t)try{var r=new Function(\"width\",\"height\",\"dpr\",\"return \"+t[1]);return r(1,1),r}catch(e){throw new Error(\"Invalid expression.\")}}const ul=function(e,t){for(var r=0,i=1/t,n=e;n>0;)r+=i*(n%t),n=Math.floor(n/t),i/=t;return r};function cl(e){for(var t=new Uint8Array(e*e*4),r=0,i=new vt,n=0;n 0.99999) {\\n gl_FragColor = vec4(1.0);\\n return;\\n }\\n mat3 kernelBasis;\\n#endif\\n\\n float z = depthTexel.r * 2.0 - 1.0;\\n\\n vec4 projectedPos = vec4(v_Texcoord * 2.0 - 1.0, z, 1.0);\\n vec4 p4 = projectionInv * projectedPos;\\n\\n vec3 position = p4.xyz / p4.w;\\n\\n float ao = ssaoEstimator(position, kernelBasis);\\n ao = clamp(1.0 - (1.0 - ao) * intensity, 0.0, 1.0);\\n gl_FragColor = vec4(vec3(ao), 1.0);\\n}\\n\\n@end\\n\\n\\n@export ecgl.ssao.blur\\n#define SHADER_NAME SSAO_BLUR\\n\\nuniform sampler2D ssaoTexture;\\n\\n#ifdef NORMALTEX_ENABLED\\nuniform sampler2D normalTex;\\n#endif\\n\\nvarying vec2 v_Texcoord;\\n\\nuniform vec2 textureSize;\\nuniform float blurSize : 1.0;\\n\\nuniform int direction: 0.0;\\n\\n#ifdef DEPTHTEX_ENABLED\\nuniform sampler2D depthTex;\\nuniform mat4 projection;\\nuniform float depthRange : 0.5;\\n\\nfloat getLinearDepth(vec2 coord)\\n{\\n float depth = texture2D(depthTex, coord).r * 2.0 - 1.0;\\n return projection[3][2] / (depth * projection[2][3] - projection[2][2]);\\n}\\n#endif\\n\\nvoid main()\\n{\\n float kernel[5];\\n kernel[0] = 0.122581;\\n kernel[1] = 0.233062;\\n kernel[2] = 0.288713;\\n kernel[3] = 0.233062;\\n kernel[4] = 0.122581;\\n\\n vec2 off = vec2(0.0);\\n if (direction == 0) {\\n off[0] = blurSize / textureSize.x;\\n }\\n else {\\n off[1] = blurSize / textureSize.y;\\n }\\n\\n vec2 coord = v_Texcoord;\\n\\n float sum = 0.0;\\n float weightAll = 0.0;\\n\\n#ifdef NORMALTEX_ENABLED\\n vec3 centerNormal = texture2D(normalTex, v_Texcoord).rgb * 2.0 - 1.0;\\n#endif\\n#if defined(DEPTHTEX_ENABLED)\\n float centerDepth = getLinearDepth(v_Texcoord);\\n#endif\\n\\n for (int i = 0; i < 5; i++) {\\n vec2 coord = clamp(v_Texcoord + vec2(float(i) - 2.0) * off, vec2(0.0), vec2(1.0));\\n\\n float w = kernel[i];\\n#ifdef NORMALTEX_ENABLED\\n vec3 normal = texture2D(normalTex, coord).rgb * 2.0 - 1.0;\\n w *= clamp(dot(normal, centerNormal), 0.0, 1.0);\\n#endif\\n#ifdef DEPTHTEX_ENABLED\\n float d = getLinearDepth(coord);\\n w *= (1.0 - smoothstep(abs(centerDepth - d) / depthRange, 0.0, 1.0));\\n#endif\\n\\n weightAll += w;\\n sum += texture2D(ssaoTexture, coord).r * w;\\n }\\n\\n gl_FragColor = vec4(vec3(sum / weightAll), 1.0);\\n}\\n\\n@end\\n\"),pl.prototype.setDepthTexture=function(e){this._depthTex=e},pl.prototype.setNormalTexture=function(e){this._normalTex=e,this._ssaoPass.material[e?\"enableTexture\":\"disableTexture\"](\"normalTex\"),this.setKernelSize(this._kernelSize)},pl.prototype.update=function(e,t,r){var i=e.getWidth(),n=e.getHeight(),a=this._ssaoPass,o=this._blurPass;a.setUniform(\"kernel\",this._kernels[r%this._kernels.length]),a.setUniform(\"depthTex\",this._depthTex),null!=this._normalTex&&a.setUniform(\"normalTex\",this._normalTex),a.setUniform(\"depthTexSize\",[this._depthTex.width,this._depthTex.height]);var s=new Ht;Ht.transpose(s,t.worldTransform),a.setUniform(\"projection\",t.projectionMatrix.array),a.setUniform(\"projectionInv\",t.invProjectionMatrix.array),a.setUniform(\"viewInverseTranspose\",s.array);var l=this._ssaoTexture,h=this._blurTexture,u=this._blurTexture2;l.width=i/2,l.height=n/2,h.width=i,h.height=n,u.width=i,u.height=n,this._framebuffer.attach(l),this._framebuffer.bind(e),e.gl.clearColor(1,1,1,1),e.gl.clear(e.gl.COLOR_BUFFER_BIT),a.render(e),o.setUniform(\"textureSize\",[i/2,n/2]),o.setUniform(\"projection\",t.projectionMatrix.array),this._framebuffer.attach(h),o.setUniform(\"direction\",0),o.setUniform(\"ssaoTexture\",l),o.render(e),this._framebuffer.attach(u),o.setUniform(\"textureSize\",[i,n]),o.setUniform(\"direction\",1),o.setUniform(\"ssaoTexture\",h),o.render(e),this._framebuffer.unbind(e);var c=e.clearColor;e.gl.clearColor(c[0],c[1],c[2],c[3])},pl.prototype.getTargetTexture=function(){return this._blurTexture2},pl.prototype.setParameter=function(e,t){\"noiseTexSize\"===e?this.setNoiseSize(t):\"kernelSize\"===e?this.setKernelSize(t):\"intensity\"===e?this._ssaoPass.material.set(\"intensity\",t):this._ssaoPass.setUniform(e,t)},pl.prototype.setKernelSize=function(e){this._kernelSize=e,this._ssaoPass.material.define(\"fragment\",\"KERNEL_SIZE\",e),this._kernels=this._kernels||[];for(var t=0;t<30;t++)this._kernels[t]=fl(e,t*e,!!this._normalTex)},pl.prototype.setNoiseSize=function(e){var t=this._ssaoPass.getUniform(\"noiseTex\");t?(t.data=cl(e),t.width=t.height=e,t.dirty()):(t=dl(e),this._ssaoPass.setUniform(\"noiseTex\",dl(e))),this._ssaoPass.setUniform(\"noiseTexSize\",[e,e])},pl.prototype.dispose=function(e){this._blurTexture.dispose(e),this._ssaoTexture.dispose(e),this._blurTexture2.dispose(e)};const ml=pl;function gl(e){e=e||{},this._ssrPass=new pn({fragment:Xe.source(\"ecgl.ssr.main\"),clearColor:[0,0,0,0]}),this._blurPass1=new pn({fragment:Xe.source(\"ecgl.ssr.blur\"),clearColor:[0,0,0,0]}),this._blurPass2=new pn({fragment:Xe.source(\"ecgl.ssr.blur\"),clearColor:[0,0,0,0]}),this._blendPass=new pn({fragment:Xe.source(\"clay.compositor.blend\")}),this._blendPass.material.disableTexturesAll(),this._blendPass.material.enableTexture([\"texture1\",\"texture2\"]),this._ssrPass.setUniform(\"gBufferTexture1\",e.normalTexture),this._ssrPass.setUniform(\"gBufferTexture2\",e.depthTexture),this._blurPass1.setUniform(\"gBufferTexture1\",e.normalTexture),this._blurPass1.setUniform(\"gBufferTexture2\",e.depthTexture),this._blurPass2.setUniform(\"gBufferTexture1\",e.normalTexture),this._blurPass2.setUniform(\"gBufferTexture2\",e.depthTexture),this._blurPass2.material.define(\"fragment\",\"VERTICAL\"),this._blurPass2.material.define(\"fragment\",\"BLEND\"),this._ssrTexture=new Dr({type:wr.HALF_FLOAT}),this._texture2=new Dr({type:wr.HALF_FLOAT}),this._texture3=new Dr({type:wr.HALF_FLOAT}),this._prevTexture=new Dr({type:wr.HALF_FLOAT}),this._currentTexture=new Dr({type:wr.HALF_FLOAT}),this._frameBuffer=new zi({depthBuffer:!1}),this._normalDistribution=null,this._totalSamples=256,this._samplePerFrame=4,this._ssrPass.material.define(\"fragment\",\"SAMPLE_PER_FRAME\",this._samplePerFrame),this._ssrPass.material.define(\"fragment\",\"TOTAL_SAMPLES\",this._totalSamples),this._downScale=1}Xe.import(\"@export ecgl.ssr.main\\n\\n#define SHADER_NAME SSR\\n#define MAX_ITERATION 20;\\n#define SAMPLE_PER_FRAME 5;\\n#define TOTAL_SAMPLES 128;\\n\\nuniform sampler2D sourceTexture;\\nuniform sampler2D gBufferTexture1;\\nuniform sampler2D gBufferTexture2;\\nuniform sampler2D gBufferTexture3;\\nuniform samplerCube specularCubemap;\\nuniform float specularIntensity: 1;\\n\\nuniform mat4 projection;\\nuniform mat4 projectionInv;\\nuniform mat4 toViewSpace;\\nuniform mat4 toWorldSpace;\\n\\nuniform float maxRayDistance: 200;\\n\\nuniform float pixelStride: 16;\\nuniform float pixelStrideZCutoff: 50; \\nuniform float screenEdgeFadeStart: 0.9; \\nuniform float eyeFadeStart : 0.2; uniform float eyeFadeEnd: 0.8; \\nuniform float minGlossiness: 0.2; uniform float zThicknessThreshold: 1;\\n\\nuniform float nearZ;\\nuniform vec2 viewportSize : VIEWPORT_SIZE;\\n\\nuniform float jitterOffset: 0;\\n\\nvarying vec2 v_Texcoord;\\n\\n#ifdef DEPTH_DECODE\\n@import clay.util.decode_float\\n#endif\\n\\n#ifdef PHYSICALLY_CORRECT\\nuniform sampler2D normalDistribution;\\nuniform float sampleOffset: 0;\\nuniform vec2 normalDistributionSize;\\n\\nvec3 transformNormal(vec3 H, vec3 N) {\\n vec3 upVector = N.y > 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\\n vec3 tangentX = normalize(cross(N, upVector));\\n vec3 tangentZ = cross(N, tangentX);\\n return normalize(tangentX * H.x + N * H.y + tangentZ * H.z);\\n}\\nvec3 importanceSampleNormalGGX(float i, float roughness, vec3 N) {\\n float p = fract((i + sampleOffset) / float(TOTAL_SAMPLES));\\n vec3 H = texture2D(normalDistribution,vec2(roughness, p)).rgb;\\n return transformNormal(H, N);\\n}\\nfloat G_Smith(float g, float ndv, float ndl) {\\n float roughness = 1.0 - g;\\n float k = roughness * roughness / 2.0;\\n float G1V = ndv / (ndv * (1.0 - k) + k);\\n float G1L = ndl / (ndl * (1.0 - k) + k);\\n return G1L * G1V;\\n}\\nvec3 F_Schlick(float ndv, vec3 spec) {\\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\\n}\\n#endif\\n\\nfloat fetchDepth(sampler2D depthTexture, vec2 uv)\\n{\\n vec4 depthTexel = texture2D(depthTexture, uv);\\n return depthTexel.r * 2.0 - 1.0;\\n}\\n\\nfloat linearDepth(float depth)\\n{\\n if (projection[3][3] == 0.0) {\\n return projection[3][2] / (depth * projection[2][3] - projection[2][2]);\\n }\\n else {\\n return (depth - projection[3][2]) / projection[2][2];\\n }\\n}\\n\\nbool rayIntersectDepth(float rayZNear, float rayZFar, vec2 hitPixel)\\n{\\n if (rayZFar > rayZNear)\\n {\\n float t = rayZFar; rayZFar = rayZNear; rayZNear = t;\\n }\\n float cameraZ = linearDepth(fetchDepth(gBufferTexture2, hitPixel));\\n return rayZFar <= cameraZ && rayZNear >= cameraZ - zThicknessThreshold;\\n}\\n\\n\\nbool traceScreenSpaceRay(\\n vec3 rayOrigin, vec3 rayDir, float jitter,\\n out vec2 hitPixel, out vec3 hitPoint, out float iterationCount\\n)\\n{\\n float rayLength = ((rayOrigin.z + rayDir.z * maxRayDistance) > -nearZ)\\n ? (-nearZ - rayOrigin.z) / rayDir.z : maxRayDistance;\\n\\n vec3 rayEnd = rayOrigin + rayDir * rayLength;\\n\\n vec4 H0 = projection * vec4(rayOrigin, 1.0);\\n vec4 H1 = projection * vec4(rayEnd, 1.0);\\n\\n float k0 = 1.0 / H0.w, k1 = 1.0 / H1.w;\\n\\n vec3 Q0 = rayOrigin * k0, Q1 = rayEnd * k1;\\n\\n vec2 P0 = (H0.xy * k0 * 0.5 + 0.5) * viewportSize;\\n vec2 P1 = (H1.xy * k1 * 0.5 + 0.5) * viewportSize;\\n\\n P1 += dot(P1 - P0, P1 - P0) < 0.0001 ? 0.01 : 0.0;\\n vec2 delta = P1 - P0;\\n\\n bool permute = false;\\n if (abs(delta.x) < abs(delta.y)) {\\n permute = true;\\n delta = delta.yx;\\n P0 = P0.yx;\\n P1 = P1.yx;\\n }\\n float stepDir = sign(delta.x);\\n float invdx = stepDir / delta.x;\\n\\n vec3 dQ = (Q1 - Q0) * invdx;\\n float dk = (k1 - k0) * invdx;\\n\\n vec2 dP = vec2(stepDir, delta.y * invdx);\\n\\n float strideScaler = 1.0 - min(1.0, -rayOrigin.z / pixelStrideZCutoff);\\n float pixStride = 1.0 + strideScaler * pixelStride;\\n\\n dP *= pixStride; dQ *= pixStride; dk *= pixStride;\\n\\n vec4 pqk = vec4(P0, Q0.z, k0);\\n vec4 dPQK = vec4(dP, dQ.z, dk);\\n\\n pqk += dPQK * jitter;\\n float rayZFar = (dPQK.z * 0.5 + pqk.z) / (dPQK.w * 0.5 + pqk.w);\\n float rayZNear;\\n\\n bool intersect = false;\\n\\n vec2 texelSize = 1.0 / viewportSize;\\n\\n iterationCount = 0.0;\\n\\n for (int i = 0; i < MAX_ITERATION; i++)\\n {\\n pqk += dPQK;\\n\\n rayZNear = rayZFar;\\n rayZFar = (dPQK.z * 0.5 + pqk.z) / (dPQK.w * 0.5 + pqk.w);\\n\\n hitPixel = permute ? pqk.yx : pqk.xy;\\n hitPixel *= texelSize;\\n\\n intersect = rayIntersectDepth(rayZNear, rayZFar, hitPixel);\\n\\n iterationCount += 1.0;\\n\\n dPQK *= 1.2;\\n\\n if (intersect) {\\n break;\\n }\\n }\\n\\n Q0.xy += dQ.xy * iterationCount;\\n Q0.z = pqk.z;\\n hitPoint = Q0 / pqk.w;\\n\\n return intersect;\\n}\\n\\nfloat calculateAlpha(\\n float iterationCount, float reflectivity,\\n vec2 hitPixel, vec3 hitPoint, float dist, vec3 rayDir\\n)\\n{\\n float alpha = clamp(reflectivity, 0.0, 1.0);\\n alpha *= 1.0 - (iterationCount / float(MAX_ITERATION));\\n vec2 hitPixelNDC = hitPixel * 2.0 - 1.0;\\n float maxDimension = min(1.0, max(abs(hitPixelNDC.x), abs(hitPixelNDC.y)));\\n alpha *= 1.0 - max(0.0, maxDimension - screenEdgeFadeStart) / (1.0 - screenEdgeFadeStart);\\n\\n float _eyeFadeStart = eyeFadeStart;\\n float _eyeFadeEnd = eyeFadeEnd;\\n if (_eyeFadeStart > _eyeFadeEnd) {\\n float tmp = _eyeFadeEnd;\\n _eyeFadeEnd = _eyeFadeStart;\\n _eyeFadeStart = tmp;\\n }\\n\\n float eyeDir = clamp(rayDir.z, _eyeFadeStart, _eyeFadeEnd);\\n alpha *= 1.0 - (eyeDir - _eyeFadeStart) / (_eyeFadeEnd - _eyeFadeStart);\\n\\n alpha *= 1.0 - clamp(dist / maxRayDistance, 0.0, 1.0);\\n\\n return alpha;\\n}\\n\\n@import clay.util.rand\\n\\n@import clay.util.rgbm\\n\\nvoid main()\\n{\\n vec4 normalAndGloss = texture2D(gBufferTexture1, v_Texcoord);\\n\\n if (dot(normalAndGloss.rgb, vec3(1.0)) == 0.0) {\\n discard;\\n }\\n\\n float g = normalAndGloss.a;\\n#if !defined(PHYSICALLY_CORRECT)\\n if (g <= minGlossiness) {\\n discard;\\n }\\n#endif\\n\\n float reflectivity = (g - minGlossiness) / (1.0 - minGlossiness);\\n\\n vec3 N = normalize(normalAndGloss.rgb * 2.0 - 1.0);\\n N = normalize((toViewSpace * vec4(N, 0.0)).xyz);\\n\\n vec4 projectedPos = vec4(v_Texcoord * 2.0 - 1.0, fetchDepth(gBufferTexture2, v_Texcoord), 1.0);\\n vec4 pos = projectionInv * projectedPos;\\n vec3 rayOrigin = pos.xyz / pos.w;\\n vec3 V = -normalize(rayOrigin);\\n\\n float ndv = clamp(dot(N, V), 0.0, 1.0);\\n float iterationCount;\\n float jitter = rand(fract(v_Texcoord + jitterOffset));\\n\\n#ifdef PHYSICALLY_CORRECT\\n vec4 color = vec4(vec3(0.0), 1.0);\\n vec4 albedoMetalness = texture2D(gBufferTexture3, v_Texcoord);\\n vec3 albedo = albedoMetalness.rgb;\\n float m = albedoMetalness.a;\\n vec3 diffuseColor = albedo * (1.0 - m);\\n vec3 spec = mix(vec3(0.04), albedo, m);\\n\\n float jitter2 = rand(fract(v_Texcoord)) * float(TOTAL_SAMPLES);\\n\\n for (int i = 0; i < SAMPLE_PER_FRAME; i++) {\\n vec3 H = importanceSampleNormalGGX(float(i) + jitter2, 1.0 - g, N);\\n vec3 rayDir = normalize(reflect(-V, H));\\n#else\\n vec3 rayDir = normalize(reflect(-V, N));\\n#endif\\n vec2 hitPixel;\\n vec3 hitPoint;\\n\\n bool intersect = traceScreenSpaceRay(rayOrigin, rayDir, jitter, hitPixel, hitPoint, iterationCount);\\n\\n float dist = distance(rayOrigin, hitPoint);\\n\\n vec3 hitNormal = texture2D(gBufferTexture1, hitPixel).rgb * 2.0 - 1.0;\\n hitNormal = normalize((toViewSpace * vec4(hitNormal, 0.0)).xyz);\\n#ifdef PHYSICALLY_CORRECT\\n float ndl = clamp(dot(N, rayDir), 0.0, 1.0);\\n float vdh = clamp(dot(V, H), 0.0, 1.0);\\n float ndh = clamp(dot(N, H), 0.0, 1.0);\\n vec3 litTexel = vec3(0.0);\\n if (dot(hitNormal, rayDir) < 0.0 && intersect) {\\n litTexel = texture2D(sourceTexture, hitPixel).rgb;\\n litTexel *= pow(clamp(1.0 - dist / 200.0, 0.0, 1.0), 3.0);\\n\\n }\\n else {\\n #ifdef SPECULARCUBEMAP_ENABLED\\n vec3 rayDirW = normalize(toWorldSpace * vec4(rayDir, 0.0)).rgb;\\n litTexel = RGBMDecode(textureCubeLodEXT(specularCubemap, rayDirW, 0.0), 8.12).rgb * specularIntensity;\\n#endif\\n }\\n color.rgb += ndl * litTexel * (\\n F_Schlick(ndl, spec) * G_Smith(g, ndv, ndl) * vdh / (ndh * ndv + 0.001)\\n );\\n }\\n color.rgb /= float(SAMPLE_PER_FRAME);\\n#else\\n #if !defined(SPECULARCUBEMAP_ENABLED)\\n if (dot(hitNormal, rayDir) >= 0.0) {\\n discard;\\n }\\n if (!intersect) {\\n discard;\\n }\\n#endif\\n float alpha = clamp(calculateAlpha(iterationCount, reflectivity, hitPixel, hitPoint, dist, rayDir), 0.0, 1.0);\\n vec4 color = texture2D(sourceTexture, hitPixel);\\n color.rgb *= alpha;\\n\\n#ifdef SPECULARCUBEMAP_ENABLED\\n vec3 rayDirW = normalize(toWorldSpace * vec4(rayDir, 0.0)).rgb;\\n alpha = alpha * (intersect ? 1.0 : 0.0);\\n float bias = (1.0 -g) * 5.0;\\n color.rgb += (1.0 - alpha)\\n * RGBMDecode(textureCubeLodEXT(specularCubemap, rayDirW, bias), 8.12).rgb\\n * specularIntensity;\\n#endif\\n\\n#endif\\n\\n gl_FragColor = encodeHDR(color);\\n}\\n@end\\n\\n@export ecgl.ssr.blur\\n\\nuniform sampler2D texture;\\nuniform sampler2D gBufferTexture1;\\nuniform sampler2D gBufferTexture2;\\nuniform mat4 projection;\\nuniform float depthRange : 0.05;\\n\\nvarying vec2 v_Texcoord;\\n\\nuniform vec2 textureSize;\\nuniform float blurSize : 1.0;\\n\\n#ifdef BLEND\\n #ifdef SSAOTEX_ENABLED\\nuniform sampler2D ssaoTex;\\n #endif\\nuniform sampler2D sourceTexture;\\n#endif\\n\\nfloat getLinearDepth(vec2 coord)\\n{\\n float depth = texture2D(gBufferTexture2, coord).r * 2.0 - 1.0;\\n return projection[3][2] / (depth * projection[2][3] - projection[2][2]);\\n}\\n\\n@import clay.util.rgbm\\n\\n\\nvoid main()\\n{\\n @import clay.compositor.kernel.gaussian_9\\n\\n vec4 centerNTexel = texture2D(gBufferTexture1, v_Texcoord);\\n float g = centerNTexel.a;\\n float maxBlurSize = clamp(1.0 - g, 0.0, 1.0) * blurSize;\\n#ifdef VERTICAL\\n vec2 off = vec2(0.0, maxBlurSize / textureSize.y);\\n#else\\n vec2 off = vec2(maxBlurSize / textureSize.x, 0.0);\\n#endif\\n\\n vec2 coord = v_Texcoord;\\n\\n vec4 sum = vec4(0.0);\\n float weightAll = 0.0;\\n\\n vec3 cN = centerNTexel.rgb * 2.0 - 1.0;\\n float cD = getLinearDepth(v_Texcoord);\\n for (int i = 0; i < 9; i++) {\\n vec2 coord = clamp((float(i) - 4.0) * off + v_Texcoord, vec2(0.0), vec2(1.0));\\n float w = gaussianKernel[i]\\n * clamp(dot(cN, texture2D(gBufferTexture1, coord).rgb * 2.0 - 1.0), 0.0, 1.0);\\n float d = getLinearDepth(coord);\\n w *= (1.0 - smoothstep(abs(cD - d) / depthRange, 0.0, 1.0));\\n\\n weightAll += w;\\n sum += decodeHDR(texture2D(texture, coord)) * w;\\n }\\n\\n#ifdef BLEND\\n float aoFactor = 1.0;\\n #ifdef SSAOTEX_ENABLED\\n aoFactor = texture2D(ssaoTex, v_Texcoord).r;\\n #endif\\n gl_FragColor = encodeHDR(\\n sum / weightAll * aoFactor + decodeHDR(texture2D(sourceTexture, v_Texcoord))\\n );\\n#else\\n gl_FragColor = encodeHDR(sum / weightAll);\\n#endif\\n}\\n\\n@end\"),gl.prototype.setAmbientCubemap=function(e,t){this._ssrPass.material.set(\"specularCubemap\",e),this._ssrPass.material.set(\"specularIntensity\",t);var r=e&&t;this._ssrPass.material[r?\"enableTexture\":\"disableTexture\"](\"specularCubemap\")},gl.prototype.update=function(e,t,r,i){var n=e.getWidth(),a=e.getHeight(),o=this._ssrTexture,s=this._texture2,l=this._texture3;o.width=this._prevTexture.width=this._currentTexture.width=n/this._downScale,o.height=this._prevTexture.height=this._currentTexture.height=a/this._downScale,s.width=l.width=n,s.height=l.height=a;var h=this._frameBuffer,u=this._ssrPass,c=this._blurPass1,d=this._blurPass2,f=this._blendPass,p=new Ht,m=new Ht;Ht.transpose(p,t.worldTransform),Ht.transpose(m,t.viewMatrix),u.setUniform(\"sourceTexture\",r),u.setUniform(\"projection\",t.projectionMatrix.array),u.setUniform(\"projectionInv\",t.invProjectionMatrix.array),u.setUniform(\"toViewSpace\",p.array),u.setUniform(\"toWorldSpace\",m.array),u.setUniform(\"nearZ\",t.near);var g=i/this._totalSamples*this._samplePerFrame;if(u.setUniform(\"jitterOffset\",g),u.setUniform(\"sampleOffset\",i*this._samplePerFrame),c.setUniform(\"textureSize\",[o.width,o.height]),d.setUniform(\"textureSize\",[n,a]),d.setUniform(\"sourceTexture\",r),c.setUniform(\"projection\",t.projectionMatrix.array),d.setUniform(\"projection\",t.projectionMatrix.array),h.attach(o),h.bind(e),u.render(e),this._physicallyCorrect&&(h.attach(this._currentTexture),f.setUniform(\"texture1\",this._prevTexture),f.setUniform(\"texture2\",o),f.material.set({weight1:i>=1?.95:0,weight2:i>=1?.05:1}),f.render(e)),h.attach(s),c.setUniform(\"texture\",this._physicallyCorrect?this._currentTexture:o),c.render(e),h.attach(l),d.setUniform(\"texture\",s),d.render(e),h.unbind(e),this._physicallyCorrect){var _=this._prevTexture;this._prevTexture=this._currentTexture,this._currentTexture=_}},gl.prototype.getTargetTexture=function(){return this._texture3},gl.prototype.setParameter=function(e,t){\"maxIteration\"===e?this._ssrPass.material.define(\"fragment\",\"MAX_ITERATION\",t):this._ssrPass.setUniform(e,t)},gl.prototype.setPhysicallyCorrect=function(e){e?(this._normalDistribution||(this._normalDistribution=_n.generateNormalDistribution(64,this._totalSamples)),this._ssrPass.material.define(\"fragment\",\"PHYSICALLY_CORRECT\"),this._ssrPass.material.set(\"normalDistribution\",this._normalDistribution),this._ssrPass.material.set(\"normalDistributionSize\",[64,this._totalSamples])):this._ssrPass.material.undefine(\"fragment\",\"PHYSICALLY_CORRECT\"),this._physicallyCorrect=e},gl.prototype.setSSAOTexture=function(e){var t=this._blurPass2;e?(t.material.enableTexture(\"ssaoTex\"),t.material.set(\"ssaoTex\",e)):t.material.disableTexture(\"ssaoTex\")},gl.prototype.isFinished=function(e){return!this._physicallyCorrect||e>this._totalSamples/this._samplePerFrame},gl.prototype.dispose=function(e){this._ssrTexture.dispose(e),this._texture2.dispose(e),this._texture3.dispose(e),this._prevTexture.dispose(e),this._currentTexture.dispose(e),this._frameBuffer.dispose(e)};const _l=gl,vl=[0,0,-.321585265978,-.154972575841,.458126042375,.188473391593,.842080129861,.527766490688,.147304551086,-.659453822776,-.331943915203,-.940619700594,.0479226680259,.54812163202,.701581552186,-.709825561388,-.295436780218,.940589268233,-.901489676764,.237713156085,.973570876096,-.109899459384,-.866792314779,-.451805525005,.330975007087,.800048655954,-.344275183665,.381779221166,-.386139432542,-.437418421534,-.576478634965,-.0148463392551,.385798197415,-.262426961053,-.666302061145,.682427250835,-.628010632582,-.732836215494,.10163141741,-.987658134403,.711995289051,-.320024291314,.0296005138058,.950296523438,.0130612307608,-.351024443122,-.879596633704,-.10478487883,.435712737232,.504254490347,.779203817497,.206477676721,.388264289969,-.896736162545,-.153106280781,-.629203242522,-.245517550697,.657969239148,.126830499058,.26862328493,-.634888119007,-.302301223431,.617074219636,.779817204925];function yl(e,t,r,i,n){var a=e.gl;t.setUniform(a,\"1i\",r,n),a.activeTexture(a.TEXTURE0+n),i.isRenderable()?i.bind(e):i.unbind(e)}function xl(e,t,r,i,n){var a,o,s,l,h=e.gl;return function(n,u,c){if(!l||l.material!==n.material){var d=n.material,f=n.__program,p=d.get(\"roughness\");null==p&&(p=1);var m=d.get(\"normalMap\")||t,g=d.get(\"roughnessMap\"),_=d.get(\"bumpMap\"),v=d.get(\"uvRepeat\"),y=d.get(\"uvOffset\"),x=d.get(\"detailUvRepeat\"),b=d.get(\"detailUvOffset\"),w=!!_&&d.isTextureEnabled(\"bumpMap\"),T=!!g&&d.isTextureEnabled(\"roughnessMap\"),S=d.isDefined(\"fragment\",\"DOUBLE_SIDED\");_=_||r,g=g||i,c!==u?(u.set(\"normalMap\",m),u.set(\"bumpMap\",_),u.set(\"roughnessMap\",g),u.set(\"useBumpMap\",w),u.set(\"useRoughnessMap\",T),u.set(\"doubleSide\",S),null!=v&&u.set(\"uvRepeat\",v),null!=y&&u.set(\"uvOffset\",y),null!=x&&u.set(\"detailUvRepeat\",x),null!=b&&u.set(\"detailUvOffset\",b),u.set(\"roughness\",p)):(f.setUniform(h,\"1f\",\"roughness\",p),a!==m&&yl(e,f,\"normalMap\",m,0),o!==_&&_&&yl(e,f,\"bumpMap\",_,1),s!==g&&g&&yl(e,f,\"roughnessMap\",g,2),null!=v&&f.setUniform(h,\"2f\",\"uvRepeat\",v),null!=y&&f.setUniform(h,\"2f\",\"uvOffset\",y),null!=x&&f.setUniform(h,\"2f\",\"detailUvRepeat\",x),null!=b&&f.setUniform(h,\"2f\",\"detailUvOffset\",b),f.setUniform(h,\"1i\",\"useBumpMap\",+w),f.setUniform(h,\"1i\",\"useRoughnessMap\",+T),f.setUniform(h,\"1i\",\"doubleSide\",+S)),a=m,o=_,s=g,l=n}}}function bl(e){e=e||{},this._depthTex=new Dr({format:wr.DEPTH_COMPONENT,type:wr.UNSIGNED_INT}),this._normalTex=new Dr({type:wr.HALF_FLOAT}),this._framebuffer=new zi,this._framebuffer.attach(this._normalTex),this._framebuffer.attach(this._depthTex,zi.DEPTH_ATTACHMENT),this._normalMaterial=new le({shader:new Xe(Xe.source(\"ecgl.normal.vertex\"),Xe.source(\"ecgl.normal.fragment\"))}),this._normalMaterial.enableTexture([\"normalMap\",\"bumpMap\",\"roughnessMap\"]),this._defaultNormalMap=an.createBlank(\"#000\"),this._defaultBumpMap=an.createBlank(\"#000\"),this._defaultRoughessMap=an.createBlank(\"#000\"),this._debugPass=new pn({fragment:Xe.source(\"clay.compositor.output\")}),this._debugPass.setUniform(\"texture\",this._normalTex),this._debugPass.material.undefine(\"fragment\",\"OUTPUT_ALPHA\")}Xe.import(\"@export ecgl.normal.vertex\\n\\n@import ecgl.common.transformUniforms\\n\\n@import ecgl.common.uv.header\\n\\n@import ecgl.common.attributes\\n\\nvarying vec3 v_Normal;\\nvarying vec3 v_WorldPosition;\\n\\n@import ecgl.common.normalMap.vertexHeader\\n\\n@import ecgl.common.vertexAnimation.header\\n\\nvoid main()\\n{\\n\\n @import ecgl.common.vertexAnimation.main\\n\\n @import ecgl.common.uv.main\\n\\n v_Normal = normalize((worldInverseTranspose * vec4(normal, 0.0)).xyz);\\n v_WorldPosition = (world * vec4(pos, 1.0)).xyz;\\n\\n @import ecgl.common.normalMap.vertexMain\\n\\n gl_Position = worldViewProjection * vec4(pos, 1.0);\\n\\n}\\n\\n\\n@end\\n\\n\\n@export ecgl.normal.fragment\\n\\n#define ROUGHNESS_CHANEL 0\\n\\nuniform bool useBumpMap;\\nuniform bool useRoughnessMap;\\nuniform bool doubleSide;\\nuniform float roughness;\\n\\n@import ecgl.common.uv.fragmentHeader\\n\\nvarying vec3 v_Normal;\\nvarying vec3 v_WorldPosition;\\n\\nuniform mat4 viewInverse : VIEWINVERSE;\\n\\n@import ecgl.common.normalMap.fragmentHeader\\n@import ecgl.common.bumpMap.header\\n\\nuniform sampler2D roughnessMap;\\n\\nvoid main()\\n{\\n vec3 N = v_Normal;\\n \\n bool flipNormal = false;\\n if (doubleSide) {\\n vec3 eyePos = viewInverse[3].xyz;\\n vec3 V = normalize(eyePos - v_WorldPosition);\\n\\n if (dot(N, V) < 0.0) {\\n flipNormal = true;\\n }\\n }\\n\\n @import ecgl.common.normalMap.fragmentMain\\n\\n if (useBumpMap) {\\n N = bumpNormal(v_WorldPosition, v_Normal, N);\\n }\\n\\n float g = 1.0 - roughness;\\n\\n if (useRoughnessMap) {\\n float g2 = 1.0 - texture2D(roughnessMap, v_DetailTexcoord)[ROUGHNESS_CHANEL];\\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\\n }\\n\\n if (flipNormal) {\\n N = -N;\\n }\\n\\n gl_FragColor.rgb = (N.xyz + 1.0) * 0.5;\\n gl_FragColor.a = g;\\n}\\n@end\"),bl.prototype.getDepthTexture=function(){return this._depthTex},bl.prototype.getNormalTexture=function(){return this._normalTex},bl.prototype.update=function(e,t,r){var i=e.getWidth(),n=e.getHeight(),a=this._depthTex,o=this._normalTex,s=this._normalMaterial;a.width=i,a.height=n,o.width=i,o.height=n;var l=t.getRenderList(r).opaque;this._framebuffer.bind(e),e.gl.clearColor(0,0,0,0),e.gl.clear(e.gl.COLOR_BUFFER_BIT|e.gl.DEPTH_BUFFER_BIT),e.gl.disable(e.gl.BLEND),e.renderPass(l,r,{getMaterial:function(){return s},ifRender:function(e){return e.renderNormal},beforeRender:xl(e,this._defaultNormalMap,this._defaultBumpMap,this._defaultRoughessMap,this._normalMaterial),sort:e.opaqueSortCompare}),this._framebuffer.unbind(e)},bl.prototype.renderDebug=function(e){this._debugPass.render(e)},bl.prototype.dispose=function(e){this._depthTex.dispose(e),this._normalTex.dispose(e)};const wl=bl;function Tl(e){e=e||{},this._edgePass=new pn({fragment:Xe.source(\"ecgl.edge\")}),this._edgePass.setUniform(\"normalTexture\",e.normalTexture),this._edgePass.setUniform(\"depthTexture\",e.depthTexture),this._targetTexture=new Dr({type:wr.HALF_FLOAT}),this._frameBuffer=new zi,this._frameBuffer.attach(this._targetTexture)}Tl.prototype.update=function(e,t,r,i){var n=e.getWidth(),a=e.getHeight(),o=this._targetTexture;o.width=n,o.height=a;var s=this._frameBuffer;s.bind(e),this._edgePass.setUniform(\"projectionInv\",t.invProjectionMatrix.array),this._edgePass.setUniform(\"textureSize\",[n,a]),this._edgePass.setUniform(\"texture\",r),this._edgePass.render(e),s.unbind(e)},Tl.prototype.getTargetTexture=function(){return this._targetTexture},Tl.prototype.setParameter=function(e,t){this._edgePass.setUniform(e,t)},Tl.prototype.dispose=function(e){this._targetTexture.dispose(e),this._frameBuffer.dispose(e)};const Sl=Tl,Ml={type:\"compositor\",nodes:[{name:\"source\",type:\"texture\",outputs:{color:{}}},{name:\"source_half\",shader:\"#source(clay.compositor.downsample)\",inputs:{texture:\"source\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 2)\",height:\"expr(height * 1.0 / 2)\",type:\"HALF_FLOAT\"}}},parameters:{textureSize:\"expr( [width * 1.0, height * 1.0] )\"}},{name:\"bright\",shader:\"#source(clay.compositor.bright)\",inputs:{texture:\"source_half\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 2)\",height:\"expr(height * 1.0 / 2)\",type:\"HALF_FLOAT\"}}},parameters:{threshold:2,scale:4,textureSize:\"expr([width * 1.0 / 2, height / 2])\"}},{name:\"bright_downsample_4\",shader:\"#source(clay.compositor.downsample)\",inputs:{texture:\"bright\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 4)\",height:\"expr(height * 1.0 / 4)\",type:\"HALF_FLOAT\"}}},parameters:{textureSize:\"expr( [width * 1.0 / 2, height / 2] )\"}},{name:\"bright_downsample_8\",shader:\"#source(clay.compositor.downsample)\",inputs:{texture:\"bright_downsample_4\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 8)\",height:\"expr(height * 1.0 / 8)\",type:\"HALF_FLOAT\"}}},parameters:{textureSize:\"expr( [width * 1.0 / 4, height / 4] )\"}},{name:\"bright_downsample_16\",shader:\"#source(clay.compositor.downsample)\",inputs:{texture:\"bright_downsample_8\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 16)\",height:\"expr(height * 1.0 / 16)\",type:\"HALF_FLOAT\"}}},parameters:{textureSize:\"expr( [width * 1.0 / 8, height / 8] )\"}},{name:\"bright_downsample_32\",shader:\"#source(clay.compositor.downsample)\",inputs:{texture:\"bright_downsample_16\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 32)\",height:\"expr(height * 1.0 / 32)\",type:\"HALF_FLOAT\"}}},parameters:{textureSize:\"expr( [width * 1.0 / 16, height / 16] )\"}},{name:\"bright_upsample_16_blur_h\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_downsample_32\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 16)\",height:\"expr(height * 1.0 / 16)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:0,textureSize:\"expr( [width * 1.0 / 32, height / 32] )\"}},{name:\"bright_upsample_16_blur_v\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_upsample_16_blur_h\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 16)\",height:\"expr(height * 1.0 / 16)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:1,textureSize:\"expr( [width * 1.0 / 16, height * 1.0 / 16] )\"}},{name:\"bright_upsample_8_blur_h\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_downsample_16\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 8)\",height:\"expr(height * 1.0 / 8)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:0,textureSize:\"expr( [width * 1.0 / 16, height * 1.0 / 16] )\"}},{name:\"bright_upsample_8_blur_v\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_upsample_8_blur_h\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 8)\",height:\"expr(height * 1.0 / 8)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:1,textureSize:\"expr( [width * 1.0 / 8, height * 1.0 / 8] )\"}},{name:\"bright_upsample_8_blend\",shader:\"#source(clay.compositor.blend)\",inputs:{texture1:\"bright_upsample_8_blur_v\",texture2:\"bright_upsample_16_blur_v\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 8)\",height:\"expr(height * 1.0 / 8)\",type:\"HALF_FLOAT\"}}},parameters:{weight1:.3,weight2:.7}},{name:\"bright_upsample_4_blur_h\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_downsample_8\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 4)\",height:\"expr(height * 1.0 / 4)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:0,textureSize:\"expr( [width * 1.0 / 8, height * 1.0 / 8] )\"}},{name:\"bright_upsample_4_blur_v\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_upsample_4_blur_h\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 4)\",height:\"expr(height * 1.0 / 4)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:1,textureSize:\"expr( [width * 1.0 / 4, height * 1.0 / 4] )\"}},{name:\"bright_upsample_4_blend\",shader:\"#source(clay.compositor.blend)\",inputs:{texture1:\"bright_upsample_4_blur_v\",texture2:\"bright_upsample_8_blend\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 4)\",height:\"expr(height * 1.0 / 4)\",type:\"HALF_FLOAT\"}}},parameters:{weight1:.3,weight2:.7}},{name:\"bright_upsample_2_blur_h\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_downsample_4\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 2)\",height:\"expr(height * 1.0 / 2)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:0,textureSize:\"expr( [width * 1.0 / 4, height * 1.0 / 4] )\"}},{name:\"bright_upsample_2_blur_v\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_upsample_2_blur_h\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 2)\",height:\"expr(height * 1.0 / 2)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:1,textureSize:\"expr( [width * 1.0 / 2, height * 1.0 / 2] )\"}},{name:\"bright_upsample_2_blend\",shader:\"#source(clay.compositor.blend)\",inputs:{texture1:\"bright_upsample_2_blur_v\",texture2:\"bright_upsample_4_blend\"},outputs:{color:{parameters:{width:\"expr(width * 1.0 / 2)\",height:\"expr(height * 1.0 / 2)\",type:\"HALF_FLOAT\"}}},parameters:{weight1:.3,weight2:.7}},{name:\"bright_upsample_full_blur_h\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright\"},outputs:{color:{parameters:{width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:0,textureSize:\"expr( [width * 1.0 / 2, height * 1.0 / 2] )\"}},{name:\"bright_upsample_full_blur_v\",shader:\"#source(clay.compositor.gaussian_blur)\",inputs:{texture:\"bright_upsample_full_blur_h\"},outputs:{color:{parameters:{width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\",type:\"HALF_FLOAT\"}}},parameters:{blurSize:1,blurDir:1,textureSize:\"expr( [width * 1.0, height * 1.0] )\"}},{name:\"bloom_composite\",shader:\"#source(clay.compositor.blend)\",inputs:{texture1:\"bright_upsample_full_blur_v\",texture2:\"bright_upsample_2_blend\"},outputs:{color:{parameters:{width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\",type:\"HALF_FLOAT\"}}},parameters:{weight1:.3,weight2:.7}},{name:\"coc\",shader:\"#source(ecgl.dof.coc)\",outputs:{color:{parameters:{minFilter:\"NEAREST\",magFilter:\"NEAREST\",width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\"}}},parameters:{focalDist:50,focalRange:30}},{name:\"dof_far_blur\",shader:\"#source(ecgl.dof.diskBlur)\",inputs:{texture:\"source\",coc:\"coc\"},outputs:{color:{parameters:{width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\",type:\"HALF_FLOAT\"}}},parameters:{textureSize:\"expr( [width * 1.0, height * 1.0] )\"}},{name:\"dof_near_blur\",shader:\"#source(ecgl.dof.diskBlur)\",inputs:{texture:\"source\",coc:\"coc\"},outputs:{color:{parameters:{width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\",type:\"HALF_FLOAT\"}}},parameters:{textureSize:\"expr( [width * 1.0, height * 1.0] )\"},defines:{BLUR_NEARFIELD:null}},{name:\"dof_coc_blur\",shader:\"#source(ecgl.dof.diskBlur)\",inputs:{texture:\"coc\"},outputs:{color:{parameters:{minFilter:\"NEAREST\",magFilter:\"NEAREST\",width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\"}}},parameters:{textureSize:\"expr( [width * 1.0, height * 1.0] )\"},defines:{BLUR_COC:null}},{name:\"dof_composite\",shader:\"#source(ecgl.dof.composite)\",inputs:{original:\"source\",blurred:\"dof_far_blur\",nearfield:\"dof_near_blur\",coc:\"coc\",nearcoc:\"dof_coc_blur\"},outputs:{color:{parameters:{width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\",type:\"HALF_FLOAT\"}}}},{name:\"composite\",shader:\"#source(clay.compositor.hdr.composite)\",inputs:{texture:\"source\",bloom:\"bloom_composite\"},outputs:{color:{parameters:{width:\"expr(width * 1.0)\",height:\"expr(height * 1.0)\"}}},defines:{}},{name:\"FXAA\",shader:\"#source(clay.compositor.fxaa)\",inputs:{texture:\"composite\"}}]};function Al(e,t){return{color:{parameters:{width:e,height:t}}}}Xe.import(Zs),Xe.import(Ys),Xe.import(Ks),Xe.import(Qs),Xe.import(Js),Xe.import($s),Xe.import(el),Xe.import(tl),Xe.import(rl),Xe.import(\"@export ecgl.dof.coc\\n\\nuniform sampler2D depth;\\n\\nuniform float zNear: 0.1;\\nuniform float zFar: 2000;\\n\\nuniform float focalDistance: 3;\\nuniform float focalRange: 1;\\nuniform float focalLength: 30;\\nuniform float fstop: 2.8;\\n\\nvarying vec2 v_Texcoord;\\n\\n@import clay.util.encode_float\\n\\nvoid main()\\n{\\n float z = texture2D(depth, v_Texcoord).r * 2.0 - 1.0;\\n\\n float dist = 2.0 * zNear * zFar / (zFar + zNear - z * (zFar - zNear));\\n\\n float aperture = focalLength / fstop;\\n\\n float coc;\\n\\n float uppper = focalDistance + focalRange;\\n float lower = focalDistance - focalRange;\\n if (dist <= uppper && dist >= lower) {\\n coc = 0.5;\\n }\\n else {\\n float focalAdjusted = dist > uppper ? uppper : lower;\\n\\n coc = abs(aperture * (focalLength * (dist - focalAdjusted)) / (dist * (focalAdjusted - focalLength)));\\n coc = clamp(coc, 0.0, 2.0) / 2.00001;\\n\\n if (dist < lower) {\\n coc = -coc;\\n }\\n coc = coc * 0.5 + 0.5;\\n }\\n\\n gl_FragColor = encodeFloat(coc);\\n}\\n@end\\n\\n\\n@export ecgl.dof.composite\\n\\n#define DEBUG 0\\n\\nuniform sampler2D original;\\nuniform sampler2D blurred;\\nuniform sampler2D nearfield;\\nuniform sampler2D coc;\\nuniform sampler2D nearcoc;\\nvarying vec2 v_Texcoord;\\n\\n@import clay.util.rgbm\\n@import clay.util.float\\n\\nvoid main()\\n{\\n vec4 blurredColor = texture2D(blurred, v_Texcoord);\\n vec4 originalColor = texture2D(original, v_Texcoord);\\n\\n float fCoc = decodeFloat(texture2D(coc, v_Texcoord));\\n\\n fCoc = abs(fCoc * 2.0 - 1.0);\\n\\n float weight = smoothstep(0.0, 1.0, fCoc);\\n \\n#ifdef NEARFIELD_ENABLED\\n vec4 nearfieldColor = texture2D(nearfield, v_Texcoord);\\n float fNearCoc = decodeFloat(texture2D(nearcoc, v_Texcoord));\\n fNearCoc = abs(fNearCoc * 2.0 - 1.0);\\n\\n gl_FragColor = encodeHDR(\\n mix(\\n nearfieldColor, mix(originalColor, blurredColor, weight),\\n pow(1.0 - fNearCoc, 4.0)\\n )\\n );\\n#else\\n gl_FragColor = encodeHDR(mix(originalColor, blurredColor, weight));\\n#endif\\n\\n}\\n\\n@end\\n\\n\\n\\n@export ecgl.dof.diskBlur\\n\\n#define POISSON_KERNEL_SIZE 16;\\n\\nuniform sampler2D texture;\\nuniform sampler2D coc;\\nvarying vec2 v_Texcoord;\\n\\nuniform float blurRadius : 10.0;\\nuniform vec2 textureSize : [512.0, 512.0];\\n\\nuniform vec2 poissonKernel[POISSON_KERNEL_SIZE];\\n\\nuniform float percent;\\n\\nfloat nrand(const in vec2 n) {\\n return fract(sin(dot(n.xy ,vec2(12.9898,78.233))) * 43758.5453);\\n}\\n\\n@import clay.util.rgbm\\n@import clay.util.float\\n\\n\\nvoid main()\\n{\\n vec2 offset = blurRadius / textureSize;\\n\\n float rnd = 6.28318 * nrand(v_Texcoord + 0.07 * percent );\\n float cosa = cos(rnd);\\n float sina = sin(rnd);\\n vec4 basis = vec4(cosa, -sina, sina, cosa);\\n\\n#if !defined(BLUR_NEARFIELD) && !defined(BLUR_COC)\\n offset *= abs(decodeFloat(texture2D(coc, v_Texcoord)) * 2.0 - 1.0);\\n#endif\\n\\n#ifdef BLUR_COC\\n float cocSum = 0.0;\\n#else\\n vec4 color = vec4(0.0);\\n#endif\\n\\n\\n float weightSum = 0.0;\\n\\n for (int i = 0; i < POISSON_KERNEL_SIZE; i++) {\\n vec2 ofs = poissonKernel[i];\\n\\n ofs = vec2(dot(ofs, basis.xy), dot(ofs, basis.zw));\\n\\n vec2 uv = v_Texcoord + ofs * offset;\\n vec4 texel = texture2D(texture, uv);\\n\\n float w = 1.0;\\n#ifdef BLUR_COC\\n float fCoc = decodeFloat(texel) * 2.0 - 1.0;\\n cocSum += clamp(fCoc, -1.0, 0.0) * w;\\n#else\\n texel = texel;\\n #if !defined(BLUR_NEARFIELD)\\n float fCoc = decodeFloat(texture2D(coc, uv)) * 2.0 - 1.0;\\n w *= abs(fCoc);\\n #endif\\n texel.rgb *= texel.a;\\n color += texel * w;\\n#endif\\n\\n weightSum += w;\\n }\\n\\n#ifdef BLUR_COC\\n gl_FragColor = encodeFloat(clamp(cocSum / weightSum, -1.0, 0.0) * 0.5 + 0.5);\\n#else\\n color /= weightSum;\\n color.rgb /= (color.a + 0.0001);\\n gl_FragColor = color;\\n#endif\\n}\\n\\n@end\"),Xe.import(\"@export ecgl.edge\\n\\nuniform sampler2D texture;\\n\\nuniform sampler2D normalTexture;\\nuniform sampler2D depthTexture;\\n\\nuniform mat4 projectionInv;\\n\\nuniform vec2 textureSize;\\n\\nuniform vec4 edgeColor: [0,0,0,0.8];\\n\\nvarying vec2 v_Texcoord;\\n\\nvec3 packColor(vec2 coord) {\\n float z = texture2D(depthTexture, coord).r * 2.0 - 1.0;\\n vec4 p = vec4(v_Texcoord * 2.0 - 1.0, z, 1.0);\\n vec4 p4 = projectionInv * p;\\n\\n return vec3(\\n texture2D(normalTexture, coord).rg,\\n -p4.z / p4.w / 5.0\\n );\\n}\\n\\nvoid main() {\\n vec2 cc = v_Texcoord;\\n vec3 center = packColor(cc);\\n\\n float size = clamp(1.0 - (center.z - 10.0) / 100.0, 0.0, 1.0) * 0.5;\\n float dx = size / textureSize.x;\\n float dy = size / textureSize.y;\\n\\n vec2 coord;\\n vec3 topLeft = packColor(cc+vec2(-dx, -dy));\\n vec3 top = packColor(cc+vec2(0.0, -dy));\\n vec3 topRight = packColor(cc+vec2(dx, -dy));\\n vec3 left = packColor(cc+vec2(-dx, 0.0));\\n vec3 right = packColor(cc+vec2(dx, 0.0));\\n vec3 bottomLeft = packColor(cc+vec2(-dx, dy));\\n vec3 bottom = packColor(cc+vec2(0.0, dy));\\n vec3 bottomRight = packColor(cc+vec2(dx, dy));\\n\\n vec3 v = -topLeft-2.0*top-topRight+bottomLeft+2.0*bottom+bottomRight;\\n vec3 h = -bottomLeft-2.0*left-topLeft+bottomRight+2.0*right+topRight;\\n\\n float edge = sqrt(dot(h, h) + dot(v, v));\\n\\n edge = smoothstep(0.8, 1.0, edge);\\n\\n gl_FragColor = mix(texture2D(texture, v_Texcoord), vec4(edgeColor.rgb, 1.0), edgeColor.a * edge);\\n}\\n@end\");var El=[\"composite\",\"FXAA\"];function Cl(){this._width,this._height,this._dpr,this._sourceTexture=new Dr({type:wr.HALF_FLOAT}),this._depthTexture=new Dr({format:wr.DEPTH_COMPONENT,type:wr.UNSIGNED_INT}),this._framebuffer=new zi,this._framebuffer.attach(this._sourceTexture),this._framebuffer.attach(this._depthTexture,zi.DEPTH_ATTACHMENT),this._normalPass=new wl,this._compositor=function(e,t){var r=new Ws;t=t||{};var i={textures:{},parameters:{}};for(var n in e.parameters){var a=e.parameters[n];i.parameters[n]=sl(a)}return function(e,t,r,i){if(e.textures){var n={},a=0,o=!1,s=r.textureRootPath;f.each(e.textures,(function(e,t){var r,l=e.path,h=sl(e.parameters);if(Array.isArray(l)&&6===l.length)s&&(l=l.map((function(e){return f.relative2absolute(e,s)}))),r=new Ai(h);else{if(\"string\"!=typeof l)return;s&&(l=f.relative2absolute(l,s)),r=new Dr(h)}r.load(l),a++,r.once(\"success\",(function(){n[t]=r,0==--a&&(i(n),o=!0)}))})),0!==a||o||i(n)}else i({})}(e,0,t,(function(n){i.textures=n,function(n,a){for(var o=0;o=this._haltonSequence.length},render:function(e,t,r){var i=this._blendPass;0===this._frame?(i.setUniform(\"weight1\",0),i.setUniform(\"weight2\",1)):(i.setUniform(\"weight1\",.9),i.setUniform(\"weight2\",.1)),i.setUniform(\"texture1\",this._prevFrameTex),i.setUniform(\"texture2\",t||this._sourceTex),this._blendFb.attach(this._outputTex),this._blendFb.bind(e),i.render(e),this._blendFb.unbind(e),r||(this._outputPass.setUniform(\"texture\",this._outputTex),this._outputPass.render(e));var n=this._prevFrameTex;this._prevFrameTex=this._outputTex,this._outputTex=n,this._frame++},dispose:function(e){this._sourceFb.dispose(e),this._blendFb.dispose(e),this._prevFrameTex.dispose(e),this._outputTex.dispose(e),this._sourceTex.dispose(e),this._outputPass.dispose(e),this._blendPass.dispose(e)}};const Pl=Ll;function Ol(e){e=e||\"perspective\",this.layer=null,this.scene=new vi,this.rootNode=this.scene,this.viewport={x:0,y:0,width:0,height:0},this.setProjection(e),this._compositor=new Dl,this._temporalSS=new Pl,this._shadowMapPass=new ks;for(var t=[],r=0,i=0;i<30;i++){for(var n=[],a=0;a<6;a++)n.push(4*ul(r,2)-2),n.push(4*ul(r,3)-2),r++;t.push(n)}this._pcfKernels=t,this.scene.on(\"beforerender\",(function(e,t,r){this.needsTemporalSS()&&this._temporalSS.jitterProjection(e,r)}),this)}Ol.prototype.setProjection=function(e){var t=this.camera;t&&t.update(),\"perspective\"===e?this.camera instanceof Ei||(this.camera=new Ei,t&&this.camera.setLocalTransform(t.localTransform)):this.camera instanceof un||(this.camera=new un,t&&this.camera.setLocalTransform(t.localTransform)),this.camera.near=.1,this.camera.far=2e3},Ol.prototype.setViewport=function(e,t,r,i,n){this.camera instanceof Ei&&(this.camera.aspect=r/i),n=n||1,this.viewport.x=e,this.viewport.y=t,this.viewport.width=r,this.viewport.height=i,this.viewport.devicePixelRatio=n,this._compositor.resize(r*n,i*n),this._temporalSS.resize(r*n,i*n)},Ol.prototype.containPoint=function(e,t){var r=this.viewport;return t=this.layer.renderer.getHeight()-t,e>=r.x&&t>=r.y&&e<=r.x+r.width&&t<=r.y+r.height};var Nl=new _e;Ol.prototype.castRay=function(e,t,r){var i=this.layer.renderer,n=i.viewport;return i.viewport=this.viewport,i.screenToNDC(e,t,Nl),this.camera.castRay(Nl,r),i.viewport=n,r},Ol.prototype.prepareRender=function(){this.scene.update(),this.camera.update(),this.scene.updateLights();var e=this.scene.updateRenderList(this.camera);this._needsSortProgressively=!1;for(var t=0;t30},Ol.prototype._doRender=function(e,t,r){var i=this.scene,n=this.camera;r=r||0,this._updateTransparent(e,i,n,r),t||(this._shadowMapPass.kernelPCF=this._pcfKernels[0],this._shadowMapPass.render(e,i,n,!0)),this._updateShadowPCFKernel(r);var a,o=e.clearColor;e.gl.clearColor(o[0],o[1],o[2],o[3]),this._enablePostEffect&&(this.needsTemporalSS()&&this._temporalSS.jitterProjection(e,n),this._compositor.updateNormal(e,i,n,this._temporalSS.getFrame())),this._updateSSAO(e,i,n,this._temporalSS.getFrame()),this._enablePostEffect?((a=this._compositor.getSourceFrameBuffer()).bind(e),e.gl.clear(e.gl.DEPTH_BUFFER_BIT|e.gl.COLOR_BUFFER_BIT),e.render(i,n,!0,!0),a.unbind(e),this.needsTemporalSS()&&t?(this._compositor.composite(e,i,n,this._temporalSS.getSourceFrameBuffer(),this._temporalSS.getFrame()),e.setViewport(this.viewport),this._temporalSS.render(e)):(e.setViewport(this.viewport),this._compositor.composite(e,i,n,null,0))):this.needsTemporalSS()&&t?((a=this._temporalSS.getSourceFrameBuffer()).bind(e),e.saveClear(),e.clearBit=e.gl.DEPTH_BUFFER_BIT|e.gl.COLOR_BUFFER_BIT,e.render(i,n,!0,!0),e.restoreClear(),a.unbind(e),e.setViewport(this.viewport),this._temporalSS.render(e)):(e.setViewport(this.viewport),e.render(i,n,!0,!0))},Ol.prototype._updateTransparent=function(e,t,r,i){for(var n=new vt,a=new Ht,o=r.getWorldPosition(),s=t.getRenderList(r).transparent,l=0;lthis.camera.far||e80*r){i=a=e[0],n=o=e[1];for(var p=r;pa&&(a=s),l>o&&(o=l);h=Math.max(a-i,o-n)}return nh(d,f,r,i,n,h),f}function rh(e,t,r,i,n){var a,o;if(n===Th(e,t,r,i)>0)for(a=t;a=t;a-=i)o=xh(a,e[a],e[a+1],o);return o&&gh(o,o.next)&&(bh(o),o=o.next),o}function ih(e,t){if(!e)return e;t||(t=e);var r,i=e;do{if(r=!1,i.steiner||!gh(i,i.next)&&0!==mh(i.prev,i,i.next))i=i.next;else{if(bh(i),(i=t=i.prev)===i.next)return null;r=!0}}while(r||i!==t);return t}function nh(e,t,r,i,n,a,o){if(e){!o&&a&&function(e,t,r,i){var n=e;do{null===n.z&&(n.z=ch(n.x,n.y,t,r,i)),n.prevZ=n.prev,n.nextZ=n.next,n=n.next}while(n!==e);n.prevZ.nextZ=null,n.prevZ=null,function(e){var t,r,i,n,a,o,s,l,h=1;do{for(r=e,e=null,a=null,o=0;r;){for(o++,i=r,s=0,t=0;t0||l>0&&i;)0!==s&&(0===l||!i||r.z<=i.z)?(n=r,r=r.nextZ,s--):(n=i,i=i.nextZ,l--),a?a.nextZ=n:e=n,n.prevZ=a,a=n;r=i}a.nextZ=null,h*=2}while(o>1)}(n)}(e,i,n,a);for(var s,l,h=e;e.prev!==e.next;)if(s=e.prev,l=e.next,a?oh(e,i,n,a):ah(e))t.push(s.i/r),t.push(e.i/r),t.push(l.i/r),bh(e),e=l.next,h=l.next;else if((e=l)===h){o?1===o?nh(e=sh(e,t,r),t,r,i,n,a,2):2===o&&lh(e,t,r,i,n,a):nh(ih(e),t,r,i,n,a,1);break}}}function ah(e){var t=e.prev,r=e,i=e.next;if(mh(t,r,i)>=0)return!1;for(var n=e.next.next;n!==e.prev;){if(fh(t.x,t.y,r.x,r.y,i.x,i.y,n.x,n.y)&&mh(n.prev,n,n.next)>=0)return!1;n=n.next}return!0}function oh(e,t,r,i){var n=e.prev,a=e,o=e.next;if(mh(n,a,o)>=0)return!1;for(var s=n.xa.x?n.x>o.x?n.x:o.x:a.x>o.x?a.x:o.x,u=n.y>a.y?n.y>o.y?n.y:o.y:a.y>o.y?a.y:o.y,c=ch(s,l,t,r,i),d=ch(h,u,t,r,i),f=e.nextZ;f&&f.z<=d;){if(f!==e.prev&&f!==e.next&&fh(n.x,n.y,a.x,a.y,o.x,o.y,f.x,f.y)&&mh(f.prev,f,f.next)>=0)return!1;f=f.nextZ}for(f=e.prevZ;f&&f.z>=c;){if(f!==e.prev&&f!==e.next&&fh(n.x,n.y,a.x,a.y,o.x,o.y,f.x,f.y)&&mh(f.prev,f,f.next)>=0)return!1;f=f.prevZ}return!0}function sh(e,t,r){var i=e;do{var n=i.prev,a=i.next.next;!gh(n,a)&&_h(n,i,i.next,a)&&vh(n,a)&&vh(a,n)&&(t.push(n.i/r),t.push(i.i/r),t.push(a.i/r),bh(i),bh(i.next),i=e=a),i=i.next}while(i!==e);return i}function lh(e,t,r,i,n,a){var o=e;do{for(var s=o.next.next;s!==o.prev;){if(o.i!==s.i&&ph(o,s)){var l=yh(o,s);return o=ih(o,o.next),l=ih(l,l.next),nh(o,t,r,i,n,a),void nh(l,t,r,i,n,a)}s=s.next}o=o.next}while(o!==e)}function hh(e,t){return e.x-t.x}function uh(e,t){if(t=function(e,t){var r,i=t,n=e.x,a=e.y,o=-1/0;do{if(a<=i.y&&a>=i.next.y&&i.next.y!==i.y){var s=i.x+(a-i.y)*(i.next.x-i.x)/(i.next.y-i.y);if(s<=n&&s>o){if(o=s,s===n){if(a===i.y)return i;if(a===i.next.y)return i.next}r=i.x=i.x&&i.x>=u&&n!==i.x&&fh(ar.x)&&vh(i,e)&&(r=i,d=l),i=i.next;return r}(e,t)){var r=yh(t,e);ih(r,r.next)}}function ch(e,t,r,i,n){return(e=1431655765&((e=858993459&((e=252645135&((e=16711935&((e=32767*(e-r)/n)|e<<8))|e<<4))|e<<2))|e<<1))|(t=1431655765&((t=858993459&((t=252645135&((t=16711935&((t=32767*(t-i)/n)|t<<8))|t<<4))|t<<2))|t<<1))<<1}function dh(e){var t=e,r=e;do{t.x=0&&(e-o)*(i-s)-(r-o)*(t-s)>=0&&(r-o)*(a-s)-(n-o)*(i-s)>=0}function ph(e,t){return e.next.i!==t.i&&e.prev.i!==t.i&&!function(e,t){var r=e;do{if(r.i!==e.i&&r.next.i!==e.i&&r.i!==t.i&&r.next.i!==t.i&&_h(r,r.next,e,t))return!0;r=r.next}while(r!==e);return!1}(e,t)&&vh(e,t)&&vh(t,e)&&function(e,t){var r=e,i=!1,n=(e.x+t.x)/2,a=(e.y+t.y)/2;do{r.y>a!=r.next.y>a&&r.next.y!==r.y&&n<(r.next.x-r.x)*(a-r.y)/(r.next.y-r.y)+r.x&&(i=!i),r=r.next}while(r!==e);return i}(e,t)}function mh(e,t,r){return(t.y-e.y)*(r.x-t.x)-(t.x-e.x)*(r.y-t.y)}function gh(e,t){return e.x===t.x&&e.y===t.y}function _h(e,t,r,i){return!!(gh(e,t)&&gh(r,i)||gh(e,i)&&gh(r,t))||mh(e,t,r)>0!=mh(e,t,i)>0&&mh(r,i,e)>0!=mh(r,i,t)>0}function vh(e,t){return mh(e.prev,e,e.next)<0?mh(e,t,e.next)>=0&&mh(e,e.prev,t)>=0:mh(e,t,e.prev)<0||mh(e,e.next,t)<0}function yh(e,t){var r=new wh(e.i,e.x,e.y),i=new wh(t.i,t.x,t.y),n=e.next,a=t.prev;return e.next=t,t.prev=e,r.next=n,n.prev=r,i.next=r,r.prev=i,a.next=i,i.prev=a,i}function xh(e,t,r,i){var n=new wh(e,t,r);return i?(n.next=i.next,n.prev=i,i.next.prev=n,i.next=n):(n.prev=n,n.next=n),n}function bh(e){e.next.prev=e.prev,e.prev.next=e.next,e.prevZ&&(e.prevZ.nextZ=e.nextZ),e.nextZ&&(e.nextZ.prevZ=e.prevZ)}function wh(e,t,r){this.i=e,this.x=t,this.y=r,this.prev=null,this.next=null,this.z=null,this.prevZ=null,this.nextZ=null,this.steiner=!1}function Th(e,t,r,i){for(var n=0,a=t,o=r-i;ah&&s.push({pivot:Math.floor((l+h)/2),left:h,right:l}),h=a[o].pivot+1,(l=a[o].right)>h&&s.push({pivot:Math.floor((l+h)/2),left:h,right:l})}a=this._parts=s}else for(o=0;o=2e4},doSortTriangles:function(e,t){var r=this.indices;if(0===t){var i=this.attributes.position;e=e.array,this._triangleZList&&this._triangleZList.length===this.triangleCount||(this._triangleZList=new Float32Array(this.triangleCount),this._sortedTriangleIndices=new Uint32Array(this.triangleCount),this._indicesTmp=new r.constructor(r.length),this._triangleZListTmp=new Float32Array(this.triangleCount));for(var n,a=0,o=0;o0,r={},n=0;n65535?new Uint32Array(3*o):new Uint16Array(3*o),d.material.shader!==t&&d.material.attachShader(t,!0),Ka.setMaterialFromModel(t.__shading,d.material,e,r),s>0&&(this._linesMesh.geometry.resetOffset(),this._linesMesh.geometry.setVertexCount(s),this._linesMesh.geometry.setTriangleCount(l)),this._dataIndexOfVertex=new Uint32Array(a),this._vertexRangeOfDataIndex=new Uint32Array(2*(n-i))},_updateRegionMesh:function(e,t,r,i){for(var n=e.getData(),a=0,o=0,s=!1,l=this._polygonMesh,h=this._linesMesh,u=r;u0;w&&(b*=t.getDevicePixelRatio(),this._updateLinesGeometry(h.geometry,e,u,v,b,e.coordinateSystem.transform)),h.invisible=!w,h.material.set({color:g})}(l=this._polygonMesh).material.transparent=s,l.material.depthMask=!s,l.geometry.updateBoundingBox(),l.frontFace=this.extrudeY?Ka.Mesh.CCW:Ka.Mesh.CW,l.material.get(\"normalMap\")&&l.geometry.generateTangents(),l.seriesIndex=e.seriesIndex,l.on(\"mousemove\",this._onmousemove,this),l.on(\"mouseout\",this._onmouseout,this)},_updateDebugWireframe:function(e){var t=e.getModel(\"debug.wireframe\");if(t.get(\"show\")){var r=Ka.parseColor(t.get(\"lineStyle.color\")||\"rgba(0,0,0,0.5)\"),i=Mn(t.get(\"lineStyle.width\"),1),n=this._polygonMesh;n.geometry.generateBarycentric(),n.material.define(\"both\",\"WIREFRAME_TRIANGLE\"),n.material.set(\"wireframeLineColor\",r),n.material.set(\"wireframeLineWidth\",i)}},_onmousemove:function(e){var t=this._dataIndexOfVertex[e.triangle[0]];null==t&&(t=-1),t!==this._lastHoverDataIndex&&(this.downplay(this._lastHoverDataIndex),this.highlight(t),this._labelsBuilder.updateLabels([t])),this._lastHoverDataIndex=t,this._polygonMesh.dataIndex=t},_onmouseout:function(e){e.target&&(this.downplay(this._lastHoverDataIndex),this._lastHoverDataIndex=-1,this._polygonMesh.dataIndex=-1),this._labelsBuilder.updateLabels([])},_updateGroundPlane:function(e,t,r){var i=e.getModel(\"groundPlane\",e);if(this._groundMesh.invisible=!i.get(\"show\",!0),!this._groundMesh.invisible){var n=e.get(\"shading\"),a=this._groundMaterials[n];a||(a=this._groundMaterials.lambert),Ka.setMaterialFromModel(n,a,i,r),a.get(\"normalMap\")&&this._groundMesh.geometry.generateTangents(),this._groundMesh.material=a,this._groundMesh.material.set(\"color\",Ka.parseColor(i.get(\"color\"))),this._groundMesh.scale.set(t.size[0],t.size[2],1)}},_triangulation:function(e,t,r){this._triangulationResults=[];for(var i=[1/0,1/0,1/0],n=[-1/0,-1/0,-1/0],a=e.coordinateSystem,o=t;o1?i:0,L[U][g]=A.points[V+2],l.set(n+U,L[U]),s?(I[0]=(A.points[V]*_[0]-v[0])/x,I[1]=(A.points[V+2]*_[g]-v[g])/x):(I[0]=(k?R:R+G)/x,I[1]=(L[U][m]*_[m]-v[m])/x),u.set(n+U,I)}for(zh.sub(P,L[1],L[0]),zh.sub(O,L[3],L[0]),zh.cross(N,P,O),zh.normalize(N,N),U=0;U<4;U++)h.set(n+U,N),f&&c.set(n+U,o);for(U=0;U<6;U++)p[3*a+U]=D[U]+n;n+=4,a+=2,R+=G}}return t.dirty(),{vertexOffset:n,triangleOffset:a}},_getRegionLinesInfo:function(e,t,r){var i=0,n=0;return t.getRegionModel(e).getModel(\"itemStyle\").get(\"borderWidth\")>0&&t.getRegionPolygonCoords(e).forEach((function(e){var t=e.exterior,a=e.interiors;i+=r.getPolylineVertexCount(t),n+=r.getPolylineTriangleCount(t);for(var o=0;othis._endIndex)){t-=this._startIndex;for(var i=this._vertexRangeOfDataIndex[2*t];i0},_displacementChanged:!0,_displacementScale:0,updateDisplacementHash:function(){var e=this.getDisplacementTexture(),t=this.getDisplacemenScale();this._displacementChanged=this._displacementTexture!==e||this._displacementScale!==t,this._displacementTexture=e,this._displacementScale=t},isDisplacementChanged:function(){return this._displacementChanged}});i.util.merge(ru.prototype,uo),i.util.merge(ru.prototype,co),i.util.merge(ru.prototype,fo),i.util.merge(ru.prototype,Kl);const iu=ru;var nu=Math.PI,au=Math.sin,ou=Math.cos,su=Math.tan,lu=Math.asin,hu=Math.atan2,uu=nu/180,cu=23.4397*uu;function du(e,t){return hu(au(e)*ou(cu)-su(t)*au(cu),ou(e))}function fu(e,t,r){return hu(au(e),ou(e)*au(t)-su(r)*ou(t))}function pu(e,t,r){return lu(au(t)*au(r)+ou(t)*ou(r)*ou(e))}var mu={};mu.getPosition=function(e,t,r){var i=uu*-r,n=uu*t,a=function(e){return function(e){return e.valueOf()/864e5-.5+2440588}(e)-2451545}(e),o=function(e){var t,r,i=(r=function(e){return uu*(357.5291+.98560028*e)}(e))+uu*(1.9148*au(r)+.02*au(2*r)+3e-4*au(3*r))+102.9372*uu+nu;return{dec:(t=i,lu(au(0)*ou(cu)+ou(0)*au(cu)*au(t))),ra:du(i,0)}}(a),s=function(e,t){return uu*(280.16+360.9856235*e)-t}(a,i)-o.ra;return{azimuth:fu(s,n,o.dec),altitude:pu(s,n,o.dec)}};const gu=mu;Ka.Shader.import(Va),Ka.Shader.import(\"@export ecgl.atmosphere.vertex\\nattribute vec3 position: POSITION;\\nattribute vec3 normal : NORMAL;\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\nuniform mat4 normalMatrix : WORLDINVERSETRANSPOSE;\\n\\nvarying vec3 v_Normal;\\n\\nvoid main() {\\n v_Normal = normalize((normalMatrix * vec4(normal, 0.0)).xyz);\\n gl_Position = worldViewProjection * vec4(position, 1.0);\\n}\\n@end\\n\\n\\n@export ecgl.atmosphere.fragment\\nuniform mat4 viewTranspose: VIEWTRANSPOSE;\\nuniform float glowPower;\\nuniform vec3 glowColor;\\n\\nvarying vec3 v_Normal;\\n\\nvoid main() {\\n float intensity = pow(1.0 - dot(v_Normal, (viewTranspose * vec4(0.0, 0.0, 1.0, 0.0)).xyz), glowPower);\\n gl_FragColor = vec4(glowColor, intensity * intensity);\\n}\\n@end\");const _u=i.ComponentView.extend({type:\"globe\",__ecgl__:!0,_displacementScale:0,init:function(e,t){this.groupGL=new Ka.Node,this._sphereGeometry=new Ka.SphereGeometry({widthSegments:200,heightSegments:100,dynamic:!0}),this._overlayGeometry=new Ka.SphereGeometry({widthSegments:80,heightSegments:40}),this._planeGeometry=new Ka.PlaneGeometry,this._earthMesh=new Ka.Mesh({renderNormal:!0}),this._atmosphereMesh=new Ka.Mesh,this._atmosphereGeometry=new Ka.SphereGeometry({widthSegments:80,heightSegments:40}),this._atmosphereMaterial=new Ka.Material({shader:new Ka.Shader(Ka.Shader.source(\"ecgl.atmosphere.vertex\"),Ka.Shader.source(\"ecgl.atmosphere.fragment\")),transparent:!0}),this._atmosphereMesh.geometry=this._atmosphereGeometry,this._atmosphereMesh.material=this._atmosphereMaterial,this._atmosphereMesh.frontFace=Ka.Mesh.CW,this._lightRoot=new Ka.Node,this._sceneHelper=new Uo,this._sceneHelper.initLight(this._lightRoot),this.groupGL.add(this._atmosphereMesh),this.groupGL.add(this._earthMesh),this._control=new Do({zr:t.getZr()}),this._control.init(),this._layerMeshes={}},render:function(e,t,r){var i=e.coordinateSystem,n=e.get(\"shading\");i.viewGL.add(this._lightRoot),e.get(\"show\")?i.viewGL.add(this.groupGL):i.viewGL.remove(this.groupGL),this._sceneHelper.setScene(i.viewGL.scene),i.viewGL.setPostEffect(e.getModel(\"postEffect\"),r),i.viewGL.setTemporalSuperSampling(e.getModel(\"temporalSuperSampling\"));var a=this._earthMesh;a.geometry=this._sphereGeometry;var o=\"ecgl.\"+n;a.material&&a.material.shader.name===o||(a.material=Ka.createMaterial(o)),Ka.setMaterialFromModel(n,a.material,e,r),[\"roughnessMap\",\"metalnessMap\",\"detailMap\",\"normalMap\"].forEach((function(e){var t=a.material.get(e);t&&(t.flipY=!1)})),a.material.set(\"color\",Ka.parseColor(e.get(\"baseColor\")));var s=.99*i.radius;if(a.scale.set(s,s,s),e.get(\"atmosphere.show\")){a.material.define(\"both\",\"ATMOSPHERE_ENABLED\"),this._atmosphereMesh.invisible=!1,this._atmosphereMaterial.setUniforms({glowPower:e.get(\"atmosphere.glowPower\")||6,glowColor:e.get(\"atmosphere.color\")||\"#ffffff\"}),a.material.setUniforms({glowPower:e.get(\"atmosphere.innerGlowPower\")||2,glowColor:e.get(\"atmosphere.color\")||\"#ffffff\"});var l=e.get(\"atmosphere.offset\")||5;this._atmosphereMesh.scale.set(s+l,s+l,s+l)}else a.material.undefine(\"both\",\"ATMOSPHERE_ENABLED\"),this._atmosphereMesh.invisible=!0;var h=a.material.setTextureImage(\"diffuseMap\",e.get(\"baseTexture\"),r,{flipY:!1,anisotropic:8});h&&h.surface&&h.surface.attachToMesh(a);var u=a.material.setTextureImage(\"bumpMap\",e.get(\"heightTexture\"),r,{flipY:!1,anisotropic:8});u&&u.surface&&u.surface.attachToMesh(a),a.material[e.get(\"postEffect.enable\")?\"define\":\"undefine\"](\"fragment\",\"SRGB_DECODE\"),this._updateLight(e,r),this._displaceVertices(e,r),this._updateViewControl(e,r),this._updateLayers(e,r)},afterRender:function(e,t,r,i){var n=i.renderer;this._sceneHelper.updateAmbientCubemap(n,e,r),this._sceneHelper.updateSkybox(n,e,r)},_updateLayers:function(e,t){var r=e.coordinateSystem,n=e.get(\"layers\"),a=r.radius,o=[],s=[],l=[],h=[];i.util.each(n,(function(e){var n=new i.Model(e),u=n.get(\"type\"),c=Ka.loadTexture(n.get(\"texture\"),t,{flipY:!1,anisotropic:8});if(c.surface&&c.surface.attachToMesh(this._earthMesh),\"blend\"===u){var d=n.get(\"blendTo\"),f=Mn(n.get(\"intensity\"),1);\"emission\"===d?(l.push(c),h.push(f)):(o.push(c),s.push(f))}else{var p=n.get(\"id\"),m=this._layerMeshes[p];m||(m=this._layerMeshes[p]=new Ka.Mesh({geometry:this._overlayGeometry,castShadow:!1,ignorePicking:!0})),\"lambert\"===n.get(\"shading\")?(m.material=m.__lambertMaterial||new Ka.Material({autoUpdateTextureStatus:!1,shader:Ka.createShader(\"ecgl.lambert\"),transparent:!0,depthMask:!1}),m.__lambertMaterial=m.material):(m.material=m.__colorMaterial||new Ka.Material({autoUpdateTextureStatus:!1,shader:Ka.createShader(\"ecgl.color\"),transparent:!0,depthMask:!1}),m.__colorMaterial=m.material),m.material.enableTexture(\"diffuseMap\");var g=n.get(\"distance\"),_=a+(null==g?r.radius/100:g);m.scale.set(_,_,_),a=_;var v=this._blankTexture||(this._blankTexture=Ka.createBlankTexture(\"rgba(255, 255, 255, 0)\"));m.material.set(\"diffuseMap\",v),Ka.loadTexture(n.get(\"texture\"),t,{flipY:!1,anisotropic:8},(function(e){e.surface&&e.surface.attachToMesh(m),m.material.set(\"diffuseMap\",e),t.getZr().refresh()})),n.get(\"show\")?this.groupGL.add(m):this.groupGL.remove(m)}}),this);var u=this._earthMesh.material;u.define(\"fragment\",\"LAYER_DIFFUSEMAP_COUNT\",o.length),u.define(\"fragment\",\"LAYER_EMISSIVEMAP_COUNT\",l.length),u.set(\"layerDiffuseMap\",o),u.set(\"layerDiffuseIntensity\",s),u.set(\"layerEmissiveMap\",l),u.set(\"layerEmissionIntensity\",h);var c=e.getModel(\"debug.wireframe\");if(c.get(\"show\")){u.define(\"both\",\"WIREFRAME_TRIANGLE\");var d=Ka.parseColor(c.get(\"lineStyle.color\")||\"rgba(0,0,0,0.5)\"),f=Mn(c.get(\"lineStyle.width\"),1);u.set(\"wireframeLineWidth\",f),u.set(\"wireframeLineColor\",d)}else u.undefine(\"both\",\"WIREFRAME_TRIANGLE\")},_updateViewControl:function(e,t){var r=e.coordinateSystem,i=e.getModel(\"viewControl\"),n=(r.viewGL.camera,this),a=this._control;a.setViewGL(r.viewGL);var o,s,l=i.get(\"targetCoord\");null!=l&&(s=l[0]+90,o=l[1]),a.setFromViewControlModel(i,{baseDistance:r.radius,alpha:o,beta:s}),a.off(\"update\"),a.on(\"update\",(function(){t.dispatchAction({type:\"globeChangeCamera\",alpha:a.getAlpha(),beta:a.getBeta(),distance:a.getDistance()-r.radius,center:a.getCenter(),from:n.uid,globeId:e.id})}))},_displaceVertices:function(e,t){var r=e.get(\"displacementQuality\"),i=e.get(\"debug.wireframe.show\"),n=e.coordinateSystem;if(e.isDisplacementChanged()||r!==this._displacementQuality||i!==this._showDebugWireframe){this._displacementQuality=r,this._showDebugWireframe=i;var a=this._sphereGeometry,o={low:100,medium:200,high:400,ultra:800}[r]||200,s=o/2;(a.widthSegments!==o||i)&&(a.widthSegments=o,a.heightSegments=s,a.build()),this._doDisplaceVertices(a,n),i&&a.generateBarycentric()}},_doDisplaceVertices:function(e,t){var r=e.attributes.position.value,i=e.attributes.texcoord0.value,n=e.__originalPosition;n&&n.length===r.length||((n=new Float32Array(r.length)).set(r),e.__originalPosition=n);for(var a=t.displacementWidth,o=t.displacementHeight,s=t.displacementData,l=0;l50&&(a=1e3);var o=[];Ou.perspective(o,Iu,this.width/this.height,1,a),this.viewGL.camera.projectionMatrix.setArray(o),this.viewGL.camera.decomposeProjectionMatrix(),o=Ou.identity([]);var s=this.dataToPoint(this.center);Ou.scale(o,o,[1,-1,1]),Ou.translate(o,o,[0,0,-e]),Ou.rotateX(o,o,t),Ou.rotateZ(o,o,-this.bearing/180*Math.PI),Ou.translate(o,o,[-s[0]*this.getScale()*Bu,-s[1]*this.getScale()*Bu,0]),this.viewGL.camera.viewMatrix.array=o;var l=[];Ou.invert(l,o),this.viewGL.camera.worldTransform.array=l,this.viewGL.camera.decomposeWorldTransform();var h,u=Nu*this.getScale();if(this.altitudeExtent&&!isNaN(this.boxHeight)){var c=this.altitudeExtent[1]-this.altitudeExtent[0];h=this.boxHeight/c*this.getScale()/Math.pow(2,this._initialZoom-this.zoomOffset)}else h=u/(2*Math.PI*6378e3*Math.abs(Math.cos(this.center[1]*(Math.PI/180))))*this.altitudeScale*Bu;this.viewGL.rootNode.scale.set(this.getScale()*Bu,this.getScale()*Bu,h)}},getScale:function(){return Math.pow(2,this.zoom-this.zoomOffset)},projectOnTile:function(e,t){return this.projectOnTileWithScale(e,this.getScale()*Nu,t)},projectOnTileWithScale:function(e,t,r){var i=e[0],n=e[1]*Ru/180,a=t*(i*Ru/180+Ru)/(2*Ru),o=t*(Ru-Math.log(Math.tan(Ru/4+.5*n)))/(2*Ru);return(r=r||[])[0]=a,r[1]=o,r},unprojectFromTile:function(e,t){return this.unprojectOnTileWithScale(e,this.getScale()*Nu,t)},unprojectOnTileWithScale:function(e,t,r){var i=e[0],n=e[1],a=i/t*(2*Ru)-Ru,o=2*(Math.atan(Math.exp(Ru-n/t*(2*Ru)))-Ru/4);return(r=r||[])[0]=180*a/Ru,r[1]=180*o/Ru,r},dataToPoint:function(e,t){return(t=this.projectOnTileWithScale(e,Nu,t))[0]-=this._origin[0],t[1]-=this._origin[1],t[2]=isNaN(e[2])?0:e[2],isNaN(e[2])||(t[2]=e[2],this.altitudeExtent&&(t[2]-=this.altitudeExtent[0])),t}};const zu=Fu;function Gu(){zu.apply(this,arguments)}function Uu(e,t,r){function i(e,t){var r=t.getWidth(),i=t.getHeight(),n=t.getDevicePixelRatio();this.viewGL.setViewport(0,0,r,i,n),this.width=r,this.height=i,this.altitudeScale=e.get(\"altitudeScale\"),this.boxHeight=e.get(\"boxHeight\")}function n(e,t){if(\"auto\"!==this.model.get(\"boxHeight\")){var r=[1/0,-1/0];e.eachSeries((function(e){if(e.coordinateSystem===this){var t=e.getData(),i=e.coordDimToDataDim(\"alt\")[0];if(i){var n=t.getDataExtent(i,!0);r[0]=Math.min(r[0],n[0]),r[1]=Math.max(r[1],n[1])}}}),this),r&&isFinite(r[1]-r[0])&&(this.altitudeExtent=r)}}return{dimensions:t.prototype.dimensions,create:function(a,o){var s=[];return a.eachComponent(e,(function(e){var r=e.__viewGL;r||(r=e.__viewGL=new Il).setRootNode(new Ka.Node);var a=new t;a.viewGL=e.__viewGL,a.resize=i,a.resize(e,o),s.push(a),e.coordinateSystem=a,a.model=e,a.update=n})),a.eachSeries((function(t){if(t.get(\"coordinateSystem\")===e){var r=t.getReferringComponents(e).models[0];if(r||(r=a.getComponent(e)),!r)throw new Error(e+' \"'+Mn(t.get(e+\"Index\"),t.get(e+\"Id\"),0)+'\" not found');t.coordinateSystem=r.coordinateSystem}})),r&&r(s,a,o),s}}}Gu.prototype=new zu,Gu.prototype.constructor=Gu,Gu.prototype.type=\"mapbox3D\";const ku=Uu(\"mapbox3D\",Gu,(function(e){e.forEach((function(e){e.setCameraOption(e.model.getMapboxCameraOption())}))}));(0,i.use)((function(e){e.registerComponentModel(Au),e.registerComponentView(Pu),e.registerCoordinateSystem(\"mapbox3D\",ku),e.registerAction({type:\"mapbox3DChangeCamera\",event:\"mapbox3dcamerachanged\",update:\"mapbox3D:updateCamera\"},(function(e,t){t.eachComponent({mainType:\"mapbox3D\",query:e},(function(t){t.setMapboxCameraOption(e)}))}))}));var Vu=[\"zoom\",\"center\",\"pitch\",\"bearing\"],Hu=i.ComponentModel.extend({type:\"maptalks3D\",layoutMode:\"box\",coordinateSystem:null,defaultOption:{zlevel:-10,urlTemplate:\"http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png\",attribution:'© OpenStreetMap contributors, © CARTO',center:[0,0],zoom:0,pitch:0,bearing:0,light:{main:{alpha:20,beta:30}},altitudeScale:1,boxHeight:\"auto\"},getMaptalksCameraOption:function(){var e=this;return Vu.reduce((function(t,r){return t[r]=e.get(r),t}),{})},setMaptalksCameraOption:function(e){null!=e&&Vu.forEach((function(t){null!=e[t]&&(this.option[t]=e[t])}),this)},getMaptalks:function(){return this._maptalks},setMaptalks:function(e){this._maptalks=e}});i.util.merge(Hu.prototype,co),i.util.merge(Hu.prototype,fo);const Wu=Hu;function ju(e,t,r,i){if(this.id=e,this.zr=t,this.dom=document.createElement(\"div\"),this.dom.style.cssText=\"position:absolute;left:0;right:0;top:0;bottom:0;\",!maptalks)throw new Error(\"Maptalks library must be included. See https://maptalks.org\");this._maptalks=new maptalks.Map(this.dom,{center:r,zoom:i,doubleClickZoom:!1,fog:!1}),this._initEvents()}ju.prototype.setUnpainted=function(){},ju.prototype.resize=function(){this._maptalks.checkSize()},ju.prototype.getMaptalks=function(){return this._maptalks},ju.prototype.clear=function(){},ju.prototype.refresh=function(){this._maptalks.checkSize()};var Xu=[\"mousedown\",\"mouseup\",\"click\",\"dblclick\",\"mousemove\",\"mousewheel\",\"DOMMouseScroll\",\"touchstart\",\"touchend\",\"touchmove\",\"touchcancel\"];ju.prototype._initEvents=function(){var e=this.dom;this._handlers=this._handlers||{contextmenu:function(e){return e.preventDefault(),!1}},Xu.forEach((function(t){this._handlers[t]=function(r){var i={};for(var n in r)i[n]=r[n];i.bubbles=!1;var a=new r.constructor(r.type,i);\"mousewheel\"===t||\"DOMMouseScroll\"===t?e.dispatchEvent(a):e.firstElementChild.dispatchEvent(a)},this.zr.dom.addEventListener(t,this._handlers[t])}),this),this.zr.dom.addEventListener(\"contextmenu\",this._handlers.contextmenu)},ju.prototype.dispose=function(){Xu.forEach((function(e){this.zr.dom.removeEventListener(e,this._handlers[e])}),this),this._maptalks.remove()};const qu=ju;Ka.Shader.import(Lu);const Zu=i.ComponentView.extend({type:\"maptalks3D\",__ecgl__:!0,init:function(e,t){this._groundMesh=new Ka.Mesh({geometry:new Ka.PlaneGeometry,material:new Ka.Material({shader:new Ka.Shader({vertex:Ka.Shader.source(\"ecgl.displayShadow.vertex\"),fragment:Ka.Shader.source(\"ecgl.displayShadow.fragment\")}),depthMask:!1}),renderOrder:-100,culling:!1,castShadow:!1,$ignorePicking:!0,renderNormal:!0})},_initMaptalksLayer:function(e,t){var r=t.getZr();this._zrLayer=new qu(\"maptalks3D\",r,e.get(\"center\"),e.get(\"zoom\")),r.painter.insertLayer(-1e3,this._zrLayer),this._lightRoot=new Ka.Node,this._sceneHelper=new Uo(this._lightRoot),this._sceneHelper.initLight(this._lightRoot);var i=this._zrLayer.getMaptalks(),n=this._dispatchInteractAction.bind(this,t,i);[\"zoomend\",\"zooming\",\"zoomstart\",\"dragrotating\",\"pitch\",\"pitchend\",\"movestart\",\"moving\",\"moveend\",\"resize\",\"touchstart\",\"touchmove\",\"touchend\",\"animating\"].forEach((function(e){i.on(e,n)}))},render:function(e,t,r){this._zrLayer||this._initMaptalksLayer(e,r);var i=this._zrLayer.getMaptalks(),n=e.get(\"urlTemplate\"),a=i.getBaseLayer();n!==this._oldUrlTemplate&&(a?a.setOptions({urlTemplate:n,attribution:e.get(\"attribution\")}):(a=new maptalks.TileLayer(\"maptalks-echarts-gl-baselayer\",{urlTemplate:n,subdomains:[\"a\",\"b\",\"c\"],attribution:e.get(\"attribution\")}),i.setBaseLayer(a))),this._oldUrlTemplate=n,i.setCenter(e.get(\"center\")),i.setZoom(e.get(\"zoom\"),{animation:!1}),i.setPitch(e.get(\"pitch\")),i.setBearing(e.get(\"bearing\")),e.setMaptalks(i);var o=e.coordinateSystem;o.viewGL.scene.add(this._lightRoot),o.viewGL.add(this._groundMesh),this._updateGroundMesh(),this._sceneHelper.setScene(o.viewGL.scene),this._sceneHelper.updateLight(e),o.viewGL.setPostEffect(e.getModel(\"postEffect\"),r),o.viewGL.setTemporalSuperSampling(e.getModel(\"temporalSuperSampling\")),this._maptalks3DModel=e},afterRender:function(e,t,r,i){var n=i.renderer;this._sceneHelper.updateAmbientCubemap(n,e,r),this._sceneHelper.updateSkybox(n,e,r),e.coordinateSystem.viewGL.scene.traverse((function(e){e.material&&(e.material.define(\"fragment\",\"NORMAL_UP_AXIS\",2),e.material.define(\"fragment\",\"NORMAL_FRONT_AXIS\",1))}))},updateCamera:function(e,t,r,i){e.coordinateSystem.setCameraOption(i),this._updateGroundMesh(),r.getZr().refresh()},_dispatchInteractAction:function(e,t,r){var i;e.dispatchAction({type:\"maptalks3DChangeCamera\",pitch:t.getPitch(),zoom:(i=t.getResolution(),19-Math.log(i/Yu)/Math.LN2+1),center:t.getCenter().toArray(),bearing:t.getBearing(),maptalks3DId:this._maptalks3DModel&&this._maptalks3DModel.id})},_updateGroundMesh:function(){if(this._maptalks3DModel){var e=this._maptalks3DModel.coordinateSystem,t=e.dataToPoint(e.center);this._groundMesh.position.set(t[0],t[1],-.001);var r=new Ka.Plane(new Ka.Vector3(0,0,1),0),i=e.viewGL.camera.castRay(new Ka.Vector2(-1,-1)),n=e.viewGL.camera.castRay(new Ka.Vector2(1,1)),a=i.intersectPlane(r),o=n.intersectPlane(r),s=a.dist(o)/e.viewGL.rootNode.scale.x;this._groundMesh.scale.set(s,s,1)}},dispose:function(e,t){this._zrLayer&&this._zrLayer.dispose(),t.getZr().painter.delLayer(-1e3)}}),Yu=12756274*Math.PI/(256*Math.pow(2,20));function Ku(){zu.apply(this,arguments),this.maxPitch=85,this.zoomOffset=1}Ku.prototype=new zu,Ku.prototype.constructor=Ku,Ku.prototype.type=\"maptalks3D\";const Qu=Uu(\"maptalks3D\",Ku,(function(e){e.forEach((function(e){e.setCameraOption(e.model.getMaptalksCameraOption())}))}));(0,i.use)((function(e){e.registerComponentModel(Wu),e.registerComponentView(Zu),e.registerCoordinateSystem(\"maptalks3D\",Qu),e.registerAction({type:\"maptalks3DChangeCamera\",event:\"maptalks3dcamerachanged\",update:\"maptalks3D:updateCamera\"},(function(e,t){t.eachComponent({mainType:\"maptalks3D\",query:e},(function(t){t.setMaptalksCameraOption(e)}))}))}));var Ju=Po.vec3,$u=i.helper.dataStack.isDimensionStacked;function ec(e,t,r){for(var i=e.getDataExtent(t),n=e.getDataExtent(r),a=i[1]-i[0]||i[0],o=n[1]-n[0]||n[0],s=new Uint8Array(2500),l=0;l0&&d>0||c<0&&d<0)),m=[\"x\",\"y\",\"z\"].map((function(t){return e.coordDimToDataDim(t)[0]})),g=$u(r,m[2]),_=g?r.getCalculationInfo(\"stackResultDimension\"):m[2];r.each(m,(function(e,i,a,o){var s=r.get(_,o),l=g?s-a:p?0:f[0],h=t.dataToPoint([e,i,l]),u=t.dataToPoint([e,i,s]),c=Ju.dist(h,u),d=[0,u[1]\"+a.join(\"
\")}(a):i.format.encodeHTML(i.format.addCommas(a)),s=n.getName(t),l=Ih(n,t);i.util.isObject(l)&&l.colorStops&&(l=(l.colorStops[0]||{}).color),l=l||\"transparent\";var h=i.format.getTooltipMarker(l),u=e.name;return\"\\0-\"===u&&(u=\"\"),u=u?i.format.encodeHTML(u)+(r?\": \":\"
\"):\"\",r?h+u+o:u+h+(s?i.format.encodeHTML(s)+\": \"+o:o)}function sc(e,t,r){r=r||e.getSource();var n=t||i.getCoordinateSystemDimensions(e.get(\"coordinateSystem\"))||[\"x\",\"y\",\"z\"],a=i.helper.createDimensions(r,{dimensionsDefine:r.dimensionsDefine||e.get(\"dimensions\"),encodeDefine:r.encodeDefine||e.get(\"encode\"),coordDimensions:n.map((function(t){var r=e.getReferringComponents(t+\"Axis3D\").models[0];return{type:r&&\"category\"===r.get(\"type\")?\"ordinal\":\"float\",name:t}}))});\"cartesian3D\"===e.get(\"coordinateSystem\")&&a.forEach((function(t){if(n.indexOf(t.coordDim)>=0){var r=e.getReferringComponents(t.coordDim+\"Axis3D\").models[0];r&&\"category\"===r.get(\"type\")&&(t.ordinalMeta=r.getOrdinalMeta())}}));var o=i.helper.dataStack.enableDataStack(e,a,{byIndex:!0,stackedCoordDimension:\"z\"}),s=new i.List(a,e);return s.setCalculationInfo(o),s.initData(r),s}var lc=i.SeriesModel.extend({type:\"series.bar3D\",dependencies:[\"globe\"],visualStyleAccessPathvisu:\"itemStyle\",getInitialData:function(e,t){return sc(this)},getFormattedLabel:function(e,t,r,i){var n=ac.getFormattedLabel(this,e,t,r,i);return null==n&&(n=this.getData().get(\"z\",e)),n},formatTooltip:function(e){return oc(this,e)},defaultOption:{coordinateSystem:\"cartesian3D\",globeIndex:0,grid3DIndex:0,zlevel:-10,bevelSize:0,bevelSmoothness:2,onGridPlane:\"xy\",shading:\"color\",minHeight:0,itemStyle:{opacity:1},label:{show:!1,distance:2,textStyle:{fontSize:14,color:\"#000\",backgroundColor:\"rgba(255,255,255,0.7)\",padding:3,borderRadius:3}},emphasis:{label:{show:!0}},animationDurationUpdate:500}});i.util.merge(lc.prototype,Kl);const hc=lc;var uc,cc,dc,fc,pc,mc,gc,_c,vc=Po.vec3,yc=Po.mat3,xc=Vr.extend((function(){return{attributes:{position:new Vr.Attribute(\"position\",\"float\",3,\"POSITION\"),normal:new Vr.Attribute(\"normal\",\"float\",3,\"NORMAL\"),color:new Vr.Attribute(\"color\",\"float\",4,\"COLOR\"),prevPosition:new Vr.Attribute(\"prevPosition\",\"float\",3),prevNormal:new Vr.Attribute(\"prevNormal\",\"float\",3)},dynamic:!0,enableNormal:!1,bevelSize:1,bevelSegments:0,_dataIndices:null,_vertexOffset:0,_triangleOffset:0}}),{resetOffset:function(){this._vertexOffset=0,this._triangleOffset=0},setBarCount:function(e){var t=this.enableNormal,r=this.getBarVertexCount()*e,i=this.getBarTriangleCount()*e;this.vertexCount!==r&&(this.attributes.position.init(r),t?this.attributes.normal.init(r):this.attributes.normal.value=null,this.attributes.color.init(r)),this.triangleCount!==i&&(this.indices=r>65535?new Uint32Array(3*i):new Uint16Array(3*i),this._dataIndices=new Uint32Array(r))},getBarVertexCount:function(){var e=this.bevelSize>0?this.bevelSegments:0;return e>0?this._getBevelBarVertexCount(e):this.enableNormal?24:8},getBarTriangleCount:function(){var e=this.bevelSize>0?this.bevelSegments:0;return e>0?this._getBevelBarTriangleCount(e):12},_getBevelBarVertexCount:function(e){return 4*(e+1)*(e+1)*2},_getBevelBarTriangleCount:function(e){return(4*e+3+1)*(2*e+1)*2+4},setColor:function(e,t){for(var r=this.getBarVertexCount(),i=r*(e+1),n=r*e;n0&&this.bevelSegments>0)this._addBevelBar(e,c,m,g,this.bevelSize,this.bevelSegments,_);else{vc.copy(n,c),vc.normalize(n,n),vc.cross(a,m,n),vc.normalize(a,a),vc.cross(i,n,a),vc.normalize(a,a),vc.negate(o,i),vc.negate(s,n),vc.negate(l,a),t(h[0],e,i,g[0]/2),t(h[0],h[0],a,g[2]/2),t(h[1],e,i,g[0]/2),t(h[1],h[1],l,g[2]/2),t(h[2],e,o,g[0]/2),t(h[2],h[2],l,g[2]/2),t(h[3],e,o,g[0]/2),t(h[3],h[3],a,g[2]/2),t(r,e,n,g[1]),t(h[4],r,i,g[0]/2),t(h[4],h[4],a,g[2]/2),t(h[5],r,i,g[0]/2),t(h[5],h[5],l,g[2]/2),t(h[6],r,o,g[0]/2),t(h[6],h[6],l,g[2]/2),t(h[7],r,o,g[0]/2),t(h[7],h[7],a,g[2]/2);var x=this.attributes;if(this.enableNormal){u[0]=i,u[1]=o,u[2]=n,u[3]=s,u[4]=a,u[5]=l;for(var b=this._vertexOffset,w=0;w0&&(f++,u[3]<.99&&(p=!0))}})),o.geometry.setBarCount(f);var m=r.getLayout(\"orient\"),g=this._barIndexOfData=new Int32Array(r.count());f=0,r.each((function(e){if(r.hasValue(e)){var t=r.getItemLayout(e),i=t[0],n=t[1],o=t[2],s=4*e;u[0]=c[s++],u[1]=c[s++],u[2]=c[s++],u[3]=c[s++],u[3]>0&&(a._barMesh.geometry.addBar(i,n,m,o,u,e),g[e]=f++)}else g[e]=-1})),o.geometry.dirty(),o.geometry.updateBoundingBox();var _=o.material;_.transparent=p,_.depthMask=!p,o.geometry.sortTriangles=p,this._initHandler(e,t)},_initHandler:function(e,t){var r=e.getData(),i=this._barMesh,n=\"cartesian3D\"===e.coordinateSystem.type;i.seriesIndex=e.seriesIndex;var a=-1;i.off(\"mousemove\"),i.off(\"mouseout\"),i.on(\"mousemove\",(function(e){var o=i.geometry.getDataIndexOfVertex(e.triangle[0]);o!==a&&(this._downplay(a),this._highlight(o),this._labelsBuilder.updateLabels([o]),n&&t.dispatchAction({type:\"grid3DShowAxisPointer\",value:[r.get(\"x\",o),r.get(\"y\",o),r.get(\"z\",o,!0)]})),a=o,i.dataIndex=o}),this),i.on(\"mouseout\",(function(e){this._downplay(a),this._labelsBuilder.updateLabels(),a=-1,i.dataIndex=-1,n&&t.dispatchAction({type:\"grid3DHideAxisPointer\"})}),this)},_highlight:function(e){var t=this._data;if(t){var r=this._barIndexOfData[e];if(!(r<0)){var n=t.getItemModel(e).getModel(\"emphasis.itemStyle\"),a=n.get(\"color\"),o=n.get(\"opacity\");if(null==a){var s=Ih(t,e);a=i.color.lift(s,-.4)}null==o&&(o=Rh(t,e));var l=Ka.parseColor(a);l[3]*=o,this._barMesh.geometry.setColor(r,l),this._api.getZr().refresh()}}},_downplay:function(e){var t=this._data;if(t){var r=this._barIndexOfData[e];if(!(r<0)){var i=Ih(t,e),n=Rh(t,e),a=Ka.parseColor(i);a[3]*=n,this._barMesh.geometry.setColor(r,a),this._api.getZr().refresh()}}},highlight:function(e,t,r,i){this._toggleStatus(\"highlight\",e,t,r,i)},downplay:function(e,t,r,i){this._toggleStatus(\"downplay\",e,t,r,i)},_toggleStatus:function(e,t,r,n,a){var o=t.getData(),s=An(o,a),l=this;null!=s?i.util.each(ac.normalizeToArray(s),(function(t){\"highlight\"===e?this._highlight(t):this._downplay(t)}),this):o.each((function(t){\"highlight\"===e?l._highlight(t):l._downplay(t)}))},remove:function(){this.groupGL.removeAll()},dispose:function(){this._labelsBuilder.dispose(),this.groupGL.removeAll()}});(0,i.use)((function(e){e.registerChartView(Tc),e.registerSeriesModel(hc),nc(e),e.registerProcessor((function(e,t){e.eachSeriesByType(\"bar3d\",(function(e){var t=e.getData();t.filterSelf((function(e){return t.hasValue(e)}))}))}))}));const Sc=i.SeriesModel.extend({type:\"series.line3D\",dependencies:[\"grid3D\"],visualStyleAccessPath:\"lineStyle\",visualDrawType:\"stroke\",getInitialData:function(e,t){return sc(this)},formatTooltip:function(e){return oc(this,e)},defaultOption:{coordinateSystem:\"cartesian3D\",zlevel:-10,grid3DIndex:0,lineStyle:{width:2},animationDurationUpdate:500}});function Mc(e,t,r,i,n,a,o){if(0===n)return!1;var s,l=n;if(o>t+l&&o>i+l||oe+l&&a>r+l||a=0){var g=3*l,_=new vt(this._points[g],this._points[g+1],this._points[g+2]);a.push({dataIndex:l,point:_,pointWorld:_.clone(),target:this._line3DMesh,distance:this._camera.getWorldPosition().dist(_)})}},remove:function(){this.groupGL.removeAll()},dispose:function(){this.groupGL.removeAll()}});(0,i.use)((function(e){e.registerChartView(Ec),e.registerSeriesModel(Sc),e.registerLayout((function(e,t){e.eachSeriesByType(\"line3D\",(function(e){var t=e.getData(),r=e.coordinateSystem;if(r){if(\"cartesian3D\"!==r.type)return;var i=new Float32Array(3*t.count()),n=[],a=[],o=r.dimensions.map((function(t){return e.coordDimToDataDim(t)[0]}));r&&t.each(o,(function(e,t,o,s){n[0]=e,n[1]=t,n[2]=o,r.dataToPoint(n,a),i[3*s]=a[0],i[3*s+1]=a[1],i[3*s+2]=a[2]})),t.setLayout(\"points\",i)}}))}))}));const Cc=i.SeriesModel.extend({type:\"series.scatter3D\",dependencies:[\"globe\",\"grid3D\",\"geo3D\"],visualStyleAccessPath:\"itemStyle\",hasSymbolVisual:!0,getInitialData:function(e,t){return sc(this)},getFormattedLabel:function(e,t,r,i){var n=ac.getFormattedLabel(this,e,t,r,i);if(null==n){var a=this.getData(),o=a.dimensions[a.dimensions.length-1];n=a.get(o,e)}return n},formatTooltip:function(e){return oc(this,e)},defaultOption:{coordinateSystem:\"cartesian3D\",zlevel:-10,progressive:1e5,progressiveThreshold:1e5,grid3DIndex:0,globeIndex:0,symbol:\"circle\",symbolSize:10,blendMode:\"source-over\",label:{show:!1,position:\"right\",distance:5,textStyle:{fontSize:14,color:\"#000\",backgroundColor:\"rgba(255,255,255,0.7)\",padding:3,borderRadius:3}},itemStyle:{opacity:.8},emphasis:{label:{show:!0}},animationDurationUpdate:500}});function Dc(e,t,r){(t=t||document.createElement(\"canvas\")).width=e,t.height=e;var i=t.getContext(\"2d\");return r&&r(i),t}var Lc={getMarginByStyle:function(e){var t=e.minMargin||0,r=0;e.stroke&&\"none\"!==e.stroke&&(r=null==e.lineWidth?1:e.lineWidth);var i=e.shadowBlur||0,n=e.shadowOffsetX||0,a=e.shadowOffsetY||0,o={};return o.left=Math.max(r/2,-n+i,t),o.right=Math.max(r/2,n+i,t),o.top=Math.max(r/2,-a+i,t),o.bottom=Math.max(r/2,a+i,t),o},createSymbolSprite:function(e,t,r,n){var a=function(e,t,r,n){i.util.isArray(t)||(t=[t,t]);var a=Lc.getMarginByStyle(r,void 0),o=t[0]+a.left+a.right,s=t[1]+a.top+a.bottom,l=i.helper.createSymbol(e,0,0,t[0],t[1]),h=Math.max(o,s);l.x=a.left,l.y=a.top,o>s?l.y+=(h-s)/2:l.x+=(h-o)/2;var u=l.getBoundingRect();return l.x-=u.x,l.y-=u.y,l.setStyle(r),l.update(),l.__size=h,l}(e,t,r),o=Lc.getMarginByStyle(r);return{image:Dc(a.__size,n,(function(e){i.innerDrawElementOnCanvas(e,a)})),margin:o}},createSDFFromCanvas:function(e,t,r,i){return Dc(t,i,(function(t){var i=e.getContext(\"2d\").getImageData(0,0,e.width,e.height);t.putImageData(function(e,t,r){var i=t.width,n=t.height,a=e.canvas.width,o=e.canvas.height,s=i/a,l=n/o;function h(e){return e<128?1:-1}function u(e,a){var o=1/0;e=Math.floor(e*s);for(var u=(a=Math.floor(a*l))*i+e,c=h(t.data[4*u]),d=Math.max(a-r,0);d=2e4},doSortVertices:function(e,t){var r=this.indices,i=Oc.create();if(!r){r=this.indices=this.vertexCount>65535?new Uint32Array(this.vertexCount):new Uint16Array(this.vertexCount);for(var n=0;n.05);else for(n=0;n<3;n++)this._progressiveQuickSort(3*t+n);this.dirtyIndices()},_simpleSort:function(e){var t=this._zList,r=this.indices;function i(e,r){return t[r]-t[e]}e?Array.prototype.sort.call(r,i):Ch.sort(r,i,0,r.length-1)},_progressiveQuickSort:function(e){var t=this._zList,r=this.indices;this._quickSort=this._quickSort||new Ch,this._quickSort.step(r,(function(e,r){return t[r]-t[e]}),e)}};var Ic=Po.vec4;Ka.Shader.import(\"@export ecgl.sdfSprite.vertex\\n\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\nuniform float elapsedTime : 0;\\n\\nattribute vec3 position : POSITION;\\n\\n#ifdef VERTEX_SIZE\\nattribute float size;\\n#else\\nuniform float u_Size;\\n#endif\\n\\n#ifdef VERTEX_COLOR\\nattribute vec4 a_FillColor: COLOR;\\nvarying vec4 v_Color;\\n#endif\\n\\n#ifdef VERTEX_ANIMATION\\nattribute vec3 prevPosition;\\nattribute float prevSize;\\nuniform float percent : 1.0;\\n#endif\\n\\n\\n#ifdef POSITIONTEXTURE_ENABLED\\nuniform sampler2D positionTexture;\\n#endif\\n\\nvarying float v_Size;\\n\\nvoid main()\\n{\\n\\n#ifdef POSITIONTEXTURE_ENABLED\\n gl_Position = worldViewProjection * vec4(texture2D(positionTexture, position.xy).xy, -10.0, 1.0);\\n#else\\n\\n #ifdef VERTEX_ANIMATION\\n vec3 pos = mix(prevPosition, position, percent);\\n #else\\n vec3 pos = position;\\n #endif\\n gl_Position = worldViewProjection * vec4(pos, 1.0);\\n#endif\\n\\n#ifdef VERTEX_SIZE\\n#ifdef VERTEX_ANIMATION\\n v_Size = mix(prevSize, size, percent);\\n#else\\n v_Size = size;\\n#endif\\n#else\\n v_Size = u_Size;\\n#endif\\n\\n#ifdef VERTEX_COLOR\\n v_Color = a_FillColor;\\n #endif\\n\\n gl_PointSize = v_Size;\\n}\\n\\n@end\\n\\n@export ecgl.sdfSprite.fragment\\n\\nuniform vec4 color: [1, 1, 1, 1];\\nuniform vec4 strokeColor: [1, 1, 1, 1];\\nuniform float smoothing: 0.07;\\n\\nuniform float lineWidth: 0.0;\\n\\n#ifdef VERTEX_COLOR\\nvarying vec4 v_Color;\\n#endif\\n\\nvarying float v_Size;\\n\\nuniform sampler2D sprite;\\n\\n@import clay.util.srgb\\n\\nvoid main()\\n{\\n gl_FragColor = color;\\n\\n vec4 _strokeColor = strokeColor;\\n\\n#ifdef VERTEX_COLOR\\n gl_FragColor *= v_Color;\\n #endif\\n\\n#ifdef SPRITE_ENABLED\\n float d = texture2D(sprite, gl_PointCoord).r;\\n gl_FragColor.a *= smoothstep(0.5 - smoothing, 0.5 + smoothing, d);\\n\\n if (lineWidth > 0.0) {\\n float sLineWidth = lineWidth / 2.0;\\n\\n float outlineMaxValue0 = 0.5 + sLineWidth;\\n float outlineMaxValue1 = 0.5 + sLineWidth + smoothing;\\n float outlineMinValue0 = 0.5 - sLineWidth - smoothing;\\n float outlineMinValue1 = 0.5 - sLineWidth;\\n\\n if (d <= outlineMaxValue1 && d >= outlineMinValue0) {\\n float a = _strokeColor.a;\\n if (d <= outlineMinValue1) {\\n a = a * smoothstep(outlineMinValue0, outlineMinValue1, d);\\n }\\n else {\\n a = a * smoothstep(outlineMaxValue1, outlineMaxValue0, d);\\n }\\n gl_FragColor.rgb = mix(gl_FragColor.rgb * gl_FragColor.a, _strokeColor.rgb, a);\\n gl_FragColor.a = gl_FragColor.a * (1.0 - a) + a;\\n }\\n }\\n#endif\\n\\n#ifdef SRGB_DECODE\\n gl_FragColor = sRGBToLinear(gl_FragColor);\\n#endif\\n}\\n@end\");const Rc=Ka.Mesh.extend((function(){var e=new Ka.Geometry({dynamic:!0,attributes:{color:new Ka.Geometry.Attribute(\"color\",\"float\",4,\"COLOR\"),position:new Ka.Geometry.Attribute(\"position\",\"float\",3,\"POSITION\"),size:new Ka.Geometry.Attribute(\"size\",\"float\",1),prevPosition:new Ka.Geometry.Attribute(\"prevPosition\",\"float\",3),prevSize:new Ka.Geometry.Attribute(\"prevSize\",\"float\",1)}});Object.assign(e,Nc);var t=new Ka.Material({shader:Ka.createShader(\"ecgl.sdfSprite\"),transparent:!0,depthMask:!1});t.enableTexture(\"sprite\"),t.define(\"both\",\"VERTEX_COLOR\"),t.define(\"both\",\"VERTEX_SIZE\");var r=new Ka.Texture2D({image:document.createElement(\"canvas\"),flipY:!1});return t.set(\"sprite\",r),e.pick=this._pick.bind(this),{geometry:e,material:t,mode:Ka.Mesh.POINTS,sizeScale:1}}),{_pick:function(e,t,r,i,n,a){var o=this._positionNDC;if(o)for(var s=r.viewport,l=2/s.width,h=2/s.height,u=this.geometry.vertexCount-1;u>=0;u--){var c,d=o[2*(c=this.geometry.indices?this.geometry.indices[u]:u)],f=o[2*c+1],p=this.geometry.attributes.size.get(c)/this.sizeScale/2;if(e>d-p*l&&ef-p*h&&t2?(p=this._updateSymbolSprite(e,d,u,c),s.enableTexture(\"sprite\")):s.disableTexture(\"sprite\"),h.position.init(n-i);var m=[];if(f){s.undefine(\"VERTEX_SIZE\"),s.undefine(\"VERTEX_COLOR\");var g=function(e){const t=e.getVisual(\"style\");if(t)return t[e.getVisual(\"drawType\")]}(o),_=function(e){return e.getVisual(\"style\").opacity}(o);Ka.parseColor(g,m),m[3]*=_,s.set({color:m,u_Size:u.maxSize*this._sizeScale})}else s.set({color:[1,1,1,1]}),s.define(\"VERTEX_SIZE\"),s.define(\"VERTEX_COLOR\"),h.size.init(n-i),h.color.init(n-i),this._originalOpacity=new Float32Array(n-i);for(var v=o.getLayout(\"points\"),y=h.position.value,x=0;x1?(o[0]=r.maxSize,o[1]=r.maxSize/r.aspect):(o[1]=r.maxSize,o[0]=r.maxSize*r.aspect),o[0]=o[0]||1,o[1]=o[1]||1,this._symbolType===r.type&&(a=o,(n=this._symbolSize)&&a&&n[0]===a[0]&&n[1]===a[1])&&this._lineWidth===t.lineWidth||(Pc.createSymbolSprite(r.type,o,{fill:\"#fff\",lineWidth:t.lineWidth,stroke:\"transparent\",shadowColor:\"transparent\",minMargin:Math.min(o[0]/2,10)},this._spriteImageCanvas),Pc.createSDFFromCanvas(this._spriteImageCanvas,Math.min(this._spriteImageCanvas.width,32),20,this._mesh.material.get(\"sprite\").image),this._symbolType=r.type,this._symbolSize=o,this._lineWidth=t.lineWidth),this._spriteImageCanvas.width/r.maxSize*i},_updateMaterial:function(e,t){var r=\"lighter\"===e.get(\"blendMode\")?Ka.additiveBlend:null,i=this._mesh.material;i.blend=r,i.set(\"lineWidth\",t.lineWidth/20);var n=Ka.parseColor(t.stroke);i.set(\"strokeColor\",n),i.transparent=!0,i.depthMask=!1,i.depthTest=!this.is2D,i.sortVertices=!this.is2D},_updateLabelBuilder:function(e,t,r){var i=e.getData(),n=this._mesh.geometry,a=n.attributes.position.value,o=(t=this._startDataIndex,this._mesh.sizeScale);this._labelsBuilder.updateData(i,t,r),this._labelsBuilder.getLabelPosition=function(e,r,i){var n=3*(e-t);return[a[n],a[n+1],a[n+2]]},this._labelsBuilder.getLabelDistance=function(e,r,i){return n.attributes.size.get(e-t)/o/2+i},this._labelsBuilder.updateLabels()},_updateAnimation:function(e){Ka.updateVertexAnimation([[\"prevPosition\",\"position\"],[\"prevSize\",\"size\"]],this._prevMesh,this._mesh,e)},_updateHandler:function(e,t,r){var i,n=e.getData(),a=this._mesh,o=this,s=-1,l=e.coordinateSystem&&\"cartesian3D\"===e.coordinateSystem.type;l&&(i=e.coordinateSystem.model),a.seriesIndex=e.seriesIndex,a.off(\"mousemove\"),a.off(\"mouseout\"),a.on(\"mousemove\",(function(t){var h=t.vertexIndex+o._startDataIndex;h!==s&&(this.highlightOnMouseover&&(this.downplay(n,s),this.highlight(n,h),this._labelsBuilder.updateLabels([h])),l&&r.dispatchAction({type:\"grid3DShowAxisPointer\",value:[n.get(e.coordDimToDataDim(\"x\")[0],h),n.get(e.coordDimToDataDim(\"y\")[0],h),n.get(e.coordDimToDataDim(\"z\")[0],h)],grid3DIndex:i.componentIndex})),a.dataIndex=h,s=h}),this),a.on(\"mouseout\",(function(e){var t=e.vertexIndex+o._startDataIndex;this.highlightOnMouseover&&(this.downplay(n,t),this._labelsBuilder.updateLabels()),s=-1,a.dataIndex=-1,l&&r.dispatchAction({type:\"grid3DHideAxisPointer\",grid3DIndex:i.componentIndex})}),this)},updateLayout:function(e,t,r){var i=e.getData();if(this._mesh){var n=this._mesh.geometry.attributes.position.value,a=i.getLayout(\"points\");if(this.is2D)for(var o=0;othis._endDataIndex||tthis._endDataIndex||t 1.0 || v_Percent < 0.0) {\\n discard;\\n }\\n\\n float fade = v_Percent;\\n\\n#ifdef SRGB_DECODE\\n gl_FragColor = sRGBToLinear(color * v_Color);\\n#else\\n gl_FragColor = color * v_Color;\\n#endif\\n\\n @import ecgl.common.wireframe.fragmentMain\\n\\n if (v_Percent > (1.0 - v_SpotPercent)) {\\n gl_FragColor.rgb *= spotIntensity;\\n }\\n\\n gl_FragColor.a *= fade;\\n}\\n\\n@end\");const ad=Ka.Mesh.extend((function(){var e=new Ka.Material({shader:new Ka.Shader(Ka.Shader.source(\"ecgl.trail2.vertex\"),Ka.Shader.source(\"ecgl.trail2.fragment\")),transparent:!0,depthMask:!1}),t=new Ro({dynamic:!0});return t.createAttribute(\"dist\",\"float\",1),t.createAttribute(\"distAll\",\"float\",1),t.createAttribute(\"start\",\"float\",1),{geometry:t,material:e,culling:!1,$ignorePicking:!0}}),{updateData:function(e,t,r){var i=e.hostModel,n=this.geometry,a=i.getModel(\"effect\"),o=a.get(\"trailWidth\")*t.getDevicePixelRatio(),s=a.get(\"trailLength\"),l=i.get(\"effect.constantSpeed\"),h=1e3*i.get(\"effect.period\"),u=null!=l;u?this.material.set(\"speed\",l/1e3):this.material.set(\"period\",h),this.material[u?\"define\":\"undefine\"](\"vertex\",\"CONSTANT_SPEED\");var c=i.get(\"polyline\");n.trailLength=s,this.material.set(\"trailLength\",s),n.resetOffset(),[\"position\",\"positionPrev\",\"positionNext\"].forEach((function(e){n.attributes[e].value=r.attributes[e].value})),[\"dist\",\"distAll\",\"start\",\"offset\",\"color\"].forEach((function(e){n.attributes[e].init(n.vertexCount)})),n.indices=r.indices;var d=[],f=a.get(\"trailColor\"),p=a.get(\"trailOpacity\"),m=null!=f,g=null!=p;this.updateWorldTransform();var _=this.worldTransform.x.len(),v=this.worldTransform.y.len(),y=this.worldTransform.z.len(),x=0,b=0;e.each((function(t){var i=e.getItemLayout(t),a=g?p:Rh(e,t),s=Ih(e,t);null==a&&(a=1),(d=Ka.parseColor(m?f:s,d))[3]*=a;for(var l=c?r.getPolylineVertexCount(i):r.getCubicCurveVertexCount(i[0],i[1],i[2],i[3]),w=0,T=[],S=[],M=x;Mx&&(w+=nd.dist(T,S)),n.attributes.dist.set(M,w),nd.copy(S,T);b=Math.max(b,w);var A=Math.random()*(u?w:h);for(M=x;M0?1:-1)*o/2),n.attributes.color.set(M,d);x+=l})),this.material.set(\"spotSize\",.1*b*s),this.material.set(\"spotIntensity\",a.get(\"spotIntensity\")),n.dirty()},setAnimationTime:function(e){this.material.set(\"time\",e)}});Ka.Shader.import(is);const od=i.ChartView.extend({type:\"lines3D\",__ecgl__:!0,init:function(e,t){this.groupGL=new Ka.Node,this._meshLinesMaterial=new Ka.Material({shader:Ka.createShader(\"ecgl.meshLines3D\"),transparent:!0,depthMask:!1}),this._linesMesh=new Ka.Mesh({geometry:new Ro,material:this._meshLinesMaterial,$ignorePicking:!0}),this._trailMesh=new ad},render:function(e,t,r){this.groupGL.add(this._linesMesh);var i=e.coordinateSystem,n=e.getData();if(i&&i.viewGL){i.viewGL.add(this.groupGL),this._updateLines(e,t,r);var a=i.viewGL.isLinearSpace()?\"define\":\"undefine\";this._linesMesh.material[a](\"fragment\",\"SRGB_DECODE\"),this._trailMesh.material[a](\"fragment\",\"SRGB_DECODE\")}var o=this._trailMesh;if(o.stopAnimation(),e.get(\"effect.show\")){this.groupGL.add(o),o.updateData(n,r,this._linesMesh.geometry),o.__time=o.__time||0;var s=36e5;this._curveEffectsAnimator=o.animate(\"\",{loop:!0}).when(s,{__time:s}).during((function(){o.setAnimationTime(o.__time)})).start()}else this.groupGL.remove(o),this._curveEffectsAnimator=null;this._linesMesh.material.blend=this._trailMesh.material.blend=\"lighter\"===e.get(\"blendMode\")?Ka.additiveBlend:null},pauseEffect:function(){this._curveEffectsAnimator&&this._curveEffectsAnimator.pause()},resumeEffect:function(){this._curveEffectsAnimator&&this._curveEffectsAnimator.resume()},toggleEffect:function(){var e=this._curveEffectsAnimator;e&&(e.isPaused()?e.resume():e.pause())},_updateLines:function(e,t,r){var i=e.getData(),n=e.coordinateSystem,a=this._linesMesh.geometry,o=e.get(\"polyline\");a.expandLine=!0;var s=function(e){return null!=e.radius?e.radius:null!=e.size?Math.max(e.size[0],e.size[1],e.size[2]):100}(n);a.segmentScale=s/20;var l=\"lineStyle.width\".split(\".\"),h=r.getDevicePixelRatio(),u=0;i.each((function(e){var t=i.getItemModel(e).get(l);null==t&&(t=1),i.setItemVisual(e,\"lineWidth\",t),u=Math.max(t,u)})),a.useNativeLine=!1;var c=0,d=0;i.each((function(e){var t=i.getItemLayout(e);o?(c+=a.getPolylineVertexCount(t),d+=a.getPolylineTriangleCount(t)):(c+=a.getCubicCurveVertexCount(t[0],t[1],t[2],t[3]),d+=a.getCubicCurveTriangleCount(t[0],t[1],t[2],t[3]))})),a.setVertexCount(c),a.setTriangleCount(d),a.resetOffset();var f=[];i.each((function(e){var t=i.getItemLayout(e),r=Ih(i,e),n=Rh(i,e),s=i.getItemVisual(e,\"lineWidth\")*h;null==n&&(n=1),(f=Ka.parseColor(r,f))[3]*=n,o?a.addPolyline(t,f,s):a.addCubicCurve(t[0],t[1],t[2],t[3],f,s)})),a.dirty()},remove:function(){this.groupGL.removeAll()},dispose:function(){this.groupGL.removeAll()}});function sd(e,t){for(var r=[],i=0;i0;this._updateSurfaceMesh(this._surfaceMesh,e,u,f);var p=this._surfaceMesh.material;f?(p.define(\"WIREFRAME_QUAD\"),p.set(\"wireframeLineWidth\",d),p.set(\"wireframeLineColor\",Ka.parseColor(c.get(\"lineStyle.color\")))):p.undefine(\"WIREFRAME_QUAD\"),this._initHandler(e,r),this._updateAnimation(e)},_updateAnimation:function(e){Ka.updateVertexAnimation([[\"prevPosition\",\"position\"],[\"prevNormal\",\"normal\"]],this._prevSurfaceMesh,this._surfaceMesh,e)},_createSurfaceMesh:function(){var e=new Ka.Mesh({geometry:new Ka.Geometry({dynamic:!0,sortTriangles:!0}),shadowDepthMaterial:new Ka.Material({shader:new Ka.Shader(Ka.Shader.source(\"ecgl.sm.depth.vertex\"),Ka.Shader.source(\"ecgl.sm.depth.fragment\"))}),culling:!1,renderOrder:10,renderNormal:!0});return e.geometry.createAttribute(\"barycentric\",\"float\",4),e.geometry.createAttribute(\"prevPosition\",\"float\",3),e.geometry.createAttribute(\"prevNormal\",\"float\",3),Object.assign(e.geometry,Nh),e},_initHandler:function(e,t){var r=e.getData(),i=this._surfaceMesh,n=e.coordinateSystem;i.seriesIndex=e.seriesIndex;var a=-1;i.off(\"mousemove\"),i.off(\"mouseout\"),i.on(\"mousemove\",(function(e){var o=function(e,t){for(var r=1/0,n=-1,a=[],o=0;o=0){var s=[];i.geometry.attributes.position.get(o,s);for(var l=n.pointToData(s),h=1/0,u=-1,c=[],d=0;d65535?Uint32Array:Uint16Array)((p-1)*(m-1)*6),w=function(e,t,r){r[1]=e*m+t,r[0]=e*m+t+1,r[3]=(e+1)*m+t+1,r[2]=(e+1)*m+t},T=!1;if(l){var S=[],M=[],A=0;g?u.init(n.vertexCount):u.value=null;for(var E=[[],[],[]],C=[],D=[],L=fd.create(),P=function(e,t,r){var i=3*t;return r[0]=e[i],r[1]=e[i+1],r[2]=e[i+2],r},O=new Float32Array(o.length),N=new Float32Array(o.length/3*4),I=0;I0;){if(Math.floor(s/u)===s/u)return[u,s/u];u--}return[u=Math.floor(Math.sqrt(s)),u]},dispose:function(){this.groupGL.removeAll()},remove:function(){this.groupGL.removeAll()}});function md(e,t){for(var r=[],i=0;i=0&&e.call(t,r[n],n)},e.prototype.eachEdge=function(e,t){for(var r=this.edges,i=r.length,n=0;n=0&&r[n].node1.dataIndex>=0&&r[n].node2.dataIndex>=0&&e.call(t,r[n],n)},e.prototype.breadthFirstTraverse=function(e,t,r,i){if(t instanceof Ad||(t=this._nodesMap[Sd(t)]),t){for(var n=\"out\"===r?\"outEdges\":\"in\"===r?\"inEdges\":\"edges\",a=0;a=0&&r.node2.dataIndex>=0})),n=0,a=i.length;n=0&&this[e][t].setItemVisual(this.dataIndex,r,i)},getVisual:function(r){return this[e][t].getItemVisual(this.dataIndex,r)},setLayout:function(r,i){this.dataIndex>=0&&this[e][t].setItemLayout(this.dataIndex,r,i)},getLayout:function(){return this[e][t].getItemLayout(this.dataIndex)},getGraphicEl:function(){return this[e][t].getItemGraphicEl(this.dataIndex)},getRawIndex:function(){return this[e][t].getRawIndex(this.dataIndex)}}}_a(Ad,Cd(\"hostGraph\",\"data\")),_a(Ed,Cd(\"hostGraph\",\"edgeData\"));const Dd=Md;var Ld=_o();function Pd(e,t){if(Ld(this).mainData===this){var r=ga({},Ld(this).datas);r[this.dataType]=t,Bd(t,r,e)}else Fd(t,this.dataType,Ld(this).mainData,e);return t}function Od(e,t){return e.struct&&e.struct.update(),t}function Nd(e,t){return ya(Ld(t).datas,(function(r,i){r!==t&&Fd(r.cloneShallow(),i,t,e)})),t}function Id(e){var t=Ld(this).mainData;return null==e||null==t?t:Ld(t).datas[e]}function Rd(){var e=Ld(this).mainData;return null==e?[{data:e}]:xa(ba(Ld(e).datas),(function(t){return{type:t,data:Ld(e).datas[t]}}))}function Bd(e,t,r){Ld(e).datas={},ya(t,(function(t,i){Fd(t,i,e,r)}))}function Fd(e,t,r,i){Ld(r).datas[t]=e,Ld(e).mainData=r,e.dataType=t,i.struct&&(e[i.structAttr]=i.struct,i.struct[i.datasAttr[t]]=e),e.getLinkedData=Id,e.getLinkedDataAll=Rd}var zd=i.SeriesModel.extend({type:\"series.graphGL\",visualStyleAccessPath:\"itemStyle\",hasSymbolVisual:!0,init:function(e){zd.superApply(this,\"init\",arguments),this.legendDataProvider=function(){return this._categoriesData},this._updateCategoriesData()},mergeOption:function(e){zd.superApply(this,\"mergeOption\",arguments),this._updateCategoriesData()},getFormattedLabel:function(e,t,r,i){var n=ac.getFormattedLabel(this,e,t,r,i);if(null==n){var a=this.getData(),o=a.dimensions[a.dimensions.length-1];n=a.get(o,e)}return n},getInitialData:function(e,t){var r=e.edges||e.links||[],n=e.data||e.nodes||[],a=this;if(n&&r)return function(e,t,r,n,a){for(var o=new Dd(!0),s=0;s \"+p)),c++)}var m=i.helper.createDimensions(e,{coordDimensions:[\"value\"]});(l=new i.List(m,r)).initData(e);var g,_,v,y=new i.List([\"value\"],r);return y.initData(u,h),a&&a(l,y),_=(g={mainData:l,struct:o,structAttr:\"graph\",datas:{node:l,edge:y},datasAttr:{node:\"data\",edge:\"edgeData\"}}).mainData,(v=g.datas)||(v={main:_},g.datasAttr={main:\"data\"}),g.datas=g.mainData=null,Bd(_,v,g),ya(v,(function(e){ya(_.TRANSFERABLE_METHODS,(function(t){e.wrapMethod(t,wa(Pd,g))}))})),_.wrapMethod(\"cloneShallow\",wa(Nd,g)),ya(_.CHANGABLE_METHODS,(function(e){_.wrapMethod(e,wa(Od,g))})),function(e,t){if(!e)throw new Error(void 0)}(v[_.dataType]===_),o.update(),o}(n,r,this,0,(function(e,r){e.wrapMethod(\"getItemModel\",(function(e){const t=a._categoriesModels[e.getShallow(\"category\")];return t&&(t.parentModel=e.parentModel,e.parentModel=t),e}));const i=t.getModel([]).getModel;function n(e,t){const r=i.call(this,e,t);return r.resolveParentPath=o,r}function o(e){if(e&&(\"label\"===e[0]||\"label\"===e[1])){const t=e.slice();return\"label\"===e[0]?t[0]=\"edgeLabel\":\"label\"===e[1]&&(t[1]=\"edgeLabel\"),t}return e}r.wrapMethod(\"getItemModel\",(function(e){return e.resolveParentPath=o,e.getModel=n,e}))})).data},getGraph:function(){return this.getData().graph},getEdgeData:function(){return this.getGraph().edgeData},getCategoriesData:function(){return this._categoriesData},formatTooltip:function(e,t,r){if(\"edge\"===r){var n=this.getData(),a=this.getDataParams(e,r),o=n.graph.getEdgeByIndex(e),s=n.getName(o.node1.dataIndex),l=n.getName(o.node2.dataIndex),h=[];return null!=s&&h.push(s),null!=l&&h.push(l),h=i.format.encodeHTML(h.join(\" > \")),a.value&&(h+=\" : \"+i.format.encodeHTML(a.value)),h}return zd.superApply(this,\"formatTooltip\",arguments)},_updateCategoriesData:function(){var e=(this.option.categories||[]).map((function(e){return null!=e.value?e:Object.assign({value:0},e)})),t=new i.List([\"value\"],this);t.initData(e),this._categoriesData=t,this._categoriesModels=t.mapArray((function(e){return t.getItemModel(e,!0)}))},setView:function(e){null!=e.zoom&&(this.option.zoom=e.zoom),null!=e.offset&&(this.option.offset=e.offset)},setNodePosition:function(e){for(var t=0;t65535?this.indices instanceof Uint16Array&&(this.indices=new Uint32Array(this.indices)):this.indices instanceof Uint32Array&&(this.indices=new Uint16Array(this.indices)))},setTriangleCount:function(e){this.triangleCount!==e&&(this.indices=0===e?null:this.vertexCount>65535?new Uint32Array(3*e):new Uint16Array(3*e))},_getCubicCurveApproxStep:function(e,t,r,i){return 1/(Ud.dist(e,t)+Ud.dist(r,t)+Ud.dist(i,r)+1)*this.segmentScale},getCubicCurveVertexCount:function(e,t,r,i){var n=this._getCubicCurveApproxStep(e,t,r,i),a=Math.ceil(1/n);return this.useNativeLine?2*a:2*a+2},getCubicCurveTriangleCount:function(e,t,r,i){var n=this._getCubicCurveApproxStep(e,t,r,i),a=Math.ceil(1/n);return this.useNativeLine?0:2*a},getLineVertexCount:function(){return this.getPolylineVertexCount(kd)},getLineTriangleCount:function(){return this.getPolylineTriangleCount(kd)},getPolylineVertexCount:function(e){var t;return t=\"number\"==typeof e?e:\"number\"!=typeof e[0]?e.length:e.length/2,this.useNativeLine?2*(t-1):2*(t-1)+2},getPolylineTriangleCount:function(e){var t;return t=\"number\"==typeof e?e:\"number\"!=typeof e[0]?e.length:e.length/2,this.useNativeLine?0:2*(t-1)},addCubicCurve:function(e,t,r,i,n,a){null==a&&(a=1);var o=e[0],s=e[1],l=t[0],h=t[1],u=r[0],c=r[1],d=i[0],f=i[1],p=this._getCubicCurveApproxStep(e,t,r,i),m=p*p,g=m*p,_=3*p,v=3*m,y=6*m,x=6*g,b=o-2*l+u,w=s-2*h+c,T=3*(l-u)-o+d,S=3*(h-c)-s+f,M=o,A=s,E=(l-o)*_+b*v+T*g,C=(h-s)*_+w*v+S*g,D=b*y+T*x,L=w*y+S*x,P=T*x,O=S*x,N=0,I=0,R=Math.ceil(1/p),B=new Float32Array(3*(R+1)),F=(B=[],0);for(I=0;I1&&(M=E>0?Math.min(M,d):Math.max(M,d),A=C>0?Math.min(A,f):Math.max(A,f));this.addPolyline(B,n,a)},addLine:function(e,t,r,i){this.addPolyline([e,t],r,i)},addPolyline:function(){var e=Ud.create(),t=Ud.create(),r=Ud.create(),i=Ud.create(),n=[],a=[],o=[];return function(s,l,h,u,c){if(s.length){var d=\"number\"!=typeof s[0];if(null==c&&(c=d?s.length:s.length/2),!(c<2)){null==u&&(u=0),null==h&&(h=1),this._itemVertexOffsets.push(this._vertexOffset);for(var f,p=d?\"number\"!=typeof l[0]:l.length/4===c,m=this.attributes.position,g=this.attributes.color,_=this.attributes.offset,v=this.attributes.normal,y=this.indices,x=this._vertexOffset,b=0;b1&&(m.copy(x,x-1),g.copy(x,x-1),x++);else{var S;if(b0){Ud.sub(e,n,o),Ud.sub(t,a,n),Ud.normalize(e,e),Ud.normalize(t,t),Ud.add(i,e,t),Ud.normalize(i,i);var M=h/2*Math.min(1/Ud.dot(e,i),2);r[0]=-i[1],r[1]=i[0],S=M}else Ud.sub(e,a,n),Ud.normalize(e,e),r[0]=-e[1],r[1]=e[0],S=h/2;else Ud.sub(e,n,o),Ud.normalize(e,e),r[0]=-e[1],r[1]=e[0],S=h/2;v.set(x,r),v.set(x+1,r),_.set(x,S),_.set(x+1,-S),Ud.copy(o,n),m.set(x,n),m.set(x+1,n),g.set(x,f),g.set(x+1,f),x+=2}if(this.useNativeLine)g.set(x,f),m.set(x,n),x++;else if(b>0){var A=3*this._faceOffset;(y=this.indices)[A]=x-4,y[A+1]=x-3,y[A+2]=x-2,y[A+3]=x-3,y[A+4]=x-1,y[A+5]=x-2,this._faceOffset+=2}}this._vertexOffset=x}}}}(),setItemColor:function(e,t){for(var r=this._itemVertexOffsets[e],i=e 0.0) {\\n float factor = 0.0;\\n if (preventOverlap) {\\n float d = sqrt(d2);\\n d = d - n0.w - n1.w;\\n if (d > 0.0) {\\n factor = scaling * n0.z * n1.z / (d * d);\\n }\\n else if (d < 0.0) {\\n factor = scaling * 100.0 * n0.z * n1.z;\\n }\\n }\\n else {\\n factor = scaling * n0.z * n1.z / d2;\\n }\\n force += dir * factor;\\n }\\n }\\n\\n vec2 dir = gravityCenter - n0.xy;\\n float d = 1.0;\\n if (!strongGravityMode) {\\n d = length(dir);\\n }\\n\\n force += dir * n0.z * gravity / (d + 1.0);\\n\\n gl_FragColor = vec4(force, 0.0, 1.0);\\n}\\n@end\\n\\n@export ecgl.forceAtlas2.updateEdgeAttraction.vertex\\n\\nattribute vec2 node1;\\nattribute vec2 node2;\\nattribute float weight;\\n\\nuniform sampler2D positionTex;\\nuniform float edgeWeightInfluence;\\nuniform bool preventOverlap;\\nuniform bool linLogMode;\\n\\nuniform vec2 windowSize: WINDOW_SIZE;\\n\\nvarying vec2 v_Force;\\n\\nvoid main() {\\n\\n vec4 n0 = texture2D(positionTex, node1);\\n vec4 n1 = texture2D(positionTex, node2);\\n\\n vec2 dir = n1.xy - n0.xy;\\n float d = length(dir);\\n float w;\\n if (edgeWeightInfluence == 0.0) {\\n w = 1.0;\\n }\\n else if (edgeWeightInfluence == 1.0) {\\n w = weight;\\n }\\n else {\\n w = pow(weight, edgeWeightInfluence);\\n }\\n vec2 offset = vec2(1.0 / windowSize.x, 1.0 / windowSize.y);\\n vec2 scale = vec2((windowSize.x - 1.0) / windowSize.x, (windowSize.y - 1.0) / windowSize.y);\\n vec2 pos = node1 * scale * 2.0 - 1.0;\\n gl_Position = vec4(pos + offset, 0.0, 1.0);\\n gl_PointSize = 1.0;\\n\\n float factor;\\n if (preventOverlap) {\\n d = d - n1.w - n0.w;\\n }\\n if (d <= 0.0) {\\n v_Force = vec2(0.0);\\n return;\\n }\\n\\n if (linLogMode) {\\n factor = w * log(d) / d;\\n }\\n else {\\n factor = w;\\n }\\n v_Force = dir * factor;\\n}\\n@end\\n\\n@export ecgl.forceAtlas2.updateEdgeAttraction.fragment\\n\\nvarying vec2 v_Force;\\n\\nvoid main() {\\n gl_FragColor = vec4(v_Force, 0.0, 0.0);\\n}\\n@end\\n\\n@export ecgl.forceAtlas2.calcWeightedSum.vertex\\n\\nattribute vec2 node;\\n\\nvarying vec2 v_NodeUv;\\n\\nvoid main() {\\n\\n v_NodeUv = node;\\n gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\\n gl_PointSize = 1.0;\\n}\\n@end\\n\\n@export ecgl.forceAtlas2.calcWeightedSum.fragment\\n\\nvarying vec2 v_NodeUv;\\n\\nuniform sampler2D positionTex;\\nuniform sampler2D forceTex;\\nuniform sampler2D forcePrevTex;\\n\\nvoid main() {\\n vec2 force = texture2D(forceTex, v_NodeUv).rg;\\n vec2 forcePrev = texture2D(forcePrevTex, v_NodeUv).rg;\\n\\n float mass = texture2D(positionTex, v_NodeUv).z;\\n float swing = length(force - forcePrev) * mass;\\n float traction = length(force + forcePrev) * 0.5 * mass;\\n\\n gl_FragColor = vec4(swing, traction, 0.0, 0.0);\\n}\\n@end\\n\\n@export ecgl.forceAtlas2.calcGlobalSpeed\\n\\nuniform sampler2D globalSpeedPrevTex;\\nuniform sampler2D weightedSumTex;\\nuniform float jitterTolerence;\\n\\nvoid main() {\\n vec2 weightedSum = texture2D(weightedSumTex, vec2(0.5)).xy;\\n float prevGlobalSpeed = texture2D(globalSpeedPrevTex, vec2(0.5)).x;\\n float globalSpeed = jitterTolerence * jitterTolerence\\n * weightedSum.y / weightedSum.x;\\n if (prevGlobalSpeed > 0.0) {\\n globalSpeed = min(globalSpeed / prevGlobalSpeed, 1.5) * prevGlobalSpeed;\\n }\\n gl_FragColor = vec4(globalSpeed, 0.0, 0.0, 1.0);\\n}\\n@end\\n\\n@export ecgl.forceAtlas2.updatePosition\\n\\nuniform sampler2D forceTex;\\nuniform sampler2D forcePrevTex;\\nuniform sampler2D positionTex;\\nuniform sampler2D globalSpeedTex;\\n\\nvarying vec2 v_Texcoord;\\n\\nvoid main() {\\n vec2 force = texture2D(forceTex, v_Texcoord).xy;\\n vec2 forcePrev = texture2D(forcePrevTex, v_Texcoord).xy;\\n vec4 node = texture2D(positionTex, v_Texcoord);\\n\\n float globalSpeed = texture2D(globalSpeedTex, vec2(0.5)).r;\\n float swing = length(force - forcePrev);\\n float speed = 0.1 * globalSpeed / (0.1 + globalSpeed * sqrt(swing));\\n\\n float df = length(force);\\n if (df > 0.0) {\\n speed = min(df * speed, 10.0) / df;\\n\\n gl_FragColor = vec4(node.xy + speed * force, node.zw);\\n }\\n else {\\n gl_FragColor = node;\\n }\\n}\\n@end\\n\\n@export ecgl.forceAtlas2.edges.vertex\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\n\\nattribute vec2 node;\\nattribute vec4 a_Color : COLOR;\\nvarying vec4 v_Color;\\n\\nuniform sampler2D positionTex;\\n\\nvoid main()\\n{\\n gl_Position = worldViewProjection * vec4(\\n texture2D(positionTex, node).xy, -10.0, 1.0\\n );\\n v_Color = a_Color;\\n}\\n@end\\n\\n@export ecgl.forceAtlas2.edges.fragment\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\nvarying vec4 v_Color;\\nvoid main() {\\n gl_FragColor = color * v_Color;\\n}\\n@end\");var Wd={repulsionByDegree:!0,linLogMode:!1,strongGravityMode:!1,gravity:1,scaling:1,edgeWeightInfluence:1,jitterTolerence:.1,preventOverlap:!1,dissuadeHubs:!1,gravityCenter:null};function jd(e){var t={type:Ka.Texture.FLOAT,minFilter:Ka.Texture.NEAREST,magFilter:Ka.Texture.NEAREST};this._positionSourceTex=new Ka.Texture2D(t),this._positionSourceTex.flipY=!1,this._positionTex=new Ka.Texture2D(t),this._positionPrevTex=new Ka.Texture2D(t),this._forceTex=new Ka.Texture2D(t),this._forcePrevTex=new Ka.Texture2D(t),this._weightedSumTex=new Ka.Texture2D(t),this._weightedSumTex.width=this._weightedSumTex.height=1,this._globalSpeedTex=new Ka.Texture2D(t),this._globalSpeedPrevTex=new Ka.Texture2D(t),this._globalSpeedTex.width=this._globalSpeedTex.height=1,this._globalSpeedPrevTex.width=this._globalSpeedPrevTex.height=1,this._nodeRepulsionPass=new pn({fragment:Ka.Shader.source(\"ecgl.forceAtlas2.updateNodeRepulsion\")}),this._positionPass=new pn({fragment:Ka.Shader.source(\"ecgl.forceAtlas2.updatePosition\")}),this._globalSpeedPass=new pn({fragment:Ka.Shader.source(\"ecgl.forceAtlas2.calcGlobalSpeed\")}),this._copyPass=new pn({fragment:Ka.Shader.source(\"clay.compositor.output\")});var r=function(e){e.blendEquation(e.FUNC_ADD),e.blendFunc(e.ONE,e.ONE)};this._edgeForceMesh=new Ka.Mesh({geometry:new Ka.Geometry({attributes:{node1:new Ka.Geometry.Attribute(\"node1\",\"float\",2),node2:new Ka.Geometry.Attribute(\"node2\",\"float\",2),weight:new Ka.Geometry.Attribute(\"weight\",\"float\",1)},dynamic:!0,mainAttribute:\"node1\"}),material:new Ka.Material({transparent:!0,shader:Ka.createShader(\"ecgl.forceAtlas2.updateEdgeAttraction\"),blend:r,depthMask:!1,depthText:!1}),mode:Ka.Mesh.POINTS}),this._weightedSumMesh=new Ka.Mesh({geometry:new Ka.Geometry({attributes:{node:new Ka.Geometry.Attribute(\"node\",\"float\",2)},dynamic:!0,mainAttribute:\"node\"}),material:new Ka.Material({transparent:!0,shader:Ka.createShader(\"ecgl.forceAtlas2.calcWeightedSum\"),blend:r,depthMask:!1,depthText:!1}),mode:Ka.Mesh.POINTS}),this._framebuffer=new zi({depthBuffer:!1}),this._dummyCamera=new Ka.OrthographicCamera({left:-1,right:1,top:1,bottom:-1,near:0,far:100}),this._globalSpeed=0}jd.prototype.updateOption=function(e){for(var t in Wd)this[t]=Wd[t];var r=this._nodes.length;if(this.jitterTolerence=r>5e4?10:r>5e3?1:.1,this.scaling=r>100?2:10,e)for(var t in Wd)null!=e[t]&&(this[t]=e[t]);if(this.repulsionByDegree)for(var i=this._positionSourceTex.pixels,n=0;ne},jd.prototype._swapTexture=function(){var e=this._positionPrevTex;this._positionPrevTex=this._positionTex,this._positionTex=e,e=this._forcePrevTex,this._forcePrevTex=this._forceTex,this._forceTex=e,e=this._globalSpeedPrevTex,this._globalSpeedPrevTex=this._globalSpeedTex,this._globalSpeedTex=e},jd.prototype._initFromSource=function(e){this._framebuffer.attach(this._positionPrevTex),this._framebuffer.bind(e),this._copyPass.setUniform(\"texture\",this._positionSourceTex),this._copyPass.render(e),e.gl.clearColor(0,0,0,0),this._framebuffer.attach(this._forcePrevTex),e.gl.clear(e.gl.COLOR_BUFFER_BIT),this._framebuffer.attach(this._globalSpeedPrevTex),e.gl.clear(e.gl.COLOR_BUFFER_BIT),this._framebuffer.unbind(e)},jd.prototype._resize=function(e,t){[\"_positionSourceTex\",\"_positionTex\",\"_positionPrevTex\",\"_forceTex\",\"_forcePrevTex\"].forEach((function(r){this[r].width=e,this[r].height=t,this[r].dirty()}),this)},jd.prototype.dispose=function(e){this._framebuffer.dispose(e),this._copyPass.dispose(e),this._nodeRepulsionPass.dispose(e),this._positionPass.dispose(e),this._globalSpeedPass.dispose(e),this._edgeForceMesh.geometry.dispose(e),this._weightedSumMesh.geometry.dispose(e),this._positionSourceTex.dispose(e),this._positionTex.dispose(e),this._positionPrevTex.dispose(e),this._forceTex.dispose(e),this._forcePrevTex.dispose(e),this._weightedSumTex.dispose(e),this._globalSpeedTex.dispose(e),this._globalSpeedPrevTex.dispose(e)};const Xd=jd;var qd=function(){var e=function(){return new Float32Array(2)},t=function(e,t){var r=t[0]-e[0],i=t[1]-e[1];return Math.sqrt(r*r+i*i)},r=function(e){var t=e[0],r=e[1];return Math.sqrt(t*t+r*r)},i=function(e,t,r,i){return e[0]=t[0]+r[0]*i,e[1]=t[1]+r[1]*i,e},n=function(e,t,r){return e[0]=t[0]+r[0],e[1]=t[1]+r[1],e},a=function(e,t,r){return e[0]=t[0]-r[0],e[1]=t[1]-r[1],e},o=function(e,t,r){return e[0]=t,e[1]=r,e};function s(){this.subRegions=[],this.nSubRegions=0,this.node=null,this.mass=0,this.centerOfMass=null,this.bbox=new Float32Array(4),this.size=0}var l=s.prototype;function h(){this.position=new Float32Array(2),this.force=e(),this.forcePrev=e(),this.mass=1,this.inDegree=0,this.outDegree=0}function u(e,t){this.source=e,this.target=t,this.weight=1}function c(){this.autoSettings=!0,this.barnesHutOptimize=!0,this.barnesHutTheta=1.5,this.repulsionByDegree=!0,this.linLogMode=!1,this.strongGravityMode=!1,this.gravity=1,this.scaling=1,this.edgeWeightInfluence=1,this.jitterTolerence=.1,this.preventOverlap=!1,this.dissuadeHubs=!1,this.rootRegion=new s,this.rootRegion.centerOfMass=e(),this.nodes=[],this.edges=[],this.bbox=new Float32Array(4),this.gravityCenter=null,this._massArr=null,this._swingingArr=null,this._sizeArr=null,this._globalSpeed=0}l.beforeUpdate=function(){for(var e=0;e=e&&this.bbox[1]<=t&&this.bbox[3]>=t},l.setBBox=function(e,t,r,i){this.bbox[0]=e,this.bbox[1]=t,this.bbox[2]=r,this.bbox[3]=i,this.size=(r-e+i-t)/2},l._newSubRegion=function(){var e=this.subRegions[this.nSubRegions];return e||(e=new s,this.subRegions[this.nSubRegions]=e),this.nSubRegions++,e},l._addNodeToSubRegion=function(e){var t=this.findSubRegion(e.position[0],e.position[1]),r=this.bbox;if(!t){var i=(r[0]+r[2])/2,n=(r[1]+r[3])/2,a=(r[2]-r[0])/2,o=(r[3]-r[1])/2,s=e.position[0]>=i?1:0,l=e.position[1]>=n?1:0;(t=this._newSubRegion()).setBBox(s*a+r[0],l*o+r[1],(s+1)*a+r[0],(l+1)*o+r[1])}t.addNode(e)},l._updateCenterOfMass=function(e){null==this.centerOfMass&&(this.centerOfMass=new Float32Array(2));var t=this.centerOfMass[0]*this.mass,r=this.centerOfMass[1]*this.mass;t+=e.position[0]*e.mass,r+=e.position[1]*e.mass,this.mass+=e.mass,this.centerOfMass[0]=t/this.mass,this.centerOfMass[1]=r/this.mass};var d=c.prototype;d.initNodes=function(e,t,r){var i=t.length;this.nodes.length=0;for(var n=void 0!==r,a=0;a0&&(this.strongGravityMode?this.applyNodeStrongGravity(c):this.applyNodeGravity(c))}for(h=0;h0&&(_=Math.min(_/this._globalSpeed,1.5)*this._globalSpeed),this._globalSpeed=_,h=0;h0&&(y=Math.min(x*y,10)/x,i(u.position,u.position,u.force,y))}},d.applyRegionToNodeRepulsion=function(){var t=e();return function(e,r){if(e.node)this.applyNodeToNodeRepulsion(e.node,r,!0);else{a(t,r.position,e.centerOfMass);var n=t[0]*t[0]+t[1]*t[1];if(n>this.barnesHutTheta*e.size*e.size){var o=this.scaling*r.mass*e.mass/n;i(r.force,r.force,t,o)}else for(var s=0;s0)s=this.scaling*e.mass*r.mass/(l*l);else{if(!(l<0))return;s=100*this.scaling*e.mass*r.mass}}else s=this.scaling*e.mass*r.mass/o;i(e.force,e.force,t,s),i(r.force,r.force,t,-s)}}}}(),d.applyEdgeAttraction=function(){var t=e();return function(e){var n=e.source,o=e.target;a(t,n.position,o.position);var s,l,h=r(t);s=0===this.edgeWeightInfluence?1:1===this.edgeWeightInfluence?e.weight:Math.pow(e.weight,this.edgeWeightInfluence),this.preventOverlap&&(h=h-n.size-o.size)<=0||(l=this.linLogMode?-s*Math.log(h+1)/(h+1):-s,i(n.force,n.force,t,l),i(o.force,o.force,t,-l))}}(),d.applyNodeGravity=function(){var t=e();return function(e){a(t,this.gravityCenter,e.position);var n=r(t);i(e.force,e.force,t,this.gravity*e.mass/(n+1))}}(),d.applyNodeStrongGravity=function(){var t=e();return function(e){a(t,this.gravityCenter,e.position),i(e.force,e.force,t,this.gravity*e.mass)}}(),d.updateBBox=function(){for(var e=1/0,t=1/0,r=-1/0,i=-1/0,n=0;n5e4?10:a>5e3?1:.1,t.scaling=a>100?2:10,t.barnesHutOptimize=a>1e3,e)for(var r in Zd)null!=e[r]&&(t[r]=e[r]);if(!t.gravityCenter){for(var o=[1/0,1/0],s=[-1/0,-1/0],l=0;le},Yd.prototype.getNodePosition=function(e,t){if(t||(t=new Float32Array(2*this._nodes.length)),this._positionArr)for(var r=0;r0?1.1:.9,a=Math.max(Math.min(this._zoom*n,this.maxZoom),this.minZoom);n=a/this._zoom;var o=this._convertPos(r,i),s=(o.x-this._dx)*(n-1),l=(o.y-this._dy)*(n-1);this._dx-=s,this._dy-=l,this._zoom=a,this._needsUpdate=!0}}},dispose:function(){var e=this.zr;e.off(\"mousedown\",this._mouseDownHandler),e.off(\"mousemove\",this._mouseMoveHandler),e.off(\"mouseup\",this._mouseUpHandler),e.off(\"mousewheel\",this._mouseWheelHandler),e.off(\"globalout\",this._mouseUpHandler),e.animation.off(\"frame\",this._update)}});var Jd=Po.vec2;Ka.Shader.import(\"@export ecgl.lines2D.vertex\\n\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\n\\nattribute vec2 position: POSITION;\\nattribute vec4 a_Color : COLOR;\\nvarying vec4 v_Color;\\n\\n#ifdef POSITIONTEXTURE_ENABLED\\nuniform sampler2D positionTexture;\\n#endif\\n\\nvoid main()\\n{\\n gl_Position = worldViewProjection * vec4(position, -10.0, 1.0);\\n\\n v_Color = a_Color;\\n}\\n\\n@end\\n\\n@export ecgl.lines2D.fragment\\n\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\n\\nvarying vec4 v_Color;\\n\\nvoid main()\\n{\\n gl_FragColor = color * v_Color;\\n}\\n@end\\n\\n\\n@export ecgl.meshLines2D.vertex\\n\\nattribute vec2 position: POSITION;\\nattribute vec2 normal;\\nattribute float offset;\\nattribute vec4 a_Color : COLOR;\\n\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\nuniform vec4 viewport : VIEWPORT;\\n\\nvarying vec4 v_Color;\\nvarying float v_Miter;\\n\\nvoid main()\\n{\\n vec4 p2 = worldViewProjection * vec4(position + normal, -10.0, 1.0);\\n gl_Position = worldViewProjection * vec4(position, -10.0, 1.0);\\n\\n p2.xy /= p2.w;\\n gl_Position.xy /= gl_Position.w;\\n\\n vec2 N = normalize(p2.xy - gl_Position.xy);\\n gl_Position.xy += N * offset / viewport.zw * 2.0;\\n\\n gl_Position.xy *= gl_Position.w;\\n\\n v_Color = a_Color;\\n}\\n@end\\n\\n\\n@export ecgl.meshLines2D.fragment\\n\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\n\\nvarying vec4 v_Color;\\nvarying float v_Miter;\\n\\nvoid main()\\n{\\n gl_FragColor = color * v_Color;\\n}\\n\\n@end\");var $d=1;const ef=i.ChartView.extend({type:\"graphGL\",__ecgl__:!0,init:function(e,t){this.groupGL=new Ka.Node,this.viewGL=new Il(\"orthographic\"),this.viewGL.camera.left=this.viewGL.camera.right=0,this.viewGL.add(this.groupGL),this._pointsBuilder=new Fc(!0,t),this._forceEdgesMesh=new Ka.Mesh({material:new Ka.Material({shader:Ka.createShader(\"ecgl.forceAtlas2.edges\"),transparent:!0,depthMask:!1,depthTest:!1}),$ignorePicking:!0,geometry:new Ka.Geometry({attributes:{node:new Ka.Geometry.Attribute(\"node\",\"float\",2),color:new Ka.Geometry.Attribute(\"color\",\"float\",4,\"COLOR\")},dynamic:!0,mainAttribute:\"node\"}),renderOrder:-1,mode:Ka.Mesh.LINES}),this._edgesMesh=new Ka.Mesh({material:new Ka.Material({shader:Ka.createShader(\"ecgl.meshLines2D\"),transparent:!0,depthMask:!1,depthTest:!1}),$ignorePicking:!0,geometry:new Hd({useNativeLine:!1,dynamic:!0}),renderOrder:-1,culling:!1}),this._layoutId=0,this._control=new Qd({zr:t.getZr(),viewGL:this.viewGL}),this._control.setTarget(this.groupGL),this._control.init(),this._clickHandler=this._clickHandler.bind(this)},render:function(e,t,r){this.groupGL.add(this._pointsBuilder.rootNode),this._model=e,this._api=r,this._initLayout(e,t,r),this._pointsBuilder.update(e,t,r),this._forceLayoutInstance instanceof Xd||this.groupGL.remove(this._forceEdgesMesh),this._updateCamera(e,r),this._control.off(\"update\"),this._control.on(\"update\",(function(){r.dispatchAction({type:\"graphGLRoam\",seriesId:e.id,zoom:this._control.getZoom(),offset:this._control.getOffset()}),this._pointsBuilder.updateView(this.viewGL.camera)}),this),this._control.setZoom(Mn(e.get(\"zoom\"),1)),this._control.setOffset(e.get(\"offset\")||[0,0]);var i=this._pointsBuilder.getPointsMesh();if(i.off(\"mousemove\",this._mousemoveHandler),i.off(\"mouseout\",this._mouseOutHandler,this),r.getZr().off(\"click\",this._clickHandler),this._pointsBuilder.highlightOnMouseover=!0,e.get(\"focusNodeAdjacency\")){var n=e.get(\"focusNodeAdjacencyOn\");\"click\"===n?r.getZr().on(\"click\",this._clickHandler):\"mouseover\"===n&&(i.on(\"mousemove\",this._mousemoveHandler,this),i.on(\"mouseout\",this._mouseOutHandler,this),this._pointsBuilder.highlightOnMouseover=!1)}this._lastMouseOverDataIndex=-1},_clickHandler:function(e){if(!this._layouting){var t=this._pointsBuilder.getPointsMesh().dataIndex;t>=0?this._api.dispatchAction({type:\"graphGLFocusNodeAdjacency\",seriesId:this._model.id,dataIndex:t}):this._api.dispatchAction({type:\"graphGLUnfocusNodeAdjacency\",seriesId:this._model.id})}},_mousemoveHandler:function(e){if(!this._layouting){var t=this._pointsBuilder.getPointsMesh().dataIndex;t>=0?t!==this._lastMouseOverDataIndex&&this._api.dispatchAction({type:\"graphGLFocusNodeAdjacency\",seriesId:this._model.id,dataIndex:t}):this._mouseOutHandler(e),this._lastMouseOverDataIndex=t}},_mouseOutHandler:function(e){this._layouting||(this._api.dispatchAction({type:\"graphGLUnfocusNodeAdjacency\",seriesId:this._model.id}),this._lastMouseOverDataIndex=-1)},_updateForceEdgesGeometry:function(e,t){var r=this._forceEdgesMesh.geometry,i=t.getEdgeData(),n=0,a=this._forceLayoutInstance,o=2*i.count();r.attributes.node.init(o),r.attributes.color.init(o),i.each((function(t){var o=e[t];r.attributes.node.set(n,a.getNodeUV(o.node1)),r.attributes.node.set(n+1,a.getNodeUV(o.node2));var s=Ih(i,o.dataIndex),l=Ka.parseColor(s);l[3]*=Mn(Rh(i,o.dataIndex),1),r.attributes.color.set(n,l),r.attributes.color.set(n+1,l),n+=2})),r.dirty()},_updateMeshLinesGeometry:function(){var e=this._model.getEdgeData(),t=this._edgesMesh.geometry,r=(e=this._model.getEdgeData(),this._model.getData().getLayout(\"points\"));t.resetOffset(),t.setVertexCount(e.count()*t.getLineVertexCount()),t.setTriangleCount(e.count()*t.getLineTriangleCount());var i=[],n=[],a=[\"lineStyle\",\"width\"];this._originalEdgeColors=new Float32Array(4*e.count()),this._edgeIndicesMap=new Float32Array(e.count()),e.each((function(o){var s=e.graph.getEdgeByIndex(o),l=2*s.node1.dataIndex,h=2*s.node2.dataIndex;i[0]=r[l],i[1]=r[l+1],n[0]=r[h],n[1]=r[h+1];var u=Ih(e,s.dataIndex),c=Ka.parseColor(u);c[3]*=Mn(Rh(e,s.dataIndex),1);var d=e.getItemModel(s.dataIndex),f=Mn(d.get(a),1)*this._api.getDevicePixelRatio();t.addLine(i,n,c,f);for(var p=0;p<4;p++)this._originalEdgeColors[4*s.dataIndex+p]=c[p];this._edgeIndicesMap[s.dataIndex]=o}),this),t.dirty()},_updateForceNodesGeometry:function(e){for(var t=this._pointsBuilder.getPointsMesh(),r=[],i=0;i=f&&(l._syncNodePosition(e),d=0),r.getZr().refresh(),Qa((function(){p(t)}))}))};Qa((function(){l._forceLayoutInstanceToDispose&&(l._forceLayoutInstanceToDispose.dispose(n.layer.renderer),l._forceLayoutInstanceToDispose=null),p(h)})),this._layouting=!0}}},stopLayout:function(e,t,r,i){i&&null!=i.from&&i.from!==this.uid||(this._layoutId=0,this.groupGL.remove(this._forceEdgesMesh),this.groupGL.add(this._edgesMesh),this._forceLayoutInstance&&this.viewGL.layer&&(i&&i.beforeLayout||(this._syncNodePosition(e),this._updateAfterLayout(e,t,r)),this._api.getZr().refresh(),this._layouting=!1))},_syncNodePosition:function(e){var t=this._forceLayoutInstance.getNodePosition(this.viewGL.layer.renderer);e.getData().setLayout(\"points\",t),e.setNodePosition(t)},_updateAfterLayout:function(e,t,r){this._updateMeshLinesGeometry(),this._pointsBuilder.removePositionTexture(),this._pointsBuilder.updateLayout(e,t,r),this._pointsBuilder.updateView(this.viewGL.camera),this._pointsBuilder.updateLabels(),this._pointsBuilder.showLabels()},focusNodeAdjacency:function(e,t,r,i){var n=this._model.getData();this._downplayAll();var a=i.dataIndex,o=n.graph,s=[],l=o.getNodeByIndex(a);s.push(l),l.edges.forEach((function(e){e.dataIndex<0||(e.node1!==l&&s.push(e.node1),e.node2!==l&&s.push(e.node2))}),this),this._pointsBuilder.fadeOutAll(.05),this._fadeOutEdgesAll(.05),s.forEach((function(e){this._pointsBuilder.highlight(n,e.dataIndex)}),this),this._pointsBuilder.updateLabels(s.map((function(e){return e.dataIndex})));var h=[];l.edges.forEach((function(e){e.dataIndex>=0&&(this._highlightEdge(e.dataIndex),h.push(e))}),this),this._focusNodes=s,this._focusEdges=h},unfocusNodeAdjacency:function(e,t,r,i){this._downplayAll(),this._pointsBuilder.fadeInAll(),this._fadeInEdgesAll(),this._pointsBuilder.updateLabels()},_highlightEdge:function(e){var t=this._model.getEdgeData().getItemModel(e),r=Ka.parseColor(t.get(\"emphasis.lineStyle.color\")||t.get(\"lineStyle.color\")),i=Mn(t.get(\"emphasis.lineStyle.opacity\"),t.get(\"lineStyle.opacity\"),1);r[3]*=i,this._edgesMesh.geometry.setItemColor(this._edgeIndicesMap[e],r)},_downplayAll:function(){this._focusNodes&&this._focusNodes.forEach((function(e){this._pointsBuilder.downplay(this._model.getData(),e.dataIndex)}),this),this._focusEdges&&this._focusEdges.forEach((function(e){this._downplayEdge(e.dataIndex)}),this)},_downplayEdge:function(e){var t=this._getColor(e,[]);this._edgesMesh.geometry.setItemColor(this._edgeIndicesMap[e],t)},_setEdgeFade:(tf=[],function(e,t){this._getColor(e,tf),tf[3]*=t,this._edgesMesh.geometry.setItemColor(this._edgeIndicesMap[e],tf)}),_getColor:function(e,t){for(var r=0;r<4;r++)t[r]=this._originalEdgeColors[4*e+r];return t},_fadeOutEdgesAll:function(e){this._model.getData().graph.eachEdge((function(t){this._setEdgeFade(t.dataIndex,e)}),this)},_fadeInEdgesAll:function(){this._fadeOutEdgesAll(1)},_updateCamera:function(e,t){this.viewGL.setViewport(0,0,t.getWidth(),t.getHeight(),t.getDevicePixelRatio());for(var r=this.viewGL.camera,i=e.getData().getLayout(\"points\"),n=Jd.create(1/0,1/0),a=Jd.create(-1/0,-1/0),o=[],s=0;sr.left&&hr.top)){var u=Math.max(a[0]-n[0],10),c=u/t.getWidth()*t.getHeight();u*=1.4,c*=1.4,n[0]-=.2*u,r.left=n[0],r.top=l-c/2,r.bottom=l+c/2,r.right=u+n[0],r.near=0,r.far=100}},dispose:function(){var e=this.viewGL.layer.renderer;this._forceLayoutInstance&&this._forceLayoutInstance.dispose(e),this.groupGL.removeAll(),this._layoutId=-1,this._pointsBuilder.dispose()},remove:function(){this.groupGL.removeAll(),this._control.dispose()}});var tf;function rf(e){return e instanceof Array||(e=[e,e]),e}(0,i.use)((function(e){function t(){}e.registerChartView(ef),e.registerSeriesModel(Gd),e.registerVisual((function(e){const t={};e.eachSeriesByType(\"graphGL\",(function(e){var r=e.getCategoriesData(),n=e.getData(),a={};r.each((function(i){var n=r.getName(i);a[\"ec-\"+n]=i;var o=r.getItemModel(i),s=o.getModel(\"itemStyle\").getItemStyle();s.fill||(s.fill=e.getColorFromPalette(n,t)),r.setItemVisual(i,\"style\",s);var l=[\"symbol\",\"symbolSize\",\"symbolKeepAspect\"];for(let e=0;e65535?new Uint32Array(3*i):new Uint16Array(3*i))},addLine:function(e){var t=this._vertexOffset;this.attributes.position.set(t,[e[0],e[1],1]),this.attributes.position.set(t+1,[e[0],e[1],-1]),this.attributes.position.set(t+2,[e[0],e[1],2]),this.attributes.position.set(t+3,[e[0],e[1],-2]),this.setTriangleIndices(this._faceOffset++,[t,t+1,t+2]),this.setTriangleIndices(this._faceOffset++,[t+1,t+2,t+3]),this._vertexOffset+=4}});Xe.import(\"@export ecgl.vfParticle.particle.fragment\\n\\nuniform sampler2D particleTexture;\\nuniform sampler2D spawnTexture;\\nuniform sampler2D velocityTexture;\\n\\nuniform float deltaTime;\\nuniform float elapsedTime;\\n\\nuniform float speedScaling : 1.0;\\n\\nuniform vec2 textureSize;\\nuniform vec4 region : [0, 0, 1, 1];\\nuniform float firstFrameTime;\\n\\nvarying vec2 v_Texcoord;\\n\\n\\nvoid main()\\n{\\n vec4 p = texture2D(particleTexture, v_Texcoord);\\n bool spawn = false;\\n if (p.w <= 0.0) {\\n p = texture2D(spawnTexture, fract(v_Texcoord + elapsedTime / 10.0));\\n p.w -= firstFrameTime;\\n spawn = true;\\n }\\n vec2 v = texture2D(velocityTexture, fract(p.xy * region.zw + region.xy)).xy;\\n v = (v - 0.5) * 2.0;\\n p.z = length(v);\\n p.xy += v * deltaTime / 10.0 * speedScaling;\\n p.w -= deltaTime;\\n\\n if (spawn || p.xy != fract(p.xy)) {\\n p.z = 0.0;\\n }\\n p.xy = fract(p.xy);\\n\\n gl_FragColor = p;\\n}\\n@end\\n\\n@export ecgl.vfParticle.renderPoints.vertex\\n\\n#define PI 3.1415926\\n\\nattribute vec2 texcoord : TEXCOORD_0;\\n\\nuniform sampler2D particleTexture;\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\n\\nuniform float size : 1.0;\\n\\nvarying float v_Mag;\\nvarying vec2 v_Uv;\\n\\nvoid main()\\n{\\n vec4 p = texture2D(particleTexture, texcoord);\\n\\n if (p.w > 0.0 && p.z > 1e-5) {\\n gl_Position = worldViewProjection * vec4(p.xy * 2.0 - 1.0, 0.0, 1.0);\\n }\\n else {\\n gl_Position = vec4(100000.0, 100000.0, 100000.0, 1.0);\\n }\\n\\n v_Mag = p.z;\\n v_Uv = p.xy;\\n\\n gl_PointSize = size;\\n}\\n\\n@end\\n\\n@export ecgl.vfParticle.renderPoints.fragment\\n\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\nuniform sampler2D gradientTexture;\\nuniform sampler2D colorTexture;\\nuniform sampler2D spriteTexture;\\n\\nvarying float v_Mag;\\nvarying vec2 v_Uv;\\n\\nvoid main()\\n{\\n gl_FragColor = color;\\n#ifdef SPRITETEXTURE_ENABLED\\n gl_FragColor *= texture2D(spriteTexture, gl_PointCoord);\\n if (color.a == 0.0) {\\n discard;\\n }\\n#endif\\n#ifdef GRADIENTTEXTURE_ENABLED\\n gl_FragColor *= texture2D(gradientTexture, vec2(v_Mag, 0.5));\\n#endif\\n#ifdef COLORTEXTURE_ENABLED\\n gl_FragColor *= texture2D(colorTexture, v_Uv);\\n#endif\\n}\\n\\n@end\\n\\n@export ecgl.vfParticle.renderLines.vertex\\n\\n#define PI 3.1415926\\n\\nattribute vec3 position : POSITION;\\n\\nuniform sampler2D particleTexture;\\nuniform sampler2D prevParticleTexture;\\n\\nuniform float size : 1.0;\\nuniform vec4 vp: VIEWPORT;\\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\\n\\nvarying float v_Mag;\\nvarying vec2 v_Uv;\\n\\n@import clay.util.rand\\n\\nvoid main()\\n{\\n vec4 p = texture2D(particleTexture, position.xy);\\n vec4 p2 = texture2D(prevParticleTexture, position.xy);\\n\\n p.xy = p.xy * 2.0 - 1.0;\\n p2.xy = p2.xy * 2.0 - 1.0;\\n\\n if (p.w > 0.0 && p.z > 1e-5) {\\n vec2 dir = normalize(p.xy - p2.xy);\\n vec2 norm = vec2(dir.y / vp.z, -dir.x / vp.w) * sign(position.z) * size;\\n if (abs(position.z) == 2.0) {\\n gl_Position = vec4(p.xy + norm, 0.0, 1.0);\\n v_Uv = p.xy;\\n v_Mag = p.z;\\n }\\n else {\\n gl_Position = vec4(p2.xy + norm, 0.0, 1.0);\\n v_Mag = p2.z;\\n v_Uv = p2.xy;\\n }\\n gl_Position = worldViewProjection * gl_Position;\\n }\\n else {\\n gl_Position = vec4(100000.0, 100000.0, 100000.0, 1.0);\\n }\\n}\\n\\n@end\\n\\n@export ecgl.vfParticle.renderLines.fragment\\n\\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\\nuniform sampler2D gradientTexture;\\nuniform sampler2D colorTexture;\\n\\nvarying float v_Mag;\\nvarying vec2 v_Uv;\\n\\nvoid main()\\n{\\n gl_FragColor = color;\\n #ifdef GRADIENTTEXTURE_ENABLED\\n gl_FragColor *= texture2D(gradientTexture, vec2(v_Mag, 0.5));\\n#endif\\n#ifdef COLORTEXTURE_ENABLED\\n gl_FragColor *= texture2D(colorTexture, v_Uv);\\n#endif\\n}\\n\\n@end\\n\");var of=function(){this.motionBlurFactor=.99,this.vectorFieldTexture=new Dr({type:wr.FLOAT,flipY:!1}),this.particleLife=[5,20],this._particleType=\"point\",this._particleSize=1,this.particleColor=[1,1,1,1],this.particleSpeedScaling=1,this._thisFrameTexture=null,this._particlePass=null,this._spawnTexture=null,this._particleTexture0=null,this._particleTexture1=null,this._particlePointsMesh=null,this._surfaceFrameBuffer=null,this._elapsedTime=0,this._scene=null,this._camera=null,this._lastFrameTexture=null,this._supersampling=1,this._downsampleTextures=[],this._width=512,this._height=512,this.init()};of.prototype={constructor:of,init:function(){var e={type:wr.FLOAT,minFilter:wr.NEAREST,magFilter:wr.NEAREST,useMipmap:!1};this._spawnTexture=new Dr(e),this._particleTexture0=new Dr(e),this._particleTexture1=new Dr(e),this._frameBuffer=new zi({depthBuffer:!1}),this._particlePass=new pn({fragment:Xe.source(\"ecgl.vfParticle.particle.fragment\")}),this._particlePass.setUniform(\"velocityTexture\",this.vectorFieldTexture),this._particlePass.setUniform(\"spawnTexture\",this._spawnTexture),this._downsamplePass=new pn({fragment:Xe.source(\"clay.compositor.downsample\")});var t=new Sr({renderOrder:10,material:new le({shader:new Xe(Xe.source(\"ecgl.vfParticle.renderPoints.vertex\"),Xe.source(\"ecgl.vfParticle.renderPoints.fragment\"))}),mode:Sr.POINTS,geometry:new Vr({dynamic:!0,mainAttribute:\"texcoord0\"})}),r=new Sr({renderOrder:10,material:new le({shader:new Xe(Xe.source(\"ecgl.vfParticle.renderLines.vertex\"),Xe.source(\"ecgl.vfParticle.renderLines.fragment\"))}),geometry:new af,culling:!1}),i=new Sr({material:new le({shader:new Xe(Xe.source(\"ecgl.color.vertex\"),Xe.source(\"ecgl.color.fragment\"))}),geometry:new ki});i.material.enableTexture(\"diffuseMap\"),this._particlePointsMesh=t,this._particleLinesMesh=r,this._lastFrameFullQuadMesh=i,this._camera=new un,this._thisFrameTexture=new Dr,this._lastFrameTexture=new Dr},setParticleDensity:function(e,t){for(var r=new Float32Array(e*t*4),i=0,n=this.particleLife,a=0;a0?e[e.length-1]:this._lastFrameTexture},setRegion:function(e){this._particlePass.setUniform(\"region\",e)},resize:function(e,t){this._lastFrameTexture.width=e*this._supersampling,this._lastFrameTexture.height=t*this._supersampling,this._thisFrameTexture.width=e*this._supersampling,this._thisFrameTexture.height=t*this._supersampling,this._width=e,this._height=t},setParticleSize:function(e){var t=this._getParticleMesh();if(e<=2)return t.material.disableTexture(\"spriteTexture\"),void(t.material.transparent=!1);this._spriteTexture||(this._spriteTexture=new Dr),this._spriteTexture.image&&this._spriteTexture.image.width===e||(this._spriteTexture.image=function(e){var t=document.createElement(\"canvas\");t.width=t.height=e;var r=t.getContext(\"2d\");return r.fillStyle=\"#fff\",r.arc(e/2,e/2,e/2,0,2*Math.PI),r.fill(),t}(e),this._spriteTexture.dirty()),t.material.transparent=!0,t.material.enableTexture(\"spriteTexture\"),t.material.set(\"spriteTexture\",this._spriteTexture),this._particleSize=e},setGradientTexture:function(e){var t=this._getParticleMesh().material;t[e?\"enableTexture\":\"disableTexture\"](\"gradientTexture\"),t.setUniform(\"gradientTexture\",e)},setColorTextureImage:function(e,t){this._getParticleMesh().material.setTextureImage(\"colorTexture\",e,t,{flipY:!0})},setParticleType:function(e){this._particleType=e},clearFrame:function(e){var t=this._frameBuffer;t.attach(this._lastFrameTexture),t.bind(e),e.gl.clear(e.gl.DEPTH_BUFFER_BIT|e.gl.COLOR_BUFFER_BIT),t.unbind(e)},setSupersampling:function(e){this._supersampling=e,this.resize(this._width,this._height)},_updateDownsampleTextures:function(e,t){for(var r=this._downsampleTextures,i=Math.max(Math.floor(Math.log(this._supersampling/t.getDevicePixelRatio())/Math.log(2)),0),n=2,a=this._width*this._supersampling,o=this._height*this._supersampling,s=0;s=359&&(n[0]>0&&(n[0]=0),a[0]1?(t.material.shader!==this._meshLinesShader&&t.material.attachShader(this._meshLinesShader),t.mode=Ka.Mesh.TRIANGLES):(t.material.shader!==this._nativeLinesShader&&t.material.attachShader(this._nativeLinesShader),t.mode=Ka.Mesh.LINES),r=r||0,i=i||n.count(),s.resetOffset();var u=0,c=0,d=[],f=[],p=[],m=[],g=[],_=.3,v=.7;function y(){f[0]=d[0]*v+m[0]*_-(d[1]-m[1])*a,f[1]=d[1]*v+m[1]*_-(m[0]-d[0])*a,p[0]=d[0]*_+m[0]*v-(d[1]-m[1])*a,p[1]=d[1]*_+m[1]*v-(m[0]-d[0])*a}if(o||0!==a)for(var x=r;x{t.exports=e}},r={};function i(e){if(r[e])return r[e].exports;var n=r[e]={exports:{}};return t[e](n,n.exports,i),n.exports}return i.g=function(){if(\"object\"==typeof globalThis)return globalThis;try{return this||new Function(\"return this\")()}catch(e){if(\"object\"==typeof window)return window}}(),i.r=e=>{\"undefined\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(e,\"__esModule\",{value:!0})},i(468)})()}));", "查询列表 参数": "Query list parameters", "模型列表": "Model list", "选中的模型": "selected model", "获取table列表数据": "Get table list data", "echarts表格": "echarts form", "新增模型 btn": "New model btn", "编辑模型 btn": "Edit model btn", "删除模型": "Delete model", "btn 批量删除": "btn batch delete", "循环 this.multipleSelection": "Loop this.multipleSelection", "async 获取table列表数据": "async gets table list data", "to do 对接接口 queryList": "to do docking interface queryList", "table 自定义行号": "table custom row number", "table 选中列": "table selected column", "按照 页码 记录对应页选择的数量": "Record the number of selected pages according to the page number", "分页 页大小": "Pagination page size", "分页 页码": "Pagination Page number", "* OrbitControls - 简化版相机控制器": "* OrbitControls - simplified camera controller", "* 专为 PromptRange 3D 地图优化": "* Optimized specifically for PromptRange 3D maps", "状态枚举": "Status enum", "* OrbitControls 构造函数": "* OrbitControls constructor", "* @param {THREE.Camera} camera - 相机对象": "* @param {THREE.Camera} camera - camera object", "* @param {HTMLElement} domElement - DOM 元素": "* @param {HTMLElement} domElement - DOM element", "公共配置": "public configuration", "内部状态": "internal state", "球坐标系": "Spherical coordinate system", "鼠标状态": "Mouse status", "更新函数": "update function", "旋转偏移到 \"y-axis-is-up\" 空间": "Rotation offset to \"y-axis-is-up\" space", "从笛卡尔坐标转换到球坐标": "Convert from Cartesian coordinates to spherical coordinates", "限制 phi 在安全范围内": "Limit phi to a safe range", "限制半径在 minDistance 和 maxDistance 之间": "Limit the radius to between minDistance and maxDistance", "移动目标以平移": "Move target to pan", "从球坐标转换回笛卡尔坐标": "Convert from spherical coordinates back to Cartesian coordinates", "旋转偏移回 \"camera-up-vector-is-up\" 空间": "Rotate offset back to \"camera-up-vector-is-up\" space", "检查是否需要更新": "Check if updates are needed", "旋转左(水平)": "Rotate left (horizontal)", "旋转上(垂直)": "Rotate up (vertical)", "缩放": "Zoom", "平移": "Pan", "获取 X 列": "Get column X", "获取 Y 列": "Get column Y", "透视相机": "perspective camera", "根据视场角的一半": "According to half of the field of view", "我们实际使用屏幕高度来做所有计算": "We actually use the screen height to do all calculations", "所以不需要改变平移的方向": "So there is no need to change the direction of translation", "鼠标事件处理": "Mouse event handling", "事件监听器": "event listener", "左键 - 旋转": "Left click - Rotate", "中键 - 缩放": "Middle click - zoom", "右键 - 平移": "Right click - pan", "绑定事件": "Binding events", "销毁方法": "Destruction method", "初始化": "initialization", "设置原型": "Set up prototype", "将 ID 列表作为请求体数据发送": "Send a list of IDs as request body data", "点击搜索": "Click to search", "优化功能": "Optimization function", "优化弹窗内进度说明(Agent → 刷新 → 打靶 → 评分)": "Optimize the progress description in the pop-up window (Agent → Refresh → Target Practice → Rating)", "优化失败时保留在弹窗内供开发者查看": "When optimization fails, it will remain in the pop-up window for developers to view.", "🆕 创建后立即打靶(默认选中)": "🆕 Target shooting immediately after creation (selected by default)", "🆕 打靶后 AI 评分(默认不选中)": "🆕 AI scoring after target shooting (not selected by default)", "PromptCatalyzer 初始化功能": "PromptCatalyzer initialization function", "初始化对话框可见性": "Initialize dialog visibility", "可用的 AI Model 列表": "List of available AI Models", "选中的 Model ID": "Selected Model ID", "加载 Model 列表的状态": "The status of loading the Model list", "正在初始化的状态": "Initializing state", "页面是否有变化": "Is there any change to the page?", "是否平均分 默认false 不平均": "Whether to score equally, default false, not evenly", "配置 输入 ---start": "Configuration input ---start", "靶场列表": "Range list", "prompt列表": "prompt list", "是否等待刷新模型列表": "Whether to wait to refresh the model list", "选择靶场": "Choose a shooting range", "选择模型": "Select model", "prompt 输入内容": "prompt input content", "prompt 输入的备注": "prompt input remarks", "是否正在使用输入法(IME composition)": "Whether input method (IME composition) is being used", "prompt 的连发次数(发射次数) 1-10": "Number of bursts of prompt (number of launches) 1-10", "参数设置 视图配置列表": "Parameter settings View configuration list", "prompt左侧区域整体 显隐": "The entire area on the left side of the prompt is shown and hidden.", "模型参数设置 显隐 false是默认显示 trun是隐藏": "Model parameter settings show and hide false is displayed by default trun is hidden", "打靶loading": "Target shooting loading", "连发loading": "burst loading", "配置 输入 ---end": "Configure input ---end", "prompt请求参数 ---start": "prompt request parameters ---start", "prompt请求参数 显隐 true是显示 false是默认隐藏": "Prompt request parameters show/hide true means display false means hide by default", "prompt请求参数 ---end": "prompt request parameters ---end", "sendBtns: 打靶、连发、保存草稿": "sendBtns: target practice, burst, save draft", "输出 ---start": "Output ---start", "输出列表的平均分": "Average score of output list", "输出列表的最高分": "Output the highest score of the list", "输出列表选中查看|评分": "Select the output list to view|score", "输出列表": "Output list", "AI评分加载状态映射 {itemId: true/false}": "AI rating loading status mapping {itemId: true/false}", "图表数据": "chart data", "图表实例": "Chart example", "输出 ---end": "Output ---end", "版本记录 ---start": "Version record ---start", "抽屉": "drawer", "版本搜索": "Version search", "版本记录 ---end": "Version record ---end", "战术选择": "Tactical options", "对话模式/直接测试,默认对话模式": "Conversation mode/direct testing, default conversation mode", "战术选择弹窗中的对话输入": "Dialogue input in tactical selection pop-up window", "对话模式下的用户输入内容": "User input in conversation mode", "继续聊天相关状态": "Continue chatting related status", "是否处于继续聊天模式": "Whether in continue chat mode", "继续聊天的 PromptResult ID": "PromptResult ID to continue chatting with", "继续聊天的历史记录": "Continue chat history", "继续聊天时使用的 SystemMessage(Prompt)": "SystemMessage(Prompt) used when continuing chat", "SystemMessage 折叠面板状态": "SystemMessage accordion status", "导图相关状态": "Map related status", "导图对话框显示状态": "Map dialog display status", "three.js 场景": "three.js scene", "three.js 相机": "three.js camera", "three.js 渲染器": "three.js renderer", "相机控制器": "camera controller", "3D 节点数组": "3D node array", "树状结构数据": "tree structure data", "点击事件处理器": "Click event handler", "动画ID": "Animation ID", "是否需要更新动画": "Do you need to update the animation?", "节点映射,用于快速查找": "Node mapping for quick lookups", "上次动画更新时间(用于节流)": "Last animation update time (for throttling)", "缓存当前选中的节点(性能优化)": "Cache the currently selected node (performance optimization)", "ai 评分标准": "ai scoring criteria", "模型": "Model", "表单校验规则": "Form validation rules", "Plugin 上传dialog 显隐": "Plugin upload dialog to show and hide", "上传区域显隐": "Upload area visible and hidden", "拖拽文件 Hover": "Drag and drop files Hover", "Plugin 文件夹的文件列表": "File list of Plugin folder", "压缩实例": "Compressed instance", "Plugin 导出dialog 显隐": "Plugin exports dialog to show or hide", "导出 Plugin 选择数据": "Export Plugin selection data", "选择的数据 tree": "Selected data tree", "是否展开所有节点": "Whether to expand all nodes", "已选择的靶道数量": "Number of target lanes selected", "prompt 输入框的行数": "prompt input box number of lines", "中间区域是否最大化": "Whether the middle area is maximized", "右侧区域是否最大化": "Whether the right area is maximized", "控制盒子显示和隐藏的状态": "Control the display and hidden state of the box", "区域宽度控制": "Area width control", "左侧区域宽度(默认360px)": "Left area width (default 360px)", "中间区域宽度(默认380px)": "Middle area width (default 380px)", "是否正在拖动": "Is dragging", "拖动类型:'left' 或 'right'": "Drag type: 'left' or 'right'", "拖动开始的X坐标": "The X coordinate where the drag starts", "拖动开始时左侧区域的宽度": "The width of the left area when dragging starts", "拖动开始时中间区域的宽度": "The width of the middle area when dragging starts", "Prompt 对比功能": "Prompt comparison function", "对比对话框显示状态": "Compare dialog display status", "对比的Prompt A的ID": "The ID of Prompt A compared", "对比的Prompt B的ID": "Compare the ID of Prompt B", "对比的Prompt A的完整数据": "Comparative complete data of Prompt A", "对比的Prompt B的完整数据": "Complete data of Prompt B compared", "自定义滚动条缩略图": "Custom scroll bar thumbnail", "获取可选择的Prompt列表(用于对比对话框)": "Get a list of selectable prompts (used in comparison dialogs)", "获取Prompt A的显示信息": "Get the display information of Prompt A", "从 fullVersion 解析名称 (格式: 靶场-靶道-战术)": "Parse name from fullVersion (format: range-range-tactical)", "获取Prompt B的显示信息": "Get the display information of Prompt B", "检查是否是同一个Prompt": "Check if it is the same prompt", "通过ID判断是否为同一个Prompt": "Determine whether it is the same prompt by ID", "获取当前战术编号(用于显示)": "Get the current tactical number (for display)", "解析当前版本号:格式为 RangeName-T{Tactic}-A{Aiming}": "Parse the current version number: the format is RangeName-T{Tactic}-A{Aiming}", "例如:2023.12.14.1-T1.2.3-A1": "For example: 2023.12.14.1-T1.2.3-A1", "versionParts[0] = RangeName (例如:2023.12.14.1)": "versionParts[0] = RangeName (for example: 2023.12.14.1)", "versionParts[1] = T{Tactic} (例如:T1.2.3)": "versionParts[1] = T{Tactic} (for example: T1.2.3)", "versionParts[2] = A{Aiming} (例如:A1,可选)": "versionParts[2] = A{Aiming} (for example: A1, optional)", "计算下一个战术编号(用于战术选择弹窗的动态提示)": "Calculate the next tactic number (dynamic prompt for tactic selection pop-up window)", "检查战术部分是否以 T 开头,如果不是,可能是格式错误": "Check if the tactics section starts with T, if not it may be a formatting error", "如果版本号格式不对,返回默认值": "If the format of the version number is incorrect, the default value will be returned.", "提取瞄准编号:A1 -> 1": "Extract aiming number: A1 -> 1", "解析战术编号:T1.2.3 -> [1, 2, 3]": "Analyze tactical number: T1.2.3 -> [1, 2, 3]", "移除开头的 T,然后按 . 分割并转换为数字": "Remove the leading T, then split by . and convert to numbers", "如果无法解析战术编号,返回默认值": "If the tactical number cannot be parsed, return the default value", "解析战术编号数组": "Parse tactical number array", "如果任何部分无法解析为数字,返回默认值": "If any part cannot be parsed as a number, return the default value", "如果解析失败,返回默认值": "If parsing fails, return the default value", "1. 创建顶级战术:当前顶级编号+1": "1. Create top-level tactics: current top-level number +1", "例如:当前是T1.2.3,下一个顶级战术是T2": "For example: currently it is T1.2.3, the next top tactic is T2", "注意:这里只是预测,实际编号需要后端查询数据库确定": "Note: This is just a prediction, the actual number needs to be determined by back-end query database", "2. 创建平行战术:同父级下,最后一个编号+1": "2. Create parallel tactics: under the same parent level, the last number +1", "例如:T1.2.3 -> T1.2.4": "For example: T1.2.3 -> T1.2.4", "3. 创建子战术:当前战术下添加 .1": "3. Create a sub-tactic: add .1 under the current tactic", "例如:T1.2.3 -> T1.2.3.1": "For example: T1.2.3 -> T1.2.3.1", "4. 重新瞄准:当前瞄准编号+1": "4. Re-aim: Current aiming number +1", "例如:T1.2.3-A1 -> T1.2.3-A2": "For example: T1.2.3-A1 -> T1.2.3-A2", "检测Prompt中的变量": "Detect variables in Prompt", "如果没有设置前缀和后缀,返回空数组": "If no prefix or suffix is ​​set, an empty array is returned.", "转义前缀和后缀用于正则表达式": "Escape prefixes and suffixes for regular expressions", "构建正则表达式:匹配 prefix + 变量名 + suffix": "Build a regular expression: match prefix + variable name + suffix", "获取所有已定义的变量名": "Get all defined variable names", "找出所有匹配的变量": "Find all matching variables", "避免重复": "avoid duplication", "监听content外部变化(如加载数据时更新编辑器)": "Monitor external content changes (such as updating the editor when loading data)", "如果编辑器内容与content不一致(外部赋值),更新编辑器": "If the editor content is inconsistent with content (external assignment), update the editor", "浏览器关闭|浏览器刷新|页面关闭|打开新页面 提示有数据变动保存数据": "Close the browser | Refresh the browser | Close the page | Open a new page Prompt for data changes to save data", "添加 beforeunload 事件监听器": "Add beforeunload event listener", "页面创建时加载保存的宽度设置": "Load saved width settings on page creation", "获取靶道列表": "Get target lane list", "获取分数趋势图": "Get score trend graph", "图表自适应": "Chart adaptive", "加个if约束,当Echarts存在时再执行resize(),否则图表不存在时运行到这会报错。": "Add an if constraint and execute resize() when Echarts exists. Otherwise, an error will be reported when the chart does not exist.", "初始化contenteditable编辑器": "Initialize contenteditable editor", "初始化代码块复制功能": "Initialize code block copy function", "销毁之前移除事件监听器": "Remove event listeners before destroying", "组件销毁前移除拖动相关的事件监听器": "Remove drag-related event listeners before component destruction", "获取路径id 页面数据回显": "Get the path id page data echo", "添加安全检查,防止 $route 未定义": "Add security check to prevent $route from being undefined", "获取分数趋势图表数据": "Get score trend chart data", "侧边栏收起操作": "Sidebar collapse operation", "放大输入区域": "Enlarge input area", "再次点击同一个区域,恢复所有区域": "Click the same area again to restore all areas", "点击不同区域,实现最大化": "Click on different areas to maximize", "右侧输出区域最大化:隐藏中间区域,隐藏分析图表,扩展右侧区域": "Maximize the right output area: hide the middle area, hide the analysis chart, expand the right area", "隐藏中间Prompt区域": "Hide the middle prompt area", "隐藏分析图表": "Hide analysis chart", "中间Prompt区域最大化:隐藏右侧所有内容,扩展中间区域": "Maximize the middle prompt area: hide all content on the right and expand the middle area", "隐藏右侧输出区域": "Hide the right output area", "保留原有逻辑": "Keep original logic", "靶场删除": "range delete", "阻止事件冒泡": "Prevent events from bubbling up", "弹出提示框": "Pop up prompt box", "重新获取靶场列表": "Retrieve range list", "重置页面数据": "Reset page data", "删除靶道": "Delete target lane", "备注失去焦点 保存": "Note loses focus Save", "弹出提示框,输入新的靶场名称,确认后提交,取消后,提示已取消操作": "A prompt box will pop up, enter the new shooting range name, confirm and submit. After cancelling, it will prompt that the operation has been cancelled.", "this.$prompt('请输入新的靶道名称', '提示', {": "this.$prompt('Please enter the new target name', 'Prompt', {", "confirmButtonText: '确定',": "confirmButtonText: 'OK',", "cancelButtonText: '取消',": "cancelButtonText: 'Cancel',", "inputErrorMessage: '靶道名称不能为空',": "inputErrorMessage: 'Target channel name cannot be empty',", "message: '已取消操作'": "message: 'Operation canceled'", "重置靶道名称": "Reset target lane name", "靶道 名称弹窗": "Target lane name popup", "修改靶道 名称弹窗 确认操作": "Modify target lane name pop-up window to confirm the operation", "如果找到了“名称:”和“| 版本号:”,则提取它们之间的文本": "If \"name:\" and \"|version number:\" are found, extract the text between them", "如果没有找到“名称:”或“| 版本号:”,则执行备用逻辑": "If \"name:\" or \"|version number:\" is not found, then perform fallback logic", "获取靶道弹窗input列表 过滤没有名字的靶道": "Get the target lane pop-up window input list and filter the target lanes without names.", "调用 callback 返回建议列表的数据": "Call callback to return the data of the suggestion list", "战术选择 dialog 提交": "Tactics selection dialog submission", "如果是继续聊天模式,直接处理,不需要验证战术字段": "If you continue in chat mode, handle it directly without verifying the tactical fields.", "检查是否有输入内容": "Check if there is any input", "调用继续聊天 API": "Call continue chat API", "普通模式,需要验证表单": "Normal mode, need to verify the form", "注意:第一次打靶时(没有promptid),不需要验证\"战术\"字段,因为会自动创建T1-A1靶道": "Note: When shooting for the first time (without promptid), there is no need to verify the \"tactics\" field, because the T1-A1 target lane will be automatically created", "先检查\"打靶测试\"字段是否已选择": "First check whether the \"Target Test\" field is selected", "如果有 promptid,需要验证\"战术\"字段": "If there is promptid, the \"tactics\" field needs to be verified", "如果选择对话模式,需要检查是否有输入内容": "If you choose conversation mode, you need to check whether there is input content", "执行打靶,将输入内容作为 userMessage 传递": "Perform target practice, passing the input as userMessage", "清空对话输入": "Clear conversation input", "直接测试模式,继续原有流程": "Direct test mode and continue the original process", "继续聊天提交": "Continue Chat Submit", "验证新消息是否有有效的 ID": "Verify that new messages have valid IDs", "如果消息没有 ID,重新加载历史记录以确保获取正确的 ID": "If the message does not have an ID, reload the history to ensure you get the correct ID", "验证重新加载后的记录是否都有有效的 ID": "Verify that the reloaded records all have valid IDs", "追加新的对话记录到历史记录": "Append new conversations to history", "找到对应的输出项并更新": "Find the corresponding output item and update it", "更新显示:将最新的 AI 回复追加到 ResultString": "Update display: Append latest AI reply to ResultString", "追加到现有的 ResultString(格式化为对话形式)": "Append to existing ResultString (formatted as conversational)", "刷新对话历史显示": "Refresh conversation history display", "添加代码块复制按钮": "Add code block copy button", "滚动到底部显示最新消息": "Scroll to bottom for latest news", "清空输入框,但保持弹窗打开以便继续对话": "Clear the input box but keep the popup open to continue the conversation", "* 打靶 事件": "* Target shooting event", "* isDraft 是否保存草稿": "* isDraft whether to save the draft", "弹窗逻辑1,有promptid且不是保存草稿,就要弹窗": "Pop-up window logic 1, if there is a promptid and the draft is not saved, a pop-up window will appear.", "弹窗逻辑2,有promptid且不是保存草稿,就要弹窗": "Pop-up window logic 2, if there is a promptid and the draft is not saved, a pop-up window will appear.", "弹窗逻辑3,第一次打靶时(没有promptid)也要弹窗,让用户选择对话模式或直接测试模式": "Pop-up window logic 3: A pop-up window should also pop up when shooting for the first time (without promptid), allowing the user to choose conversation mode or direct test mode", "注意:第一次打靶时不传递promptid,让后端创建新的靶道(T1-A1)": "Note: Do not pass promptid when shooting for the first time, let the backend create a new target lane (T1-A1)", "promptid: this.promptid,// 选择靶场": "promptid: this.promptid,//Select the shooting range", "prompt 输入的备注,": "prompt input remarks,", "ai评分标准": "ai scoring criteria", "请求参数": "Request parameters", "todo 单独处理": "todo is handled separately", "要提交this.promptField": "To submit this.promptField", "提示保存成功": "Prompt to save successfully", "拷贝数据": "copy data", "1 ai、2手动": "1 ai, 2 manual", "是否显示评分视图": "Whether to display the rating view", "时间 格式化 addTime": "time format addTime", "手动评分": "Manual scoring", "ai评分预期结果": "ai scoring expected results", "提交数据后,选择正确的靶场和靶道": "After submitting your data, select the correct range and lane", "进入连发模式, 根据numOfResults-1 的数量调用N次连发接口": "Enter the burst mode and call the burst interface N times according to the number of numOfResults-1", "* 连发 事件": "* Continuous events", "注意:现在后端会自动根据第一个 PromptResult 来判断类型": "Note: Now the backend will automatically determine the type based on the first PromptResult", "如果第一个结果是 Chat 模式,后端会从对话记录中获取 userMessage": "If the first result is Chat mode, the backend will get the userMessage from the conversation record", "所以前端不需要传递 userMessage,后端会自动处理": "So the front end does not need to pass userMessage, the back end will handle it automatically.", "但为了兼容性,如果前端有保存的 userMessage,仍然可以传递": "But for compatibility, if the front end has a saved userMessage, it can still be passed", "后端会自动判断第一个结果的模式,不需要前端传递 userMessage": "The backend will automatically determine the mode of the first result, and the frontend does not need to pass userMessage", "但如果前端有保存的 userMessage,可以传递以保持一致性": "But if the front end has a saved userMessage, it can be passed to maintain consistency", "从新获取靶场列表": "Retrieve shooting range list", "导入 plugins dialog close 回调": "Import plugins dialog close callback", "清空fileData": "Clear fileData", "导入 plugins 在拖动区来回拖拽时": "Import plugins when dragging back and forth in the drag area", "导入 plugins 第一次进入拖动区时": "Import plugins when entering the drag area for the first time", "导入 plugins 拖后放": "Import plugins drag and drop", "导入 plugins 拖拽 选择文件夹": "Import plugins drag and drop select folder", "FileSystemFileEntry 或 FileSystemDirectoryEntry 对象": "FileSystemFileEntry or FileSystemDirectoryEntry object", "递归地获取entry下包含的所有File": "Recursively obtain all files contained under entry", "拖拽上传 获取文件": "Drag and drop to upload files", "文件": "document", "想要保留拖拽的层级结构的话,只能从 entry 中获取": "If you want to retain the drag-and-drop hierarchy, you can only get it from entry", "取path是为了获取上传的文件夹一级的名称": "The purpose of taking path is to get the first-level name of the uploaded folder", "文件夹": "folder", "导入 plugins 点击 选择文件夹": "Import plugins Click Select Folder", "设置了webkitdirectory就可以选择文件夹进行上传了": "After setting up webkitdirectory, you can select a folder to upload.", "处理文件夹里的所有子文件": "Process all sub-files in a folder", "将上传的文件添加到压缩包中": "Add uploaded files to compressed package", "归类处理文件到指定的文件夹": "Categorize processed files into specified folders", "导入 plugins 上传按钮": "Import plugins upload button", "生成压缩文件": "Generate compressed file", "将blob类型的再转为file类型用于上传": "Convert blob type to file type for uploading", "做个大小限制": "Make a size limit", "message: \"上传文件大小不能超过 80MB!\",": "message: \"The uploaded file size cannot exceed 80MB!\",", "this.folderHandlesubmit(filedata); //上传事件,filedata已经是压缩好的文件了": "this.folderHandlesubmit(filedata); //Upload event, filedata is already a compressed file", "//saveAs(content, `${name}.zip`); //下载用,可以下载下来文件查看上传的是否正确": "//saveAs(content, `${name}.zip`); //For downloading, you can download the file to check whether the uploaded one is correct.", "上传事件,filedata已经是压缩好的文件了": "Upload event, filedata is already a compressed file", "saveAs(content, `${name}.zip`); //下载用,可以下载下来文件查看上传的是否正确": "saveAs(content, `${name}.zip`); //For downloading, you can download the file to check whether the uploaded one is correct.", "上传 Plugins api": "Upload Plugins api", "ajax上传formData": "ajax upload formData", "更新靶场数据": "Update range data", "导出 plugins dialog close 回调": "Export plugins dialog close callback", "tree数据列表": "tree data list", "重置已选择数量": "Reset selected quantity", "重置展开状态": "Reset expanded state", "导出 plugins dialog open": "export plugins dialog open", "获取树形数据 靶场列表": "Get tree data shooting range list", "等待 DOM 更新后再设置选中和更新计数": "Wait for DOM to update before setting selection and update counts", "设置默认选中": "Set default selected", "确保 checkList 只包含叶子节点": "Make sure checkList only contains leaf nodes", "只记录叶子节点(靶道)": "Only record leaf nodes (target lanes)", "更新已选择数量": "Update selected quantity", "导出 plugins dialog tree 选中变化": "Export plugins dialog tree selected changes", "更新已选择数量(使用 $nextTick 确保 Tree 状态已更新)": "Update the selected quantity (use $nextTick to ensure the Tree state is updated)", "使用 Tree API 重新获取所有选中的节点(只获取叶子节点)": "Use Tree API to re-fetch all selected nodes (only leaf nodes)", "更新 checkList,只包含叶子节点": "Update checkList to include only leaf nodes", "更新计数": "update count", "更新已选择的靶道数量": "Update the number of selected target lanes", "使用 Tree 组件的 API 获取所有选中的节点(包括半选状态的父节点的子节点)": "Use the API of the Tree component to get all selected nodes (including the child nodes of the parent node in the semi-selected state)", "统计所有选中的子节点(靶道)": "Count all selected child nodes (target lane)", "靶道的特征:有 idkey 且包含下划线,或者没有 children": "Characteristics of the target lane: There is an idkey and contains an underscore, or there is no children", "统计完全选中的节点中的靶道": "Count target lanes in fully selected nodes", "如果是叶子节点(靶道),统计": "If it is a leaf node (target channel), statistics", "对于半选状态的父节点,需要统计其已选中的子节点": "For a parent node in a half-selected state, it is necessary to count its selected child nodes.", "Element UI Tree 的 getCheckedNodes() 已经包含了所有选中的子节点,所以不需要额外处理": "Element UI Tree's getCheckedNodes() already contains all selected child nodes, so no additional processing is required.", "导出plugin - 全选": "Export plugin - select all", "获取所有节点的key(包括父节点和子节点)": "Get the keys of all nodes (including parent nodes and child nodes)", "设置选中": "Set selected", "等待 DOM 更新后,只记录叶子节点": "After waiting for the DOM to be updated, only the leaf nodes are recorded.", "导出plugin - 反选": "Export plugin - inverse selection", "收集所有叶子节点(靶道)的key": "Collect the keys of all leaf nodes (target lanes)", "这是叶子节点(靶道)": "This is the leaf node (target lane)", "这是父节点(靶场),继续递归": "This is the parent node (shooting range), continue the recursion", "获取当前选中的叶子节点key(使用 leafOnly=true 参数)": "Get the currently selected leaf node key (use leafOnly=true parameter)", "反选:所有叶子节点 - 当前选中的叶子节点": "Inverse selection: all leaf nodes - currently selected leaf nodes", "设置反选后的结果(只设置叶子节点)": "Set the result after inverse selection (only set leaf nodes)", "等待 DOM 更新后更新状态": "Update status after waiting for DOM update", "导出plugin - 清空选择": "Export plugin - clear selection", "等待 DOM 更新后再统计": "Wait for the DOM to be updated before counting", "导出plugin - 切换展开/收起": "Export plugin - toggle expand/collapse", "需要重新渲染树来应用展开状态": "The tree needs to be re-rendered to apply the expanded state", "导出 plugins 确认": "Export plugins Confirm", "console.log('导出 plugins 确认', this.expectedPluginFoem.checkList)": "console.log('Export plugins confirmation', this.expectedPluginFoem.checkList)", "导出 plugins": "Export plugins", "判断是否包含 - 如果包含就是靶道 否则就是靶场": "Determine whether it is included - if included, it is the target lane, otherwise it is the shooting range", "_rangeIds 和 _ids 去重": "_rangeIds and _ids deduplication", "设置下载的文件名,可以根据需要修改": "Set the downloaded file name, which can be modified as needed", "触发点击事件开始下载": "Trigger click event to start downloading", "下载完成后删除 标签": "Remove tag after download is complete", "释放 URL 对象": "Release the URL object", "从 DOM 中删除 标签": "Remove tag from DOM", "服务器返回错误响应": "Server returns error response", "ai 评分删除": "ai rating delete", "console.log('删除', index)": "console.log('delete', index)", "新增靶场": "Add new shooting range", "如果靶场变化 靶道": "If the range changes, the range", "连发次数 数量变化": "Number of bursts Quantity changes", "打靶按钮 类型切换": "Target button type switching", "打靶按钮 点击 触发对应类型事件": "Click the target button to trigger the corresponding type of event", "console.log('点击了' + command)": "console.log('clicked' + command)", "beforeunload 事件处理函数": "beforeunload event handler function", "console.log('浏览器关闭|浏览器刷新|页面关闭|打开新页面')": "console.log('Browser close|Browser refresh|Page close|Open new page')", "如果数据没有变动,则不需要提示用户保存": "If the data has not changed, there is no need to prompt the user to save it.", "显示自定义对话框": "Show custom dialog", "阻止默认行为": "Block default behavior", "兼容旧版本浏览器": "Compatible with older browsers", "// 弹出自定义模态框": "// Pop up a custom modal box", "modal.innerHTML = \"您确定要离开本页面吗?\";": "modal.innerHTML = \"Are you sure you want to leave this page?\";", "btn.textContent = \"留在页面\";": "btn.textContent = \"Stay on the page\";", "// 取消默认的 beforeunload 行为": "//Cancel the default beforeunload behavior", "// 关闭自定义模态框": "// Close the custom modal box", "如果传入了参数,则复制对比窗口中的版本号": "If parameters are passed in, copy the version number in the comparison window", "否则复制当前选中的 Prompt": "Otherwise copy the currently selected Prompt", "格式化时间": "Format time", "格式化聊天时间(更简洁的格式)": "Format chat time (more concise format)", "超过7天显示具体日期": "Show specific date if more than 7 days", "格式化聊天内容(支持markdown)": "Format chat content (support markdown)", "使用marked解析markdown": "Use marked to parse markdown", "切换聊天反馈(Like/Unlike)": "Switch chat feedback (Like/Unlike)", "防止重复点击:如果正在处理,直接返回": "Prevent repeated clicks: If it is being processed, return directly", "验证 chatId 是否有效": "Verify chatId is valid", "确保 chatId 是数字类型": "Make sure chatId is a numeric type", "找到当前消息(同时检查 id 和 numericChatId)": "Find the current message (check both id and numericChatId)", "验证消息 ID 是否有效": "Verify that the message ID is valid", "如果点击的是当前已选中的反馈,则取消反馈(设为null)": "If you click on the currently selected feedback, cancel the feedback (set to null)", "调用API更新反馈(使用有效的消息 ID)": "Call the API to update the feedback (using a valid message ID)", "注意:使用 Request DTO 格式,属性名需要首字母大写": "Note: Using the Request DTO format, the first letter of the attribute name needs to be capitalized", "更新本地数据": "Update local data", "重置标志,允许下次点击": "Reset flag to allow next click", "处理对话历史滚动": "Handle conversation history scrolling", "可以在这里添加滚动相关的逻辑,比如显示滚动位置等": "You can add scrolling-related logic here, such as displaying the scroll position, etc.", "目前暂时不需要特殊处理": "No special treatment is required at the moment", "输出 分数趋势图初始化": "Output Score Trend Chart Initialization", "是否将 tooltip 框限制在图表的区域内。": "Whether to limit the tooltip box to the area of ​​the chart.", "interval: 10 //使x轴都显示": "interval: 10 //Display all x-axis", "3维图表的高度 z轴": "Height z-axis of 3D chart", "3维图表的宽度 x轴": "Width of 3D chart x-axis", "3维图表的深度 y轴": "Depth y-axis of 3D chart", "整个chart背景,可为自定义颜色或图片": "The entire chart background can be a custom color or picture", "坐标轴轴线(线)控制": "Coordinate axis axis (line) control", "该参数需设为true": "This parameter needs to be set to true", "interval:200,//x,y坐标轴刻度标签的显示间隔,在类目轴中有效。": "interval:200,//The display interval of the x, y coordinate axis scale labels, valid in the category axis.", "坐标轴样式": "axis style", "(单个刻度不会受影响)": "(Single scales will not be affected)", "线条宽度": "line width", "坐标轴 label": "axis label", "是否显示刻度 (刻度上的数字,或者类目)": "Whether to display the scale (number on the scale, or category)", "坐标轴刻度标签的显示间隔,在类目轴中有效。": "The display interval of coordinate axis scale labels, valid in category axis.", "刻度标签样式": "tick label style", "return value >= 6 ? 'green' : 'red';//根据范围显示颜色,主页为值有效": "return value >= 6 ? 'green' : 'red';//Display color according to the range, the home page is valid for the value", "borderWidth:\"\",//文字的描边宽度。": "borderWidth:\"\",//The stroke width of the text.", "borderColor:'',//文字的描边颜色。": "borderColor:'',//The stroke color of the text.", "刻度标签字体大小": "Tick ​​label font size", "粗细": "Thickness", "刻度": "scale", "是否显示出": "Whether to display", "interval:100,//坐标轴刻度标签的显示间隔,在类目轴中有效": "interval:100, //Display interval of coordinate axis scale labels, valid in category axis", "length: 5,//坐标轴刻度的长度": "length: 5,//The length of the coordinate axis scale", "lineStyle: {//举个例子,样式太丑将就": "lineStyle: {//For example, if the style is too ugly, just make do with it.", "color: '#000',//颜色": "color: '#000',//color", "width: 5//厚度(虽然为宽表现为高度),对应length*(宽)": "width: 5//Thickness (although width is expressed as height), corresponding to length*(width)", "平面上的分隔线。": "Dividers on a flat surface.", "立体网格线": "Three-dimensional grid lines", "// interval:100,//坐标轴刻度标签的显示间隔,在类目轴中有效": "//interval:100,//Display interval of coordinate axis scale labels, valid in category axis", "坐标轴指示线。": "Coordinate axis indicator line.", "鼠标在chart上的显示线": "The display line of the mouse on the chart", "color:'#000',//颜色": "color:'#000',//color", "width:5//厚度(虽然为宽表现为高度),对应length*(宽)": "width:5//Thickness (although width is expressed as height), corresponding to length*(width)", "viewControl用于鼠标的旋转,缩放等视角控制。(以下适合用于地球自转等)": "viewControl is used for mouse rotation, zoom and other perspective control. (The following is suitable for earth rotation, etc.)", "最小旋转角度": "Minimum rotation angle", "最大旋转角度": "Maximum rotation angle", "旋转灵敏度,值越大旋转越快": "Rotation sensitivity, the larger the value, the faster the rotation", "projection: 'orthographic'//默认为透视投影'perspective',也支持设置为正交投影'orthographic'。": "projection: 'orthographic' //The default is perspective projection 'perspective', and it also supports setting to orthogonal projection 'orthographic'.", "autoRotate:true,//会有自动旋转查看动画出现,可查看每个维度信息": "autoRotate:true,//There will be an automatic rotation viewing animation, and each dimension information can be viewed", "autoRotateDirection:'ccw',//物体自传的方向。默认是 'cw' 也就是从上往下看是顺时针方向,也可以取 'ccw',既从上往下看为逆时针方向。": "autoRotateDirection:'ccw',//The direction of the object's autobiography. The default is 'cw', which means the direction is clockwise when viewed from top to bottom, or 'ccw', which means the direction is counterclockwise when viewed from top to bottom.", "autoRotateSpeed:12,//物体自传的速度": "autoRotateSpeed:12,//The speed of the object’s autorotation", "autoRotateAfterStill:2,//在鼠标静止操作后恢复自动旋转的时间间隔。在开启 autoRotate 后有效。": "autoRotateAfterStill:2,//The time interval for resuming automatic rotation after the mouse is stationary. Valid after turning on autoRotate.", "默认视角距离主体的距离(常用)": "The distance between the default viewing angle and the subject (commonly used)", "视角绕 x 轴,即上下旋转的角度(与beta一起控制视野成像效果)": "The viewing angle is around the x-axis, that is, the angle of up and down rotation (together with beta, it controls the visual field imaging effect)", "视角绕 y 轴,即左右旋转的角度。": "The angle of view rotates around the y-axis, which is the angle of left-right rotation.", "center:[]//视角中心点,旋转也会围绕这个中心点旋转,默认为[0,0,0]": "center:[]//The center point of the perspective, the rotation will also rotate around this center point, the default is [0,0,0]", "光照相关的设置": "Lighting related settings", "color: '#fff',//光照颜色会与所设置颜色发生混合": "color: '#fff',//The lighting color will be mixed with the set color", "intensity: 1.2,//主光源的强度(光的强度)": "intensity: 1.2,//The intensity of the main light source (the intensity of the light)", "shadow: false,//主光源是否投射阴影。默认关闭。": "shadow: false, //Whether the main light source casts shadows. Off by default.", "// alpha:0//主光源绕 x 轴,即上下旋转的角度。配合 beta 控制光源的方向(跟beta结合可确定太阳位置)": "// alpha:0//The main light source rotates around the x-axis, that is, the angle of up and down rotation. Use beta to control the direction of the light source (combine with beta to determine the position of the sun)", "// beta:10//主光源绕 y 轴,即左右旋转的角度。": "// beta:10//The main light source rotates around the y-axis, that is, the angle of left and right rotation.", "ambient: {//全局的环境光设置。": "ambient: {//Global ambient light settings.", "color: '#fff'//影响柱条颜色": "color: '#fff'//Affects the color of the bar", "// ambientCubemap: {//会使用纹理作为光源的环境光": "//ambientCubemap: {//The texture will be used as the ambient light of the light source", "// // 解析 hdr 时使用的曝光值": "// // Exposure value used when parsing hdr", "postEffect:{//后处理特效的相关配置,后处理特效可以为画面添加高光,景深,环境光遮蔽(SSAO),调色等效果。可以让整个画面更富有质感。": "postEffect:{//Relevant configuration of post-processing special effects. Post-processing special effects can add highlights, depth of field, ambient light occlusion (SSAO), color adjustment and other effects to the picture. It can make the whole picture more textured.", "enable:true//高光特效,适合地球仪": "enable:true//Highlight special effect, suitable for globe", "每个区的数据一一对应": "The data in each area corresponds to one-to-one", "将 label 内容固定为 \"\"": "Fix label content to \"\"", "监听点击事件": "Listen for click events", "设置霸道选中": "Set overbearing selected", "获取靶道详情": "Get target lane details", "获取输出列表和平均分": "Get output list and average score", "监听图表鼠标移入事件 mouseover globalout": "Listen to the chart mouse move event mouseover globalout", "// 添加对应的 scatter3D": "//Add the corresponding scatter3D", "symbol: 'circle', // 设置圆点样式为圆形": "symbol: 'circle', // Set the dot style to circle", "symbolSize: 10, // 设置圆点的大小": "symbolSize: 10, //Set the size of the dots", "show: false, // 设置 label 显示": "show: false, //Set label display", "return ''; // 将 label 内容固定为 \"\"": "return ''; // Fix label content to \"\"", "data: [params.data] //每个区的数据一一对应": "data: [params.data] //One-to-one correspondence between data in each area", "监听图表鼠标移出事件": "Listen to chart mouse out events", "输出 获取评分趋势 图表数据": "Output Get rating trend chart data", "console.log('获取评分趋势 图表数据', this.isAvg)": "console.log('Get rating trend chart data', this.isAvg)", "初始化图表 接口调用成功": "Initialization chart interface call successful", "靶场|靶道|模型 选择变化": "Range|Range|Model Selection Changes", "靶道变化时,重置打靶按钮": "When the target lane changes, reset the target button", "提示 有数据变化 是否保存为草稿": "Tip: There are data changes. Do you want to save it as a draft?", "保存草稿": "save draft", "重置 页面变化记录": "Reset page change history", "重新获取靶道列表": "Retrieve target list", "val 在 promptOpt 中的位置": "The position of val in promptOpt", "清空ai评分标准": "Clear ai scoring criteria", "其他": "other", "页面变化记录": "Page change record", "输入Prompt 重置": "Enter Prompt to reset", "ai评分标准 重置": "ai scoring criteria reset", "继续聊天:加载历史记录并打开战术选择弹窗": "Continue chatting: Load history and open tactical selection popup", "使用新的 API 同时获取对话历史和 Prompt 内容": "Get both conversation history and prompt content using new API", "打开战术选择弹窗,锁定为对话模式": "Open the tactical selection pop-up window and lock it in dialogue mode", "降级方案:如果新 API 失败,使用旧的 API": "Downgrade scenario: If new API fails, use old API", "尝试从 outputList 获取 Prompt 内容": "Try to get Prompt content from outputList", "处理对话输入框的键盘事件(快捷键支持)": "Handle keyboard events of dialog input boxes (shortcut key support)", "Ctrl+Enter (Windows/Linux) 或 Cmd+Enter (Mac)": "Ctrl+Enter (Windows/Linux) or Cmd+Enter (Mac)", "触发提交": "trigger commit", "战术选择 关闭弹出": "Tactical Options Close Popup", "战术选择 dialog 关闭": "Tactics selection dialog close", "重置为默认值": "reset to default", "重置继续聊天状态": "Reset chat status", "使用 clearValidate 清除验证状态,而不是 resetFields": "Use clearValidate to clear validation status instead of resetFields", "因为某些字段可能是条件显示的(v-if),resetFields 可能会出错": "Because some fields may be displayed conditionally (v-if), resetFields may error", "打开导图对话框": "Open the map dialog box", "检查当前靶场是否有靶道数据": "Check whether the current shooting range has target track data", "关闭导图对话框": "Close the map dialog box", "初始化 3D 导图": "Initialize 3D map", "构建树状结构数据": "Build tree-structured data", "确保 THREE 已加载": "Make sure THREE is loaded", "创建场景(使用渐变背景)": "Create a scene (using a gradient background)", "创建渐变背景": "Create a gradient background", "禁用雾化效果,确保远处节点清晰可见": "Disable the fog effect to ensure that distant nodes are clearly visible", "创建相机(增大远裁剪面,确保所有节点都可见)": "Create camera (increase far clipping plane, make sure all nodes are visible)", "参数:视场角(75度), 宽高比, 近裁剪面(0.1), 远裁剪面(5000 - 增大以支持更远的节点)": "Parameters: Field of view (75 degrees), aspect ratio, near clipping plane (0.1), far clipping plane (5000 - increased to support further nodes)", "创建渲染器(禁用对数深度缓冲,提高远处物体的清晰度)": "Create renderer (disables logarithmic depth buffering, improves clarity of distant objects)", "使用高精度,提高渲染质量": "Use high precision to improve rendering quality", "设置像素比,在高DPI屏幕上更清晰": "Set pixel ratio for better clarity on high DPI screens", "添加控制器(使用本地化的 OrbitControls)": "Add a controller (using localized OrbitControls)", "启用阻尼效果,使旋转更平滑": "Enable damping effect for smoother rotation", "启用缩放(增大最大距离,支持更远的视角)": "Enable zoom (increases maximum distance, supports further viewing angles)", "从 200 增加到 500": "Increased from 200 to 500", "启用旋转": "Enable rotation", "启用平移": "Enable panning", "设置初始相机位置,使其能看到整个场景": "Set the initial camera position so that it can see the entire scene", "添加更丰富的光源系统(增强远处物体的照明)": "Add a richer light source system (enhance lighting of distant objects)", "环境光 - 提供基础照明(增强亮度以照亮远处节点)": "Ambient Light - Provides basic lighting (increases brightness to illuminate distant nodes)", "从 0.4 增加到 0.6": "Increased from 0.4 to 0.6", "主方向光 - 模拟太阳光(无衰减,照亮所有节点)": "Main directional light - simulates sunlight (no attenuation, illuminates all nodes)", "辅助方向光 - 补充照明(从另一侧照亮)": "Auxiliary directional light - supplementary lighting (light from the other side)", "从 0.4 增加到 0.5": "Increased from 0.4 to 0.5", "第三方向光 - 从前方照亮远处节点": "Third directional light - illuminates distant nodes from the front", "点光源 - 增加层次感(无衰减距离限制)": "Point light source - increase the sense of layering (no attenuation distance limit)", "distance=0 表示无限距离": "distance=0 means infinite distance", "渲染节点": "render node", "开始动画循环": "Start animation loop", "处理窗口大小变化": "Handling window size changes", "解析每个靶道的 FullVersion": "Parse the FullVersion of each target lane", "解析格式:2023.12.14.1-T1.1-A123": "Parsing format: 2023.12.14.1-T1.1-A123", "获取或创建 RangeName 根节点": "Get or create the RangeName root node", "解析 Tactic(每个.是一层)": "Parse Tactic (each . is a layer)", "修复 parentPath:应该包含完整的路径前缀": "Fix parentPath: should contain full path prefix", "例如: \"Range1-T2.1\"": "For example: \"Range1-T2.1\"", "例如: \"Range1\"": "For example: \"Range1\"", "确保 lastTacticNode 存在": "Make sure lastTacticNode exists", "添加 Aiming(特殊层)- 修复:使用唯一的key避免共享": "Add Aiming (special layer) - Fix: Use unique key to avoid sharing", "使用完整路径作为key,确保每个Aiming节点都是独立的": "Use the full path as the key to ensure that each Aiming node is independent", "例如: \"T1.1-A1\", \"T1.2-A1\"": "For example: \"T1.1-A1\", \"T1.2-A1\"", "显示名称去掉A": "Remove A from display name", "如果没有 Aiming,直接添加到最后一个 Tactic 节点": "If there is no Aiming, add it directly to the last Tactic node", "--- 优化功能 ---": "---Optimization function ---", "检查 PromptCatalyzer 是否已初始化": "Check if PromptCatalyzer has been initialized", "NCF AppResponseBase 返回格式:{ success: true, data: { isInitialized: true, ... } }": "NCF AppResponseBase return format: { success: true, data: { isInitialized: true, ... } }", "获取可用的 AI Model 列表": "Get the list of available AI Models", "NCF AppResponseBase 返回格式:{ success: true, data: { models: [...], recommendedModelId: 1 } }": "NCF AppResponseBase return format: { success: true, data: { models: [...], recommendedModelId: 1 } }", "自动选择推荐的 Model": "Automatically select recommended Models", "处理错误响应": "Handling error responses", "执行初始化": "Perform initialization", "NCF AppResponseBase 返回格式:{ success: true, data: { promptCode: \"...\", ... } }": "NCF AppResponseBase return format: { success: true, data: { promptCode: \"...\", ... } }", "初始化成功后,刷新页面数据": "After initialization is successful, refresh the page data", "继续执行优化": "Continue with optimization", "继续执行优化(初始化完成后)": "Continue optimization (after initialization is complete)", "重新打开优化对话框": "Reopen the optimization dialog", "⭐ 新增:检查分数并提示优化": "⭐ NEW: Check scores and prompt for optimization", "获取最终分数": "Get final score", "如果无法获取分数,不提示": "If score cannot be obtained, do not prompt", "设置优化建议的阈值(分数低于6分时建议优化)": "Set the threshold for optimization suggestions (optimization is recommended when the score is lower than 6 points)", "分数较低,提示用户是否需要优化": "The score is low, prompting the user whether optimization is needed", "用户确认优化": "User confirmation optimization", "用户取消": "User cancels", "分数较高,显示简单的成功消息": "Higher scores show a simple success message", "不显示错误消息,避免影响用户体验": "Do not display error messages to avoid affecting user experience", "⭐ 新增:根据 PromptItem 的平均分检查是否需要优化": "⭐ New: Check whether optimization is needed based on the average score of PromptItem", "获取当前选中的 Prompt 信息": "Get the currently selected Prompt information", "如果平均分数为 -1 或无效,不提示": "Do not prompt if average score is -1 or invalid", "设置优化建议的阈值": "Set thresholds for optimization recommendations", "如果平均分低于阈值,且当前 Range 有多个测试结果,则提示优化": "If the average score is lower than the threshold and the current Range has multiple test results, it will prompt optimization", "使用 Notification 而不是 Confirm,避免阻塞用户操作": "Use Notification instead of Confirm to avoid blocking user operations", "检查是否已初始化": "Check if it has been initialized", "未初始化,显示初始化对话框": "Not initialized, display initialization dialog box", "加载可用 Model 列表": "Load the list of available Models", "显示初始化对话框": "Show initialization dialog", "已初始化,直接打开优化对话框": "Already initialized, open the optimization dialog box directly", "/ 将当前靶道的预期结果同步到 aiScoreForm,避免「打靶后 AI 评分」仅依赖表单内存而遗漏详情里的 expectedResultsJson。": "/ Synchronize the expected results of the current target lane to aiScoreForm to avoid \"post-target AI scoring\" relying only on the form memory and missing the expectedResultsJson in the details.", "获取当前选择的 Prompt Code": "Get the currently selected Prompt Code", "如果没找到,尝试从详情获取": "If not found, try to get it from details", "获取当前 Prompt 的详细信息(包括参数)": "Get the details of the current Prompt (including parameters)", "尽早占位,避免校验通过后、发请求前的双击产生两次 HTTP 请求": "Seize the space as early as possible to avoid double-clicking after the verification is passed and before sending the request to generate two HTTP requests.", "getFieldList 只更新靶场列表;靶道下拉 promptOpt 必须由 getPromptOptData 拉取,否则列表仍是旧的,会误判「未匹配到 Prompt Code」": "getFieldList only updates the shooting range list; the target drop-down promptOpt must be pulled by getPromptOptData, otherwise the list will still be old and \"Prompt Code not matched\" will be misjudged.", "优化已创建新版本,打靶时用\"连发\"模式(不再创建新版本)": "A new version has been created for optimization, and the \"burst\" mode is used during target shooting (no new version will be created)", "计算树的高度(用于平衡布局)": "Calculate the height of the tree (for balanced layout)", "@deprecated 不再使用:由于A节点在Z轴延伸,T节点间距已改为固定值": "@deprecated No longer used: Since the A node extends in the Z axis, the T node spacing has been changed to a fixed value", "计算子树的节点数量(用于平衡布局)": "Count the number of nodes in a subtree (for balanced layout)", "当前节点": "current node", "**计算所有评分的统计信息(用于排名)**": "**Calculate statistics for all ratings (used for ranking)**", "遍历所有 promptOpt 收集评分": "Iterate through all promptOpt to collect ratings", "优先使用 evalMaxScore": "Prefer using evalMaxScore", "如果没有,使用 evalAvgScore": "If not, use evalAvgScore", "排序(从大到小)": "Sort (largest to smallest)", "计算百分位点(用于分级)": "Calculate percentile points (for grading)", "返回该分数在所有分数中的排名百分比(0-100)": "Returns the score's ranking percentage among all scores (0-100)", "只显示前10个": "Only show the first 10", "渲染树节点(优化版:动态平衡布局 + 动画效果)": "Rendering tree nodes (optimized version: dynamic balanced layout + animation effect)", "**计算评分统计信息**": "**Calculate rating statistics**", "初始化节点映射": "Initialize node mapping", "**全新渲染逻辑:简化的固定间距布局**": "**New rendering logic: simplified fixed spacing layout**", "currentX: 当前X轴位置(全局计数器,每个节点递增)": "currentX: current X-axis position (global counter, incremented by each node)", "默认展开": "Expand by default", "**重要:跳过 Aiming 类型节点,它们会在父节点处理时被创建**": "**Important: Skip Aiming type nodes, they will be created when the parent node is processed**", "不在这里处理 Aiming 节点": "Aiming nodes are not handled here", "**新逻辑:分离 Aiming 子节点和 Tactic 子节点**": "**New logic: Separate Aiming child nodes and Tactic child nodes**", "任何T节点都可能同时有A节点和T子节点": "Any T node may have both A nodes and T child nodes", "**全新简化布局:固定间距,顺序排列**": "**New simplified layout: fixed spacing, sequential arrangement**", "固定间距": "fixed spacing", "当前节点位置": "Current node position", "X轴位置(全局计数器)": "X-axis position (global counter)", "Y轴深度(层级向下)": "Y-axis depth (level down)", "T节点统一在 Z=0 平面": "T nodes are unified on the Z=0 plane", "递增X轴位置(为下一个同级节点预留)": "Increment the X-axis position (reserved for the next sibling node)", "检查是否有当前编辑的靶道": "Check if there is a currently edited target lane", "创建几何体(只为 Range 和 Tactic 创建,Aiming 在专门的地方创建)": "Create geometry (only created for Range and Tactic, Aiming is created in a dedicated place)", "靶场:方块(使用更平滑的圆角效果)": "Shooting Range: Block (uses smoother rounded corners)", "添加发光效果(当前选中时)": "Add glow effect (when currently selected)", "靶道(Tactic):圆柱体(代表路径/通道)": "Tactic: Cylinder (representing path/channel)", "不应该到达这里,因为 Aiming 节点已经在上面被 return 了": "Shouldn't get here because the Aiming node has already been returned above", "初始位置设置为目标位置(后续用于动画)": "The initial position is set to the target position (subsequently used for animation)", "如果是圆柱体(tactic节点),需要旋转90度使其竖立": "If it is a cylinder (tactic node), it needs to be rotated 90 degrees to make it stand upright.", "绕X轴旋转90度": "Rotate 90 degrees around the X axis", "用于展开动画": "for expansion animation", "如果是新创建的节点,从小到大的展开动画": "If it is a newly created node, the animation will expand from small to large.", "添加发光效果": "Add glow effect", "如果是圆柱体(tactic节点),发光效果也需要旋转": "If it is a cylinder (tactic node), the lighting effect also needs to be rotated", "准备标签文字(提前计算,用于动态宽度)": "Prepare label text (calculated in advance, used for dynamic width)", "创建文字标签(更大的文字,动态宽度)": "Create text labels (larger text, dynamic width)", "根据文字内容和类型动态计算画布大小(再次加大一倍)": "Dynamically calculate canvas size based on text content and type (double again)", "从 90/80 加大到 180/160": "Increased from 90/80 to 180/160", "从 140/120 加大到 280/240": "Increased from 140/120 to 280/240", "从 110/95 加大到 220/190": "Increased from 110/95 to 220/190", "预计算文字宽度": "Precomputed text width", "从 30 增加到 50": "increased from 30 to 50", "从 20 增加到 30": "increased from 20 to 30", "动态设置画布大小(字体加大后,画布也要更大)": "Dynamically set the canvas size (when the font is increased, the canvas must also be larger)", "从 256 增加到 512": "Increased from 256 to 512", "绘制圆角矩形(兼容性处理)": "Draw a rounded rectangle (compatibility processing)", "玻璃效果:半透明白色背景": "Glass effect: translucent white background", "当前选中:淡红色玻璃": "Currently selected: light red glass", "普通:半透明白色玻璃": "Ordinary: translucent white glass", "玻璃边框(增强玻璃效果)": "Glass frame (enhanced glass effect)", "当前选中:红色边框": "Currently selected: red border", "普通:白色边框": "Normal: white border", "添加高光效果(模拟玻璃反光)": "Add highlight effect (simulate glass reflection)", "绘制文字(带阴影增强可读性,更大字体)": "Draw text (with shading to enhance readability, larger font size)", "从 10 增加到 20": "increased from 10 to 20", "从 3 增加到 5": "Increased from 3 to 5", "如果有类型前缀(T),T 和数字在同一行显示": "If there is a type prefix (T), T and the number are displayed on the same line", "组合文本:T + 数字": "Combined text: T + number", "使用统一的大字体显示": "Use uniform large font display", "靶场名称(更大字体)": "Range name (larger font)", "如果有提示信息,显示在下方": "If there is a prompt message, it will be displayed below", "从 55/48 加大到 110/96": "Increased from 55/48 to 110/96", "从 +70 改为 +120": "Changed from +70 to +120", "初始透明,用于渐入动画": "Initial transparency, used for fade-in animations", "**横向布局:标签在节点下方(Y轴负方向),Z轴稍微向前避免被遮挡**": "**Horizontal layout: The label is below the node (in the negative direction of the Y axis), and the Z axis is slightly forward to avoid being blocked**", "Z轴向前偏移,避免被节点遮挡": "The Z-axis is offset forward to avoid being blocked by nodes.", "动态设置 sprite 尺寸(根据画布宽度,字体加大后标签也要更大)": "Dynamically set the sprite size (according to the canvas width, the label will be larger when the font is increased)", "从 12 增加到 18": "Increased from 12 to 18", "从 3 增加到 4.5": "Increased from 3 to 4.5", "**关键修复:使用 Object.freeze 防止 Vue 响应式监听导致栈溢出**": "**Key fix: Use Object.freeze to prevent Vue reactive listening from causing stack overflow**", "Three.js 对象不应该被 Vue 监听,否则会导致性能问题和循环引用": "Three.js objects should not be listened to by Vue, otherwise it will cause performance issues and circular references", "冻结 Three.js 相关对象,防止 Vue 添加响应式 getter/setter": "Freeze Three.js related objects to prevent Vue from adding responsive getters/setters", "这个数组可能会很大,不需要响应式": "This array may be large and does not require responsiveness", "存储节点引用以便快速查找": "Store node references for quick lookup", "先不添加连接线,稍后统一处理(避免在位置调整前创建线条)": "Do not add connecting lines first and process them later (avoid creating lines before position adjustment)", "**1. 先处理 Aiming 子节点(在Z轴延伸,面向镜头)**": "**1. Process the Aiming child node first (extend on the Z axis, facing the camera)**", "Aiming 节点:Z轴排列(面向镜头方向延伸)": "Aiming node: Z-axis arrangement (extending towards the camera direction)", "先保存当前节点的引用,稍后会更新 Aiming 的 parentPosition": "Save the reference to the current node first, and update Aiming's parentPosition later.", "Aiming 节点之间的 Z 轴间距(增加1倍:8 -> 16)": "Aiming Z-axis spacing between nodes (increase by 1x: 8 -> 16)", "**新策略:A节点从父T节点位置开始,向摄像机方向(+Z)依次排列**": "**New strategy: A nodes start from the position of the parent T node and are arranged in sequence toward the camera direction (+Z)**", "A1 在最靠近 T 的位置,A2 在 A1 前面,A3 在 A2 前面...": "A1 is at the position closest to T, A2 is in front of A1, A3 is in front of A2...", "不再使用中心对称或 hash 偏移": "No more use of centrosymmetry or hash offsets", "保存 Aiming 节点数据,稍后更新 parentPosition": "Save Aiming node data and update parentPosition later", "**新布局:A节点在Z轴朝向摄像机方向依次排列**": "**New layout: A nodes are arranged sequentially on the Z axis toward the camera**", "X和Y保持与父T节点相同,只在Z轴上递增": "X and Y remain the same as the parent T node, only incrementing on the Z axis", "与父节点同一水平位置": "Same horizontal position as parent node", "与父节点同一深度(不向下一层)": "Same depth as parent node (not one level lower)", "Z轴:从T节点位置开始,每个A节点增加 aimingSpacing": "Z axis: Starting from the T node position, each A node increases aimingSpacing", "**根据评分改变大小和颜色**": "**Change size and color based on rating**", "默认大小": "default size", "默认颜色(浅绿)": "Default color (light green)", "**提升到外层作用域,供后续发光效果使用**": "**Promoted to the outer scope for subsequent use of glowing effects**", "如果有评分数据,根据评分调整": "If there is rating data, adjust it based on the rating", "**调试:查看数据结构**": "**Debug: View data structure**", "**优先使用 evalMaxScore(最高分)作为评分**": "**Prefer using evalMaxScore (highest score) as the score**", "score 已在外层定义,直接赋值": "score has been defined in the outer layer and can be assigned directly.", "先尝试 finalScore": "Try finalScore first", "如果没有 finalScore,使用 evalMaxScore": "If there is no finalScore, use evalMaxScore", "如果没有 evalMaxScore,使用 evalAvgScore": "If evalMaxScore is not available, use evalAvgScore", "**根据相对排名设置大小和颜色**": "**Set size and color based on relative ranking**", "**特殊处理:排名第一的节点**": "**Special treatment: Node ranked first**", "第一名 - 特大、紫色(区别于黄色高亮)": "1st Place - Extra Large, Purple (Different from Yellow Highlight)", "紫色(与黄色区分明显)": "Purple (obviously different from yellow)", "紫红色发光": "Purple red glow", "根据排名百分位分级(Top 20%, 20-40%, 40-60%, 60-80%, Bottom 20%)": "Ranked according to ranking percentile (Top 20%, 20-40%, 40-60%, 60-80%, Bottom 20%)", "Top 20% - 最大、亮绿色": "Top 20% - Largest, bright green", "20-40% - 较大、绿色": "20-40% - Larger, greener", "40-60% - 正常、橙色": "40-60% - Normal, Orange", "60-80% - 较小、浅红色": "60-80% - smaller, light red", "Bottom 20% - 最小、红色": "Bottom 20% - smallest, red", "如果是当前选中的节点,覆盖为黄色高亮": "If it is the currently selected node, it will be highlighted in yellow.", "创建 Aiming 几何体:小圆球(大小由评分决定)": "Create aiming geometry: small sphere (size determined by score)", "**为第一名添加特殊发光效果**": "**Add special glow effect for first place**", "创建紫色发光外壳(与黄色高亮区分)": "Create purple glowing shell (distinguishable from yellow highlight)", "紫色": "Purple", "创建 Aiming 文字标签(更大的文字,动态宽度)": "Create Aiming text labels (larger text, dynamic width)", "提取 Aiming 数字(提前,用于计算宽度)": "Extract Aiming numbers (ahead of time, used to calculate width)", "根据文字内容动态计算画布大小(再次加大一倍)": "Dynamically calculate canvas size based on text content (double again)", "组合文本(用于宽度计算)": "Combined text (for width calculation)", "预计算文字宽度(使用组合后的文本)": "Precompute text width (using combined text)", "动态设置画布大小(根据文字内容,字体加大后画布也要更大)": "Dynamically set the canvas size (according to the text content, the canvas will be larger when the font is increased)", "玻璃效果背景": "glass effect background", "玻璃边框": "glass frame", "高光效果": "Highlight effect", "绘制文字(A 和数字在同一行,已在上面定义 aimingCombinedText)": "Draw text (A and number are on the same line, aimingCombinedText is defined above)", "使用统一的大字体显示(组合文本已在前面定义)": "Display using a uniform large font (combined text has been defined previously)", "创建 sprite": "Create a sprite", "**横向布局:标签在节点下方,Z轴稍微向前避免被球遮挡**": "**Horizontal layout: label is below the node, Z-axis is slightly forward to avoid being obscured by the ball**", "Z轴向前偏移": "Z axis offset forward", "冻结 Three.js 对象,防止 Vue 响应式监听导致栈溢出": "Freeze Three.js objects to prevent Vue responsive listening from causing stack overflow", "更新当前节点数据中记录的子节点引用": "Update the child node reference recorded in the current node data", "**2. 再处理 Tactic 子节点(继续在Y轴向下,递归调用)**": "**2. Process the Tactic child node again (continue downward on the Y axis and call recursively)**", "简单递归渲染子节点,currentX 会自动累加": "Simple recursive rendering of child nodes, currentX will automatically accumulate", "记录子节点引用(用于快速更新可见性)": "Record child node references (for quick visibility updates)", "**直接渲染所有根节点**": "**Render all root nodes directly**", "重置X轴起点": "Reset the X-axis starting point", "**特殊处理:将Range节点移动到其子节点的水平中点**": "**Special handling: Move the Range node to the horizontal midpoint of its child node**", "找到这个Range的所有子T节点": "Find all child T nodes of this Range", "计算子节点的X轴范围": "Calculate the X-axis range of child nodes", "移动Range节点到中点": "Move the Range node to the midpoint", "同时更新该Range的所有子节点的parentPosition": "Update the parentPosition of all child nodes of the Range at the same time", "**计算树的实际范围**": "**Calculate the actual extent of the tree**", "计算X轴范围(只计算T节点,不包括A节点)": "Calculate the X-axis range (only T nodes are calculated, excluding A nodes)", "计算Z轴范围(只计算A节点)": "Calculate the Z-axis range (only calculate the A node)", "水平宽度(X轴)": "Horizontal width (X-axis)", "深度(Y轴)": "Depth (Y-axis)", "Z轴跨度(A节点)": "Z-axis span (A node)", "向下展开,中心点在负Y": "Expand downward, the center point is at negative Y", "Z轴中心(Aiming节点的中心)": "Z axis center (center of Aiming node)", "调整相机初始位置,确保能看到整个树": "Adjust the initial position of the camera to ensure that the entire tree can be seen", "**优化相机距离计算:使用更智能的算法**": "**Optimize camera distance calculation: use smarter algorithm**", "考虑3个维度,但给予不同的权重": "Consider 3 dimensions but give different weights", "使用包围盒对角线长度作为基准": "Use the bounding box diagonal length as a baseline", "根据节点密度调整距离系数": "Adjust distance coefficient based on node density", "基础系数": "Basic coefficient", "节点越多,距离系数稍微减小(避免拉太远)": "The more nodes there are, the distance coefficient decreases slightly (to avoid pulling too far)", "在所有节点位置确定后,统一创建连接线": "After the positions of all nodes are determined, connect lines are created uniformly", "**优化相机位置:从右前上方观察**": "**Optimized camera position: viewed from the top right front**", "这样可以同时看清楚:": "This way you can see clearly at the same time:", "- X轴的T节点排列": "- Arrangement of T nodes on the X axis", "- Y轴的深度层级": "- Depth level of Y axis", "- Z轴的A节点分布": "- A node distribution on Z axis", "X轴:稍微偏右": "X-axis: slightly to the right", "Y轴:从上方看(更高)": "Y-axis: Viewed from above (higher)", "Z轴:从前方看": "Z axis: Viewed from the front", "启动入场动画": "Start entry animation", "添加点击事件(简化版:吸入/弹出效果)": "Add click event (simplified version: inhale/pop effect)", "标记是否正在执行动画": "Whether the marker is performing animation", "如果正在执行动画,忽略点击": "Ignore clicks if animation is in progress", "只处理有子节点的节点": "Only process nodes with child nodes", "切换展开/收拢状态": "Toggle expanded/collapsed state", "设置动画标记": "Set animated markers", "执行动画": "Execute animation", "展开:弹出动画": "Expand: Pop-up animation", "收拢:吸入动画": "Collapse: Inhale animation", "**添加双击事件:选中Aiming节点对应的靶道**": "**Add double-click event: select the target channel corresponding to the Aiming node**", "检测mesh和sprite": "Detect mesh and sprite", "只处理Aiming节点": "Only handle Aiming nodes", "获取该Aiming节点对应的promptId": "Get the promptId corresponding to the Aiming node", "**关闭3D导图浮窗(先关闭,避免与确认对话框冲突)**": "**Close the 3D map floating window (close it first to avoid conflict with the confirmation dialog box)**", "**复用原有的靶道切换逻辑(包括保存草稿提示)**": "**Reuse the original target lane switching logic (including save draft prompts)**", "注意:程序化设置 v-model 不会触发 @change 事件": "Note: Setting v-model programmatically will not trigger the @change event", "需要手动调用 promptChangeHandel 函数来触发完整逻辑": "You need to manually call the promptChangeHandel function to trigger the complete logic", "该函数会自动处理:": "This function automatically handles:", "1. 检查 pageChange 状态": "1. Check pageChange status", "2. 如果有修改,弹出\"是否保存为草稿\"确认框": "2. If there are any modifications, a confirmation box \"Whether to save as a draft\" will pop up.", "3. 根据用户选择保存或不保存": "3. Save or not save according to user choice", "4. 调用 getPromptetail 获取靶道详情": "4. Call getPromptetail to obtain target channel details", "保存旧值(用于change检测)": "Save old value (for change detection)", "设置新值": "Set new value", "手动触发change处理逻辑": "Manually trigger change processing logic", "**添加鼠标移动事件:显示节点信息tooltip + 边缘高光**": "**Add mouse movement event: display node information tooltip + edge highlight**", "当前悬停的节点": "currently hovering node", "当前的高光外壳": "Current High Gloss Shell", "创建tooltip元素": "Create tooltip element", "**同时检测mesh和sprite(文字标签)**": "**Detect mesh and sprite (text labels) simultaneously**", "根据相交的对象(mesh或sprite)查找对应的nodeData": "Find the corresponding nodeData based on the intersecting objects (mesh or sprite)", "如果是新的节点,更新tooltip和高光": "If it is a new node, update tooltip and highlight", "**移除之前的高光**": "**Remove previous highlights**", "**添加边缘高光效果**": "**Add edge highlight effect**", "根据节点类型创建不同形状的高光": "Create differently shaped highlights based on node type", "方块:使用稍大的方块作为边缘": "Squares: Use slightly larger squares for edges", "原大小是5": "The original size is 5", "圆球:使用稍大的球体作为边缘": "Sphere: Use a slightly larger sphere as the edge", "放大15%": "Magnify 15%", "创建高光材质(边缘发光效果)": "Create a specular material (edge ​​glow effect)", "白色": "White", "只显示背面,形成边缘效果": "Only the back side is shown, creating an edge effect", "构建tooltip内容": "Build tooltip content", "靶场信息": "Range information", "Tactic 信息": "Tactic information", "Aiming 信息(包含评分)": "Aiming information (including ratings)", "**从promptOpt获取完整的评分数据**": "**Get complete rating data from promptOpt**", "从 promptOpt 中查找完整数据": "Find complete data from promptOpt", "**获取最终评分:优先使用 evalMaxScore(最高分)**": "**Get final score: use evalMaxScore (highest score) first**", "如果有评分数据,显示评分区域": "If there is rating data, display the rating area", "**显示最终评分(大号、醒目)+ 排名**": "**Show final score (large, eye-catching) + ranking**", "**根据相对排名设置颜色和大小**": "**Set color and size based on relative ranking**", "**特殊处理:排名第一**": "**Special Treatment: Ranked First**", "紫色(与黄色高亮区分)": "Purple (distinguishable from yellow highlight)", "根据排名百分位分级": "Ranked by Ranking Percentile", "显示排名信息": "Show ranking information", "**显示详细评分**": "**Show detailed rating**", "显示平均分": "Show average score", "显示最高分(如果不是用作主评分)": "Shows the highest score (if not used as the primary score)", "**添加双击提示(仅Aiming节点)**": "**Add double click prompt (Aiming node only)**", "更新tooltip位置": "Update tooltip position", "改变鼠标样式": "Change mouse style", "没有悬停在任何节点上": "Not hovering over any node", "**移除高光效果**": "**Remove highlight effect**", "创建连接线(在所有节点位置确定后调用)- 动态绑定版本": "Create connection line (called after all node positions are determined) - dynamic binding version", "如果已经有连接线,先清理": "If there is already a connection cable, clean it first", "通过 parentPath 找到父节点": "Find the parent node via parentPath", "**保存父节点引用,用于动态更新**": "**Save parent node reference for dynamic updates**", "**使用明亮的白色或接近白色的颜色**": "**Use bright white or something close to white**", "默认白色": "Default white", "当前节点使用黄色": "The current node uses yellow", "使用 Line 而不是 Cylinder,这样可以动态更新端点": "Use Line instead of Cylinder so endpoints can be updated dynamically", "创建线条几何体(两个点)": "Create line geometry (two points)", "2 个点 × 3 个坐标": "2 points × 3 coordinates", "使用 LineBasicMaterial 代替圆柱体": "Use LineBasicMaterial instead of cylinder", "添加连接点(小圆球)- 也会动态跟随,使用明亮颜色": "Add connection points (small balls) - also follow dynamically, use bright colors", "使用白色": "use white", "发光颜色": "Glow color", "初始透明": "initial transparency", "**立即更新一次连接线位置**": "**Update the connection line position immediately**", "调试:记录找不到父节点的情况": "Debugging: Logging when the parent node cannot be found", "尝试输出所有节点的 fullPath,帮助调试": "Try to output the fullPath of all nodes to help debugging", "更新单个连接线的位置(动态绑定到节点位置)": "Update the position of a single connecting line (dynamically bound to node position)", "更新线条的端点位置": "Update the endpoint position of the line", "更新连接点位置": "Update connection point location", "更新所有连接线的位置": "Update the position of all connecting lines", "启动节点入场动画": "Start node entry animation", "动画持续时间(ms)": "Animation duration (ms)", "使用缓动函数(easeOutCubic)": "Use the easing function (easeOutCubic)", "延迟每个节点的动画开始时间,创建波浪效果": "Delay the animation start time of each node to create a wave effect", "每个节点延迟20ms": "20ms delay per node", "缩放动画:从 0.1 到 1": "Scale animation: from 0.1 to 1", "透明度动画": "transparency animation", "**关键:在动画过程中更新连接线位置**": "**Key: Update connection line positions during animation**", "动画完成后,确保所有节点都完全可见": "Once the animation is complete, make sure all nodes are fully visible", "创建径向渐变": "Create a radial gradient", "清空 3D 场景": "Clear 3D scene", "移除所有对象": "Remove all objects", "动画循环(优化版:减少不必要的计算和渲染)": "Animation loop (optimized version: reduce unnecessary calculation and rendering)", "跟踪是否需要渲染": "Track whether rendering is required", "更新控制器(启用阻尼时需要每帧更新)": "Update controller (requires update every frame when damping is enabled)", "update() 返回 true 表示相机位置发生了变化": "update() returns true indicating that the camera position has changed", "优化:大幅减少动画更新频率,避免 CPU 占用过高": "Optimization: Significantly reduce animation update frequency to avoid excessive CPU usage", "缓存当前选中的节点": "Cache the currently selected node", "每 200ms 更新一次动画(降低频率从100ms到200ms)": "Update animation every 200ms (reduce frequency from 100ms to 200ms)", "第一次时,找出所有当前选中的节点并缓存": "The first time, find all currently selected nodes and cache them", "**更新连接线位置(确保始终跟随节点)**": "**Update connection line position (make sure to always follow the node)**", "只更新缓存的当前选中节点,避免遍历所有节点": "Only update the currently selected node in the cache to avoid traversing all nodes", "只在需要时渲染,避免无效渲染": "Only render when needed to avoid invalid rendering", "弹出动画(展开子节点)": "Pop-up animation (expand child nodes)", "**递归收集所有应该显示的子节点**": "**Recursively collect all child nodes that should be displayed**", "如果子节点本身是展开状态(expanded !== false),则递归收集它的子节点": "If the child node itself is in the expanded state (expanded !== false), then recursively collect its child nodes", "**关键修复:如果这个子节点是展开状态,递归收集它的子节点**": "**Key fix: If this child node is in expanded state, recursively collect its child nodes**", "这样可以一次性展开所有层级": "This allows you to expand all levels at once", "弹出动画:从父节点位置开始,缩放+位移到目标位置": "Pop-up animation: starting from the parent node position, scaling + displacement to the target position", "**缩短单个节点动画时间,让多米诺效果更明显**": "**Shorten the animation time of a single node to make the domino effect more obvious**", "从 350ms 减少到 250ms": "Reduced from 350ms to 250ms", "**修复:保存每个子节点的目标位置和它的直接父节点位置**": "**Fix: Save the target position of each child node and its immediate parent node position**", "**关键修复:对于 Aiming 节点,重新计算 Z 轴位置**": "**Critical Fix: For Aiming nodes, recalculate Z axis position**", "找到父节点": "Find parent node", "重新计算 Aiming 节点的 Z 轴偏移": "Recalculate the Z-axis offset of the Aiming node", "与创建时保持一致(增加1倍)": "Remain the same as when it was created (increased by 1x)", "**新策略:A节点从父节点位置开始,向摄像机方向(+Z)依次排列**": "**New strategy: A nodes start from the position of the parent node and are arranged in sequence toward the camera direction (+Z)**", "**新布局:Aiming 节点的坐标重新计算**": "**New layout: Coordinates of Aiming nodes recalculated**", "X 轴:与父节点相同(Aiming 节点在同一水平位置)": "X-axis: Same as parent node (Aiming node is at the same horizontal position)", "Y 轴:与父节点相同(Aiming不向下一层,在Z轴延伸)": "Y axis: Same as the parent node (Aiming does not go down one level, but extends on the Z axis)", "使用重新计算的正确位置(X, Y, Z 都重新计算)": "Use recalculated correct positions (X, Y, Z are all recalculated)", "与父节点相同的 X": "Same X as parent node", "与父节点相同的 Y(不向下)": "Same Y as parent node (not down)", "从父节点向摄像机方向延伸": "Extends from the parent node toward the camera", "非 Aiming 节点:使用保存的原始位置": "Non-aiming nodes: use saved original position", "**关键修复:找到这个节点的直接父节点位置**": "**Key Fix: Find the direct parent node position of this node**", "而不是使用最顶层的 parentNodeData": "Instead of using the top-level parentNodeData", "如果有 parentNodeData(来自连接线逻辑),使用它": "If there is parentNodeData (from connection line logic), use it", "否则,尝试查找直接父节点": "Otherwise, try to find the direct parent node", "如果找不到,使用最顶层的父节点(兜底)": "If not found, use the top-most parent node (cover the bottom)", "使用 easeOutBack 缓动(轻微弹出效果)": "Easing using easeOutBack (slight pop effect)", "**大幅增加延迟,产生明显的多米诺骨牌效果**": "**Significantly increases latency, creating a noticeable domino effect**", "从 50ms 增加到 80ms,让每个节点间隔更明显": "Increased from 50ms to 80ms to make the interval between each node more obvious", "**如果动画还没开始(nodeProgress === 0),隐藏节点**": "**If the animation has not started yet (nodeProgress === 0), hide the node**", "从父节点位置插值到目标位置": "Interpolate from parent node position to target position", "缩放:从0.1放大到1": "Zoom: zoom in from 0.1 to 1", "更新节点位置和缩放": "Update node position and scale", "更新连接线": "Update connection line", "**动画完成,恢复正常位置并确保完全可见**": "**Animation complete, return to normal position and ensure full visibility**", "恢复位置": "restore location", "吸入动画(收起子节点)": "Inhale animation (collapse child nodes)", "收集所有直接子节点": "Collect all direct child nodes", "递归收集所有子节点": "Recursively collect all child nodes", "吸入动画:从当前位置缩放+位移到父节点位置,然后隐藏": "Inhalation animation: scale + displacement from current position to parent node position, then hide", "从 300ms 减少到 200ms": "Reduced from 300ms to 200ms", "保存每个子节点的起始位置": "Save the starting position of each child node", "使用 easeInCubic 缓动(加速吸入)": "Easing (accelerating inhalation) using easeInCubic", "**大幅增加反向延迟,产生明显的反向多米诺骨牌效果**": "**Significantly increases reverse latency, creating a noticeable reverse domino effect**", "从 35ms 增加到 60ms": "Increased from 35ms to 60ms", "从当前位置插值到父节点位置": "Interpolate from current position to parent node position", "缩放:从1缩小到0.1": "Zoom: from 1 to 0.1", "动画完成,完全隐藏所有节点": "The animation is completed and all nodes are completely hidden.", "销毁 3D 场景": "Destroy 3D scene", "移除点击事件": "Remove click event", "**移除双击事件**": "**Remove double click event**", "**移除鼠标移动事件**": "**Remove mouse movement events**", "**移除tooltip元素**": "**Remove tooltip element**", "停止动画循环": "Stop animation loop", "销毁控制器": "Destroy the controller", "清空场景": "Clear scene", "清空节点映射": "Clear node mapping", "清理缓存的当前节点": "Clear cached current node", "关闭对话输入弹窗": "Close the dialog input pop-up window", "执行打靶的核心逻辑(提取出来供对话模式和直接测试模式复用)": "Execute the core logic of target practice (extracted for reuse in dialogue mode and direct test mode)", "执行打靶的核心逻辑(支持对话模式,userMessage 为 null 时表示直接测试模式)": "Execute the core logic of target shooting (supports conversation mode, when userMessage is null, it indicates direct test mode)", "保存 userMessage,用于后续连发时保持 Chat 模式": "Save userMessage to maintain Chat mode for subsequent bursts of messages", "如果提供了 userMessage,添加到请求数据中": "If userMessage is provided, add to request data", "如果是继续聊天模式,传递历史记录": "If you continue chat mode, pass the history", "创建顶级战术,创建平行战术,创建子战术,重新瞄准": "Create top-level tactics, create parallel tactics, create sub-tactics, re-target", "确保 mode 字段正确设置(后端返回的是枚举值 1 或 2)": "Make sure the mode field is set correctly (the backend returns an enum value of 1 or 2)", "兼容旧数据": "Compatible with old data", "检查第一个结果的模式,如果不是 Chat 模式,清空保存的 userMessage": "Check the mode of the first result, if it is not Chat mode, clear the saved userMessage", "如果不是 Chat 模式,清空保存的 userMessage": "If it is not Chat mode, clear the saved userMessage", "版本记录 获取版本记录 树形数据": "Version record Get version record Tree data", "console.log('获取版本记录数据', res.data.data.rootNodeList)": "console.log('Get version record data', res.data.data.rootNodeList)", "树形数据格式化 参数data:要格式化的数据,child为要格式化数据的子数组值名": "Tree data formatting Parameter data: the data to be formatted, child is the subarray value name of the data to be formatted", "版本记录 查看": "Version record view", "版本记录 树形控件 过滤节点": "Version record tree control filter node", "版本记录 抽屉关闭": "Version history Drawer closed", "版本记录 是否公开": "Is the version record public?", "console.log('版本记录 是否公开:', itemData)": "console.log('Is the version record public:', itemData)", "to do 接口对接 async await": "to do interface docking async await", "提示敬请期待": "Tips to stay tuned", "版本记录 编辑": "Version record edit", "console.log('版本记录 编辑:', itemData)": "console.log('Version record editor:', itemData)", "关闭抽屉": "close drawer", "版本记录 生成代码": "version record generated code", "console.log('版本记录 生成代码:', itemData)": "console.log('Version record generated code:', itemData)", "版本记录 删除": "version record delete", "console.log('版本记录 删除:', itemData)": "console.log('Version record deleted:', itemData)", "this.$confirm('此操作将永久删除该靶道版本, 是否继续?', '提示', {": "this.$confirm('This operation will permanently delete the target version, do you want to continue?', 'Prompt', {", "// 对接接口 删除": "// docking interface delete", "message: '已取消删除'": "message: 'Deletion canceled'", "版本记录 查看备注": "Version record View notes", "console.log('版本记录 查看备注:', itemData)": "console.log('Version record view remarks:', itemData)", "获取输出列表": "Get output list", "平均分 _totalScore/promptResults 保留整数": "Average score _totalScore/promptResults retain integer", "保留整数": "Keep integer", "输出 保存评分": "Output Save Rating", "重新获取输出列表": "Retrieve output list", "清除AI评分加载状态": "Clear AI scoring loading status", "重新获取图表": "Retrieve chart", "输出 选中切换": "Output selected switch", "输出 显示评分视图": "Output Show scoring view", "如果是ai评分 不显示评分视图 如果没有预期结果则提醒设置预期结果": "If it is AI scoring, the scoring view will not be displayed. If there is no expected result, it will remind you to set the expected result.", "设置AI评分加载状态": "Set AI rating loading status", "todo 接口对接 重新评分": "todo interface docking re-score", "输出 切换ai评分": "Output switch ai rating", "输出 ai评分 增加结果行": "Output ai score Add result row", "输出 切换手动评分": "Output Toggle manual scoring", "如果提供了 userMessage,添加到参数中(用于保持 Chat 模式)": "Add to parameter if userMessage is provided (used to maintain Chat mode)", "配置 重置参数": "Configuration reset parameters", "todo 判断是否 记录 页面变化记录": "todo determines whether to record page change records", "console.log('配置参数 重置:', this.parameterViewList)": "console.log('Configuration parameters reset:', this.parameterViewList)", "配置 参数设置输入回调": "Configuration parameter setting input callback", "根据 item里面的参数 判断限制输入的内容": "Determine the content of the restricted input based on the parameters in the item", "字符串类型": "string type", "有滑动选择的 数据必须为数字": "Data with sliding selection must be numeric", "且小于sliderMax大于sliderMin保留位数与sliderStep一样": "and is less than sliderMax and is greater than sliderMin. The number of reserved digits is the same as sliderStep.", "配置 输入Prompt 重置": "Configuration Enter Prompt Reset", "console.log('输入Prompt 重置:', this.content)": "console.log('Enter Prompt to reset:', this.content)", "this.remarks = '' // prompt 输入的备注": "this.remarks = '' // prompt input comments", "删除模型 confirm": "Delete model confirm", "重置模型列表": "Reset model list", "prompt请求参数 关闭": "prompt request parameters close", "prompt请求参数 添加变量行btn": "prompt request parameter add variable line btn", "prompt请求参数 删除变量行btn": "prompt request parameter delete variable line btn", "prompt请求参数 提交": "prompt request parameters submit", "提示刷新完成": "Prompt refresh completed", "配置 获取模型 下拉列表数据": "Configure Get model drop-down list data", "构建label:模型名称 + 版本号(如果有)+ 部署名称(如果有)": "Build label: model name + version number (if any) + deployment name (if any)", "保留原始名称用于其他地方显示": "Keep the original name for display elsewhere", "保留部署名称": "Keep deployment name", "保留版本号": "Keep version number", "新增模型 dialog 关闭": "New model dialog close", "新增模型 dialog 提交": "Add new model dialog submission", "重新获取模型列表": "Retrieve model list", "提示添加成功": "Prompt added successfully", "关闭dialog": "close dialog", "关闭新增靶场 dialog": "Close the new shooting range dialog", "dialog 新增靶场 提交按钮": "dialog add shooting range submit button", "post 接口 /api/Senparc.Xncf.PromptRange/PromptRangeAppService/Xncf.PromptRange_PromptRangeAppService.AddAsync'": "post interface /api/Senparc.Xncf.PromptRange/PromptRangeAppService/Xncf.PromptRange_PromptRangeAppService.AddAsync'", "配置 获取靶场 下拉列表数据": "Configure Get Range Dropdown List Data", "获取靶道 下拉列表数据": "Get target lane dropdown list data", "find rangeName by id(与 promptField 比较时统一为字符串,避免 el-select 存 string 而接口返回 number 导致找不到靶场)": "find rangeName by id (unify it into a string when compared with promptField to avoid el-select storing string and the interface returning number, resulting in the range not being found)", "导出 树形数据": "Export tree data", "靶场名称": "Range name", "靶场id": "shooting range id", "获取 prompt 详情": "Get prompt details", "如果获取到的结果没有,则延续以往的expectedJson.": "If there is no result obtained, the previous expectedJson will be continued.", "参数覆盖": "Parameter override", "判断模型列表是否有选中的模型": "Determine whether there is a selected model in the model list", "模型覆盖": "Model coverage", "prompt请求参数": "prompt request parameters", "_variableDictJson不是空对象 就循环赋值": "_variableDictJson is not an empty object, so assign values ​​in a loop", "ai 期望结果里面增加接口返回内容": "Add the interface return content to the expected result of ai", "⭐ 新增:检查平均分并提示优化": "⭐ New: Check average score and prompt for optimization", "删除 prompt": "Delete prompt", "重新获取 靶道列表 如果删除的是当前选中的靶道,就重重置靶道选中值并重置模型、参数、输入内容、备注、输出列表、平均分、图表、ai评分预期结果": "Re-acquire the target lane list. If the currently selected target lane is deleted, reset the target lane selection value and reset the model, parameters, input content, notes, output list, average score, chart, and AI score expected results.", "重新获取prompt列表": "Re-obtain prompt list", "重新获取版本记录": "Retrieve version records", "删除成功": "Delete successfully", "修改 prompt 别名": "Modify prompt alias", "重新获取 prompt列表": "Re-obtain prompt list", "ai评分设置 dialog 新增结果行btn": "ai scoring setting dialog new result line btn", "ai评分设置 打开 dialog": "ai scoring settings open dialog", "// 如果没有预期结果就重置": "//Reset if no expected result", "label: '预期结果1',": "label: 'Expected result 1',", "判断 this.aiScoreForm.resultList 是否有值": "Determine whether this.aiScoreForm.resultList has a value", "关闭ai评分设置 dialog": "Close ai scoring settings dialog", "判断 this.aiScoreForm.resultList 对比详情 this.promptDetail.expectedResultsJson 是否有改动": "Determine whether the comparison details of this.aiScoreForm.resultList and this.promptDetail.expectedResultsJson have changed", "dialog ai评分设置 提交按钮": "dialog ai rating settings submit button", "重新获取详情": "Retrieve details", "复制 Prompt 测试结果": "Copy Prompt test results", "* 初始化代码块复制按钮": "* Initialization code block copy button", "* 为所有
 代码块添加复制按钮": "* Add copy button to all 
 code blocks", "使用 MutationObserver 监听 DOM 变化,自动为新增的代码块添加复制按钮": "Use MutationObserver to monitor DOM changes and automatically add copy buttons for new code blocks", "监听输出区域和对话历史区域": "Monitor output area and conversation history area", "初始添加": "initial addition", "* 为所有代码块添加复制按钮": "* Add copy button to all code blocks", "查找所有代码块 (输出区域 + 对话历史区域)": "Find all code blocks (output area + conversation history area)", "标记已添加,避免重复": "Tags have been added to avoid duplication", "创建复制按钮": "Create a copy button", "绑定点击事件": "Bind click event", "获取代码内容": "Get code content", "使用 CopyHelper 复制": "Copy using CopyHelper", "显示复制成功状态": "Show copy success status", "2秒后恢复": "Restore after 2 seconds", "降级方案:使用传统方法": "Downgrade option: Use traditional methods", "将按钮添加到 pre 元素中": "Add button to pre element", "* 降级复制代码方法": "* Downgrade copy code method", "* @param {string} code - 要复制的代码": "* @param {string} code - the code to copy", "* @param {HTMLElement} button - 复制按钮元素": "* @param {HTMLElement} button - copy button element", "自定义滚动条缩略图相关方法": "Methods related to customizing scroll bar thumbnails", "计算每个item的相对位置": "Calculate the relative position of each item", "计算在缩略图轨道中的位置(按比例)": "Calculate position in thumbnail track (proportionally)", "最小20px": "Minimum 20px", "判断item是否在可视区域内": "Determine whether the item is within the visible area", "提取时间部分,例如 \"2024-01-01 10:30:45\" => \"10:30\"": "Extract the time part, for example \"2024-01-01 10:30:45\" => \"10:30\"", "获取最终评分(使用系统的finalScore字段)": "Get the final score (using the system's finalScore field)", "直接使用系统的finalScore字段,这是被标记为红色的那个分数": "Directly use the system's finalScore field, which is the score marked in red", "获取评分等级的样式类": "Get the style class of the rating level", "8-10分:优秀": "8-10 points: excellent", "6-8分:良好": "6-8 points: good", "4-6分:中等": "4-6 points: medium", "2-4分:较低": "2-4 points: lower", "0-2分:差": "0-2 points: poor", "获取数据条的宽度样式(Excel风格)": "Get the width style of the data bar (Excel style)", "0-10分映射到0-100%": "0-10 points map to 0-100%", "获取缩略图的工具提示文本": "Get the tooltip text of the thumbnail", "判断finalScore等于哪个评分,那个就是最终评分类型": "Determine which score the finalScore is equal to, that is the final score type", "处理输出区域的鼠标移动事件 - 判断是否在右半侧": "Handle mouse movement events in the output area - determine whether it is on the right half", "计算鼠标相对于输出区域的位置": "Calculate mouse position relative to output area", "只在右半侧显示滚动条": "Show scrollbar only on right half", "处理鼠标离开输出区域": "Handle mouse leaving the output area", "========== 区域宽度拖动调整功能 ==========": "========== Area width drag adjustment function ==========", "从localStorage加载保存的宽度设置": "Load saved width settings from localStorage", "保存宽度设置到localStorage": "Save width settings to localStorage", "开始拖动左侧分隔条": "Start dragging the left divider bar", "开始拖动右侧分隔条": "Start dragging the right divider bar", "处理拖动": "Handle drag", "调整左侧区域宽度": "Adjust left area width", "限制在280-600px之间": "Limit to 280-600px", "调整中间区域宽度": "Adjust the width of the middle area", "限制在320-800px之间": "Limit to 320-800px", "停止拖动": "Stop dragging", "保存当前宽度设置": "Save current width setting", "双击分隔条还原默认宽度": "Double-click the separator bar to restore the default width", "恢复默认宽度": "Restore default width", "清除localStorage中保存的设置": "Clear saved settings in localStorage", "添加视觉反馈动画": "Add visual feedback animation", "显示提示消息": "Show prompt message", "========== Prompt 对比功能 ==========": "========== Prompt comparison function ==========", "打开对比对话框(可选预设Prompt A)": "Open the comparison dialog (optional preset Prompt A)", "阻止事件冒泡,防止触发el-select的下拉": "Prevent events from bubbling and triggering the el-select drop-down", "Prompt A 应该是当前已经选中显示的 Prompt": "Prompt A should be the currently selected and displayed Prompt", "Prompt B 是点击\"对比\"按钮的对应 Prompt": "Prompt B is the corresponding prompt for clicking the \"Compare\" button", "如果传入了item,则将其设置为Prompt B": "If item is passed in, set it to Prompt B", "item.value 是 el-option 的值,对应 Prompt 的 ID": "item.value is the value of el-option, corresponding to the ID of Prompt", "打开对话框": "Open dialog", "加载对比Prompt A的数据": "Load data comparing Prompt A", "检查是否选择了同一个Prompt": "Check if the same prompt is selected", "加载对比Prompt B的数据": "Load data comparing Prompt B", "获取单个Prompt的详细信息": "Get details of a single prompt", "交换Prompt A和B": "Swap Prompt A and B", "交换ID": "Exchange ID", "交换数据": "exchange data", "检查评分是否存在": "Check if the rating exists", "获取前缀(直接从 prefix 字段获取)": "Get the prefix (directly from the prefix field)", "获取后缀(直接从 suffix 字段获取)": "Get the suffix (directly from the suffix field)", "跳转到指定的Prompt(完全模拟手动点击靶道选择的行为)": "Jump to the specified Prompt (completely simulates the behavior of manually clicking on the target channel selection)", "1. 先获取完整的Prompt数据": "1. Get the complete Prompt data first", "2. 从fullVersion解析靶场名称(格式: 靶场-靶道-战术)": "2. Parse the shooting range name from fullVersion (format: shooting range-range-tactics)", "3. 查找对应的靶场ID": "3. Find the corresponding shooting range ID", "4. 关闭对比对话框(先关闭,避免干扰后续操作)": "4. Close the comparison dialog box (close it first to avoid interfering with subsequent operations)", "5. 重置 pageChange 标记,避免触发\"是否保存草稿\"的确认对话框": "5. Reset the pageChange mark to avoid triggering the \"Do you want to save the draft\" confirmation dialog box?", "6. 检查是否需要切换靶场": "6. Check whether you need to switch shooting ranges", "切换靶场(这会触发promptOpt的更新)": "Switch ranges (this triggers an update of promptOpt)", "等待promptOpt更新完成": "Wait for promptOpt update to complete", "7. 在promptOpt中查找对应的Prompt": "7. Find the corresponding prompt in promptOpt", "注意:promptOpt中的item.value才是正确的promptid": "Note: item.value in promptOpt is the correct promptid", "8. 设置正确的promptid(这会触发el-select的v-model更新)": "8. Set the correct promptid (this will trigger the v-model update of el-select)", "重要:再次确保 pageChange = false,因为切换靶场可能会触发它": "IMPORTANT: Again make sure pageChange = false as switching ranges may trigger this", "9. 手动触发 promptChangeHandel,完全模拟用户点击靶道下拉选择": "9. Manually trigger promptChangeHandel to completely simulate the user clicking on the target drop-down selection", "这会:": "This will:", "- 更新 sendBtns(根据是否是草稿)": "- Update sendBtns (depending on whether it is a draft)", "- 清空 AI 评分标准": "- Clear AI scoring criteria", "- 调用 getPromptetail 获取完整详情(包括输出列表、评分、图表等)": "- Call getPromptetail to get complete details (including output list, ratings, charts, etc.)", "10. 显示成功提示": "10. Display success prompt", "检查字段是否有差异": "Check if fields are different", "处理null/undefined情况": "Handling null/undefined cases", "如果是对象或数组,进行深度比较": "If it is an object or array, perform a deep comparison", "格式化变量配置(从JSON字符串转为可读格式)": "Format variable configuration (convert from JSON string to readable format)", "生成Git风格的Prompt内容差异HTML": "Generate Git-style Prompt content diff HTML", "如果都为空": "if both are empty", "如果只有一个为空": "If only one is empty", "如果内容相同": "If the content is the same", "使用jsdiff库进行差异对比": "Difference comparison using jsdiff library", "如果diff库未加载,返回原始内容": "If the diff library is not loaded, return the original content", "生成Git风格的变量配置差异HTML": "Generate Git-style variable configuration diff HTML", "格式化JSON以便对比": "Format JSON for comparison", "保持原样": "stay as is", "渲染差异HTML(Git风格,支持行内单词高亮)": "Rendering differential HTML (Git style, supports inline word highlighting)", "移除最后的空行": "Remove last blank line", "检查是否是相邻的删除和新增(可以做行内diff)": "Check whether there are adjacent deletions and additions (inline diff can be done)", "行内单词级别差异": "Inline word level differences", "跳过下一个part(因为已经处理了)": "Skip the next part (because it has already been processed)", "常规的整行差异": "regular whole line diff", "新增的内容(绿色背景)- 仅在B侧显示": "New content (green background) - only shown on side B", "A侧不显示新增内容": "New content is not displayed on side A", "删除的内容(红色背景)- 仅在A侧显示": "Deleted content (red background) - only shown on side A", "B侧不显示删除内容": "Deleted content is not displayed on side B", "渲染行内单词级别差异": "Rendering intraline word-level differences", "A侧:高亮删除的单词": "Side A: Highlight deleted words", "B侧:高亮新增的单词": "Side B: Highlight newly added words", "HTML转义工具函数": "HTML escape tool function", "========== contenteditable编辑器方法(简洁版)==========": "========== contenteditable editor method (concise version) ==========", "获取编辑器中的纯文本": "Get plain text in editor", "生成带高亮的HTML": "Generate HTML with highlighting", "获取前缀和后缀(从当前编辑的prompt参数)": "Get the prefix and suffix (from the currently edited prompt parameter)", "调试日志": "debug log", "没有前缀后缀,直接返回转义的HTML": "There is no prefix or suffix, and the escaped HTML is returned directly.", "获取所有已定义的变量名(不带前后缀)": "Get all defined variable names (without prefix and suffix)", "转义正则特殊字符": "Escape regular special characters", "构建正则:匹配 {{$varName}},使用非贪婪匹配": "Build regex: match {{$varName}}, use non-greedy matching", "例如:prefix={{$, suffix=}}, 正则为:\\{\\{\\$(.+?)\\}\\}": "For example: prefix={{$, suffix=}}, the regular expression is: \\{\\{\\$(.+?)\\}\\}", "使用 exec 逐个匹配": "Use exec to match one by one", "完整匹配,如 {{$prefix}}": "Complete match, such as {{$prefix}}", "捕获组,如 prefix": "Capturing groups, such as prefix", "添加匹配前的文本(HTML转义并处理换行)": "Add text before match (HTML escape and handle newlines)", "处理换行:将换行符替换为 
": "Handle newlines: replace newlines with
", "注意:不再移除 span 标签前的尾随空白字符,因为用户可能有意输入空格": "NOTE: Trailing whitespace characters before span tags are no longer removed as the user may enter the space intentionally", "将换行符替换为
(保留所有空格和制表符,因为用户可能有意输入)": "Replace newlines with
(keep all spaces and tabs as the user may have entered them intentionally)", "判断变量是否已定义": "Determine whether the variable has been defined", "添加高亮的变量(HTML转义,但不包含换行)": "Add highlighted variables (HTML escaped, but without newlines)", "确保 span 标签前后没有空白字符,直接拼接(不在 HTML 字符串中添加换行或空格)": "Make sure there are no whitespace characters before and after the span tag, and splice it directly (do not add newlines or spaces in the HTML string)", "添加剩余文本(HTML转义并处理换行)": "Add remaining text (HTML escaping and handling newlines)", "如果剩余文本开头有空白字符(空格、制表符等),先移除,避免在 span 后面产生空白": "If there are whitespace characters (spaces, tabs, etc.) at the beginning of the remaining text, remove them first to avoid creating whitespace after span", "但保留换行符,因为需要转换为
": "But keep the newlines as they need to be converted to
", "移除开头的空白字符(但保留换行符)": "Remove leading whitespace characters (but keep newlines)", "将换行符替换为
": "Replace newlines with
", "注意:不再清理 span 标签前后的空白字符(空格、制表符等)": "Note: Whitespace characters (spaces, tabs, etc.) before and after the span tag are no longer cleaned", "因为用户可能在 span 前后输入空格,这些空格应该保留": "Because the user may enter spaces before and after the span, these spaces should be preserved", "清理函数 cleanupHighlightBrTags 会处理多余的空白文本节点": "The cleanup function cleanupHighlightBrTags will handle excess blank text nodes", "转义正则表达式特殊字符": "Escape regular expression special characters", "保存光标位置(返回字符偏移量,基于纯文本)": "Save cursor position (returns character offset, based on plain text)", "使用与 restoreCaretPosition 完全相同的遍历逻辑,确保一致性": "Use exactly the same traversal logic as restoreCaretPosition to ensure consistency", "使用与 restoreCaretPosition 完全相同的遍历逻辑": "Uses exactly the same traversal logic as restoreCaretPosition", "找到了目标文本节点,加上偏移量": "The target text node is found, plus the offset", "不是目标节点,加上整个节点的长度": "Not the target node, plus the length of the entire node", "检查光标是否在这个 BR 标签之后": "Check if the cursor is after this BR tag", "如果 targetContainer 是 BR 的父节点,且 targetOffset 指向这个 BR 之后": "If targetContainer is the parent node of BR and targetOffset points to after this BR", "检查 targetOffset 是否指向这个 BR 之后": "After checking whether targetOffset points to this BR", "光标在这个 BR 标签之后": "The cursor is after this BR tag", "BR 标签算作一个字符(换行符)": "BR tag counts as one character (newline character)", "检查光标是否在这个元素节点的子节点之间": "Checks whether the cursor is between the child nodes of this element node", "光标在这个节点的子节点之间": "The cursor is between the child nodes of this node", "使用与 restoreCaretPosition 完全相同的遍历逻辑计算字符数": "Count the number of characters using exactly the same traversal logic as restoreCaretPosition", "递归遍历子节点,使用相同的逻辑": "Recursively traverse child nodes, using the same logic", "对于元素节点,递归遍历子节点": "For element nodes, recursively traverse the child nodes", "恢复光标位置(基于纯文本偏移量)": "Restore cursor position (based on plain text offset)", "使用与 saveCaretPosition 完全相同的遍历逻辑,确保一致性": "Use exactly the same traversal logic as saveCaretPosition to ensure consistency", "使用与 saveCaretPosition 完全相同的遍历逻辑": "Uses exactly the same traversal logic as saveCaretPosition", "光标在这个文本节点内": "The cursor is within this text node", "光标在 BR 标签之前": "The cursor is before the BR tag", "光标在 BR 标签之后": "The cursor is after the BR tag", "注意:span 等内联元素不影响字符计数,只计算其内部的文本": "Note: Inline elements such as span do not affect the character count, only the text inside them is counted.", "Fallback: 放在末尾": "Fallback: put at the end", "如果找不到精确位置,尝试将光标放在末尾": "If you can't find the exact location, try placing the cursor at the end", "折叠到末尾": "fold to end", "处理粘贴事件": "Handle paste event", "标记正在粘贴": "Tag is pasting", "清除之前的防抖定时器": "Clear previous anti-shake timer", "在粘贴前保存光标位置(使用与 saveCaretPosition 相同的方法)": "Save cursor position before pasting (using the same method as saveCaretPosition)", "使用与 saveCaretPosition 相同的遍历方法来计算位置": "Use the same traversal method as saveCaretPosition to calculate the position", "获取粘贴的纯文本内容": "Get pasted plain text content", "等待粘贴内容插入完成后再处理": "Wait for the pasted content to be inserted before processing", "使用 requestAnimationFrame 确保粘贴操作完全完成": "Use requestAnimationFrame to ensure the paste operation is fully completed", "计算粘贴后的光标位置:粘贴开始位置 + 粘贴文本长度": "Calculate the cursor position after pasting: paste start position + paste text length", "检查内容是否真的发生了变化": "Check if the content has actually changed", "更新 content": "Update content", "如果内容真的发生了变化,触发状态更新(从\"连发\"切换到\"打靶\")": "If the content really changes, trigger a status update (switch from \"Burst\" to \"Target Shooting\")", "应用高亮,并传入预期的光标位置": "Apply highlighting, passing in the expected cursor position", "处理键盘按键事件(特别是回车键)": "Handle keyboard key events (especially the Enter key)", "如果是回车键,需要特殊处理": "If it is the Enter key, special processing is required", "标记正在输入回车": "Mark Enter Enter", "在回车键插入 BR 标签之前,保存当前光标位置": "Save the current cursor position before entering the BR tag", "使用与 saveCaretPosition 相同的方法计算位置": "Calculate the position using the same method as saveCaretPosition", "等待回车键插入 BR 标签完成后再处理": "Wait for the Enter key to be inserted into the BR tag before processing.", "回车后,光标应该在 BR 标签之后,位置应该是 enterStartPos + 1": "After pressing Enter, the cursor should be after the BR label and the position should be enterStartPos + 1", "使用 applyHighlightWithCaretPos 来应用高亮并恢复光标位置": "Use applyHighlightWithCaretPos to apply highlight and restore cursor position", "用户输入时(立即更新高亮,使用短防抖优化性能)": "On user input (immediately updates highlights, uses short stabilization to optimize performance)", "IME输入中不处理": "IME input is not processed", "粘贴操作中不处理,由 handlePaste 处理": "Not processed during paste operation, handled by handlePaste", "回车操作中不处理,由 handleKeyDown 处理": "It is not processed during the carriage return operation and is processed by handleKeyDown.", "使用较短的防抖时间(150ms)来平衡性能和响应性": "Use a short anti-shake time (150ms) to balance performance and responsiveness", "这样用户输入、删除、剪切等操作时,高亮会立即更新": "In this way, the highlight will be updated immediately when the user enters, deletes, cuts, etc.", "清理 var-highlight span 周围多余的
标签和空白文本节点,并规范化 DOM": "Clean up excess
tags and blank text nodes around var-highlight span and normalize DOM", "查找所有 var-highlight span 标签(使用 Array.from 创建副本,避免动态查询问题)": "Find all var-highlight span tags (use Array.from to create a copy to avoid dynamic query issues)", "清理 span 前面的空白文本节点": "Clean up the blank text nodes in front of the span", "只移除完全空白的文本节点(不包含任何可见字符),保留包含用户输入空格的文本节点": "Only remove text nodes that are completely blank (containing no visible characters), leaving text nodes that contain user-entered spaces", "只移除完全空白的文本节点(只包含空白字符且长度为0,或者是浏览器自动添加的空白)": "Only remove completely blank text nodes (containing only whitespace characters and a length of 0, or whitespace automatically added by the browser)", "保留包含用户输入空格的文本节点(即使只有空格,也应该保留,因为用户可能有意输入空格)": "Preserve text nodes that contain user-entered spaces (even if there are only spaces, they should be preserved because the user may enter spaces intentionally)", "完全空的文本节点,移除": "Completely empty text nodes, removed", "有内容的文本节点,保留(包括只包含空格的节点,因为用户可能有意输入空格)": "Text nodes with content, reserved (including nodes containing only spaces, as the user may enter spaces intentionally)", "其他类型的节点(包括 BR 标签),停止清理": "Nodes of other types (including BR tags), stop cleaning", "清理 span 后面的空白文本节点": "Clean up the blank text nodes after span", "只移除完全空白的文本节点,保留包含用户输入空格的文本节点": "Only remove text nodes that are completely blank, leaving text nodes that contain user-entered spaces", "只移除完全空白的文本节点": "Remove only completely blank text nodes", "保留包含用户输入空格的文本节点": "Preserve text nodes containing user-entered spaces", "遇到非文本节点(包括 BR 标签),停止清理": "Stop cleaning when encountering non-text nodes (including BR tags)", "BR 标签是用户输入的换行,不应该被移除": "The BR tag is a user-entered line break and should not be removed", "规范化 DOM:合并相邻的文本节点": "Normalize DOM: merge adjacent text nodes", "这可以确保 span 标签紧贴文本节点,没有中间的空白文本节点": "This ensures that the span tags fit snugly into the text nodes, with no intervening white space text nodes", "应用高亮(使用指定的光标位置,用于粘贴等操作)": "Apply highlighting (using the specified cursor position, for operations such as pasting)", "获取当前文本": "Get the current text", "同步 content(但不触发状态变化,因为状态变化应该在 handleEditorInput、handlePaste、handleKeyDown 中处理)": "Synchronize content (but do not trigger state changes, because state changes should be handled in handleEditorInput, handlePaste, handleKeyDown)", "这里只负责高亮显示": "This is only responsible for highlighting", "生成高亮HTML": "Generate highlighted HTML", "更新HTML": "Update HTML", "使用 requestAnimationFrame 确保 DOM 完全更新后再清理和恢复光标": "Use requestAnimationFrame to ensure the DOM is fully updated before cleaning and restoring the cursor", "清理多余的
标签和空白文本节点": "Clean up redundant
tags and blank text nodes", "使用 $nextTick 确保清理完成后再恢复光标": "Use $nextTick to ensure cleanup is complete before restoring the cursor", "使用 innerText 来计算文本长度,确保与 saveCaretPosition 一致": "Use innerText to calculate the text length and ensure it is consistent with saveCaretPosition", "注意:不再在这里调用 promptChangeHandel": "NOTE: promptChangeHandel is no longer called here", "内容变化的状态更新应该在 handleEditorInput、handlePaste、handleKeyDown 等实际输入事件中处理": "Status updates of content changes should be handled in actual input events such as handleEditorInput, handlePaste, handleKeyDown, etc.", "应用高亮(保存光标位置)": "Apply highlighting (save cursor position)", "检查编辑器是否有焦点": "Check if the editor has focus", "获取当前文本(在保存光标位置之前获取,确保文本一致性)": "Get the current text (get it before saving the cursor position to ensure text consistency)", "同步 content(但不触发状态变化,因为状态变化应该在 handleEditorInput 中处理)": "Synchronize content (but not trigger state changes, since state changes should be handled in handleEditorInput)", "只有在编辑器有焦点时才保存光标位置": "Save cursor position only when editor has focus", "使用基于文本内容的方法,而不是 DOM 遍历,确保与恢复时一致": "Use a text content-based approach rather than DOM traversal to ensure consistency with recovery", "创建一个从编辑器开始到光标位置的 range": "Create a range from the beginning of the editor to the cursor position", "使用 textContent 来计算位置(不包括 BR 标签,BR 标签会在遍历时单独计算)": "Use textContent to calculate position (excluding BR tags, which are calculated separately during traversal)", "但我们需要考虑 BR 标签,所以使用遍历 DOM 的方法": "But we need to consider the BR tag, so we use the method of traversing the DOM", "只有在编辑器有焦点时才恢复光标位置": "Only restore cursor position when editor has focus", "恢复光标位置(使用相同的遍历逻辑)": "Restore cursor position (using the same traversal logic)", "这里只负责高亮显示,避免在 blur 时误触发状态变化": "This is only responsible for highlighting to avoid accidentally triggering state changes during blur.", "辅助方法:根据ID获取名称": "Helper method: Get name based on ID", "使用 NameHelper 工具类": "Using the NameHelper utility class", "从promptOpt中查找对应的靶道(使用自定义字段名 idkey)": "Find the corresponding target channel from promptOpt (use custom field name idkey)", "* 复制功能辅助工具": "* Copy function auxiliary tool", "* 提供复制文本到剪贴板的功能,兼容多种浏览器": "* Provides the function of copying text to the clipboard, compatible with multiple browsers", "* Copy 辅助工具集": "*Copy auxiliary toolset", "* 使用对象字面量模式(无需实例化)": "* Use object literal mode (no instantiation required)", "* 复制文本到剪贴板": "* Copy text to clipboard", "* 优先使用现代 Clipboard API,降级使用传统方法": "* Prioritize using modern Clipboard API, downgrading to traditional methods", "* @param {string} text - 要复制的文本": "* @param {string} text - the text to copy", "* @param {string} [successMessage='复制成功'] - 成功提示消息": "* @param {string} [successMessage='Copy successfully'] - Success message", "* @param {string} [errorMessage='复制失败'] - 失败提示消息": "* @param {string} [errorMessage='Copy failed'] - Failure message", "* @param {boolean} [showMessage=true] - 是否显示提示消息": "* @param {boolean} [showMessage=true] - whether to display prompt messages", "* @returns {boolean} 是否复制成功": "* @returns {boolean} Whether the copy was successful", "方法1: 使用现代 Clipboard API (需要 HTTPS 或 localhost)": "Method 1: Use the modern Clipboard API (requires HTTPS or localhost)", "降级到传统方法": "Downgrade to traditional methods", "方法2: 使用传统方法": "Method 2: Use traditional methods", "* 降级复制方法(使用 document.execCommand)": "* Downgrade copy method (using document.execCommand)", "* 复制 Prompt 结果": "* Copy Prompt results", "* 专门用于复制 Prompt 结果的便捷方法": "* Convenience method specifically used to copy Prompt results", "* @param {Object} item - Prompt 结果对象": "* @param {Object} item - Prompt result object", "* @param {boolean} [rawResult=false] - 是否复制原始结果": "* @param {boolean} [rawResult=false] - whether to copy the original result", "* 复制对象为 JSON 字符串": "* Copy object as JSON string", "* 将对象序列化为格式化的 JSON 后复制": "* Copy the object after serializing it to formatted JSON", "* @param {*} obj - 要复制的对象": "* @param {*} obj - the object to be copied", "* @param {number} [indent=2] - 缩进空格数": "* @param {number} [indent=2] - Number of indent spaces", "* 复制数组内容": "* Copy the contents of the array", "* 将数组元素用换行符连接后复制": "* Copy the array elements after concatenating them with newline characters", "* @param {Array} arr - 要复制的数组": "* @param {Array} arr - the array to copy", "* @param {string} [separator='\\n'] - 分隔符": "* @param {string} [separator='\\n'] - separator", "* 复制 HTML 内容(保留格式)": "* Copy HTML content (preserve formatting)", "* @param {string} html - HTML 内容": "* @param {string} html - HTML content", "创建临时容器": "Create temporary container", "选中内容": "Selected content", "* 显示消息提示": "* Display message prompt", "* 使用 Element UI 的 Message 组件": "* Use the Message component of Element UI", "* @param {string} message - 消息内容": "* @param {string} message - message content", "* @param {string} type - 消息类型 (success/error/warning/info)": "* @param {string} type - message type (success/error/warning/info)", "* 检查复制功能是否可用": "* Check if the copy function is available", "* @returns {boolean} 是否可用": "* @returns {boolean} whether available", "暴露到全局命名空间": "Exposed to the global namespace", "* 日期时间辅助工具": "* Date and time auxiliary tools", "* 提供日期格式化、相对时间显示等功能": "* Provides date formatting, relative time display and other functions", "* 日期辅助工具集": "*Date auxiliary tool set", "* 格式化日期": "* Format date", "* 将日期对象、字符串或时间戳格式化为指定格式的字符串": "* Format a date object, string or timestamp into a string in the specified format", "* @param {Date|string|number} date - 日期对象、字符串或时间戳": "* @param {Date|string|number} date - date object, string or timestamp", "* @param {string} [format='YYYY-MM-DD HH:mm:ss'] - 格式字符串": "* @param {string} [format='YYYY-MM-DD HH:mm:ss'] - format string", "* @returns {string} 格式化后的日期字符串": "* @returns {string} Formatted date string", "* 支持的格式标记:": "*Supported format tags:", "* - YYYY: 四位年份": "* - YYYY: four-digit year", "* - MM: 两位月份 (01-12)": "* - MM: two digit month (01-12)", "* - DD: 两位日期 (01-31)": "* - DD: two digit date (01-31)", "* - HH: 两位小时 (00-23)": "* - HH: two digit hours (00-23)", "* - mm: 两位分钟 (00-59)": "* - mm: two digit minutes (00-59)", "* - ss: 两位秒 (00-59)": "* - ss: two digit seconds (00-59)", "检查日期是否有效": "Check if the date is valid", "辅助函数:补零": "Auxiliary function: zero padding", "* 格式化聊天时间(相对时间)": "* Format chat time (relative time)", "* 将时间转换为相对当前时间的描述(如\"刚刚\"、\"5分钟前\")": "* Convert the time into a description relative to the current time (such as \"just\", \"5 minutes ago\")", "* @returns {string} 相对时间描述": "* @returns {string} relative time description", "* 格式化时间字符串": "* Format time string", "* 提取并格式化 ISO 8601 时间字符串": "* Extract and format ISO 8601 time string", "* @param {string} timeStr - 时间字符串": "* @param {string} timeStr - time string", "* @returns {string} 格式化后的时间字符串": "* @returns {string} Formatted time string", "* 获取时间戳": "* Get timestamp", "* 返回当前时间的时间戳(毫秒)": "* Returns the timestamp of the current time (milliseconds)", "* @returns {number} 时间戳": "* @returns {number} timestamp", "* 计算时间差": "* Calculate time difference", "* 计算两个时间之间的差值": "* Calculate the difference between two times", "* @param {Date|string|number} startDate - 开始时间": "* @param {Date|string|number} startDate - start time", "* @param {Date|string|number} endDate - 结束时间": "* @param {Date|string|number} endDate - end time", "* @returns {Object} 时间差对象 {days, hours, minutes, seconds, milliseconds}": "* @returns {Object} time difference object {days, hours, minutes, seconds, milliseconds}", "* 判断是否为今天": "* Determine whether it is today", "* @param {Date|string|number} date - 要判断的日期": "* @param {Date|string|number} date - the date to be judged", "* @returns {boolean} 是否为今天": "* @returns {boolean} whether it is today", "* 判断是否为昨天": "* Determine whether it is yesterday", "* @returns {boolean} 是否为昨天": "* @returns {boolean} whether it is yesterday", "* 获取月份的天数": "* Get the number of days in the month", "* @param {number} year - 年份": "* @param {number} year - year", "* @param {number} month - 月份 (1-12)": "* @param {number} month - month (1-12)", "* @returns {number} 该月的天数": "* @returns {number} The number of days in the month", "* 格式化持续时间": "*Format duration", "* 将毫秒数转换为可读的持续时间格式": "* Convert milliseconds to a readable duration format", "* @param {number} milliseconds - 毫秒数": "* @param {number} milliseconds - number of milliseconds", "* @returns {string} 格式化后的持续时间(如 \"2小时30分钟\")": "* @returns {string} formatted duration (such as \"2 hours and 30 minutes\")", "* HTML 操作辅助工具": "* HTML operation assistance tool", "* 提供 HTML 转义、UUID 生成、防抖节流等通用功能": "* Provides common functions such as HTML escaping, UUID generation, anti-shake throttling, etc.", "* HTML 辅助工具集": "*HTML auxiliary toolset", "* HTML 转义": "* HTML escape", "* 将文本中的特殊字符转换为 HTML 实体": "* Convert special characters in text to HTML entities", "* @param {string} text - 要转义的文本": "* @param {string} text - the text to escape", "* @returns {string} 转义后的文本": "* @returns {string} escaped text", "* 正则表达式转义": "* Regular expression escape", "* 转义正则表达式中的特殊字符": "* Escape special characters in regular expressions", "* @param {string} str - 要转义的字符串": "* @param {string} str - the string to escape", "* @returns {string} 转义后的字符串": "* @returns {string} escaped string", "* 生成 UUID (v4)": "* Generate UUID (v4)", "* 生成符合 RFC4122 标准的 UUID": "* Generate UUID compliant with RFC4122 standard", "* @returns {string} UUID 字符串": "* @returns {string} UUID string", "* 格式化文件大小": "* Format file size", "* 将字节数转换为人类可读的格式": "* Convert byte count to human-readable format", "* @param {number} bytes - 字节数": "* @param {number} bytes - number of bytes", "* @returns {string} 格式化后的文件大小(如 \"1.23 MB\")": "* @returns {string} formatted file size (such as \"1.23 MB\")", "* 防抖函数": "* Anti-shake function", "* 延迟执行函数,如果在延迟期间再次调用,则重新计时": "* Delay execution function, if called again during the delay period, retime", "* @param {Function} func - 要防抖的函数": "* @param {Function} func - the function to be anti-shake", "* @param {number} wait - 延迟时间(毫秒)": "* @param {number} wait - delay time (milliseconds)", "* @returns {Function} 防抖后的函数": "* @returns {Function} function after anti-shake", "* 节流函数": "* Throttle function", "* 限制函数在指定时间内只能执行一次": "* Limit the function to be executed only once within a specified time", "* @param {Function} func - 要节流的函数": "* @param {Function} func - the function to throttle", "* @param {number} limit - 时间限制(毫秒)": "* @param {number} limit - time limit (milliseconds)", "* @returns {Function} 节流后的函数": "* @returns {Function} The function after throttling", "* 深度克隆对象": "* Deep clone object", "* 创建对象的深拷贝": "* Create a deep copy of the object", "* @param {*} obj - 要克隆的对象": "* @param {*} obj - the object to be cloned", "* @returns {*} 克隆后的对象": "* @returns {*} cloned object", "* 获取 URL 查询参数": "* Get URL query parameters", "* 从 URL 中提取指定的查询参数值": "* Extract the specified query parameter value from the URL", "* @param {string} name - 参数名": "* @param {string} name - parameter name", "* @param {string} [url] - URL 字符串,默认为当前页面 URL": "* @param {string} [url] - URL string, defaults to the current page URL", "* @returns {string|null} 参数值,不存在则返回 null": "* @returns {string|null} parameter value, if it does not exist, return null", "* 判断是否为空值": "* Determine whether it is a null value", "* 检查值是否为 null、undefined、空字符串、空数组或空对象": "* Check whether the value is null, undefined, empty string, empty array or empty object", "* @param {*} value - 要检查的值": "* @param {*} value - the value to check", "* @returns {boolean} 是否为空": "* @returns {boolean} whether it is empty", "* 名称查询辅助工具": "* Name query auxiliary tool", "* 提供统一的名称查询功能,避免代码重复": "* Provide a unified name query function to avoid code duplication", "* 名称查询辅助工具集": "* Name query auxiliary tool set", "* 通用的名称查询方法": "* Universal name query method", "* 从选项数组中根据 ID 查找对应的名称": "* Find the corresponding name based on ID from the options array", "* @param {Array} options - 选项数组": "* @param {Array} options - array of options", "* @param {string|number} id - 要查询的 ID": "* @param {string|number} id - the ID to be queried", "* @param {string} [defaultName='未知'] - 默认名称(找不到时返回)": "* @param {string} [defaultName='Unknown'] - Default name (returned if not found)", "* @param {string} [valueKey='value'] - ID 字段名": "* @param {string} [valueKey='value'] - ID field name", "* @param {string} [labelKey='label'] - 名称字段名": "* @param {string} [labelKey='label'] - Name field name", "* @returns {string} 查找到的名称或默认名称": "* @returns {string} The found name or the default name", "如果不是数组,返回默认值": "If not an array, returns the default value", "查找匹配的项": "Find matching items", "* 创建名称查询器": "* Create name query", "* 返回一个绑定了特定选项数组的查询函数": "* Returns a query function bound to a specific option array", "* @param {string} [defaultName='未知'] - 默认名称": "* @param {string} [defaultName='Unknown'] - default name", "* @returns {Function} 查询函数,接受 id 参数,返回对应名称": "* @returns {Function} query function, accepts the id parameter and returns the corresponding name", "* 批量获取名称": "* Get names in batches", "* 根据 ID 数组批量查询名称": "* Query names in batches based on ID array", "* @param {Array} ids - ID 数组": "* @param {Array} ids - ID array", "* @returns {Array} 名称数组": "* @returns {Array} name array", "* 根据名称查找 ID": "* Find ID based on name", "* 与 getName 相反,根据名称查找对应的 ID": "* Contrary to getName, find the corresponding ID based on the name", "* @param {string} name - 要查询的名称": "* @param {string} name - the name to be queried", "* @param {string|number} [defaultId=null] - 默认 ID(找不到时返回)": "* @param {string|number} [defaultId=null] - Default ID (returned if not found)", "* @returns {string|number|null} 查找到的 ID 或默认 ID": "* @returns {string|number|null} The found ID or default ID", "* 检查 ID 是否存在": "* Check if ID exists", "* @param {string|number} id - 要检查的 ID": "* @param {string|number} id - the ID to check", "* @returns {boolean} ID 是否存在": "* @returns {boolean} Whether the ID exists", "* 获取完整的选项对象": "* Get the complete options object", "* 根据 ID 获取完整的选项对象(不仅仅是名称)": "* Get the complete option object based on ID (not just the name)", "* @returns {Object|null} 找到的选项对象或 null": "* @returns {Object|null} the option object found or null", "* LocalStorage 存储辅助工具": "* LocalStorage storage auxiliary tool", "* 提供简化的本地存储操作,自动处理 JSON 序列化": "* Provide simplified local storage operations and automatically handle JSON serialization", "* Storage 辅助工具集": "* Storage auxiliary toolset", "* 保存数据到 localStorage": "* Save data to localStorage", "* 自动进行 JSON 序列化": "* Automatic JSON serialization", "* @param {string} key - 键名": "* @param {string} key - key name", "* @param {*} value - 值(会自动 JSON 序列化)": "* @param {*} value - value (automatically JSON serialized)", "* @returns {boolean} 是否保存成功": "* @returns {boolean} Whether the save was successful", "* 从 localStorage 读取数据": "* Read data from localStorage", "* 自动进行 JSON 反序列化": "* Automatic JSON deserialization", "* @param {*} [defaultValue=null] - 默认值(键不存在或解析失败时返回)": "* @param {*} [defaultValue=null] - Default value (returned when the key does not exist or parsing fails)", "* @returns {*} 读取到的值或默认值": "* @returns {*} read value or default value", "* 从 localStorage 移除数据": "* Remove data from localStorage", "* @returns {boolean} 是否移除成功": "* @returns {boolean} Whether the removal was successful", "* 清空所有 localStorage 数据": "* Clear all localStorage data", "* @returns {boolean} 是否清空成功": "* @returns {boolean} Whether the clearing is successful", "* 检查键是否存在": "* Check if the key exists", "* @returns {boolean} 键是否存在": "* @returns {boolean} whether the key exists", "* 获取所有键名": "* Get all key names", "* @returns {Array} 所有键名的数组": "* @returns {Array} Array of all key names", "* 获取存储数据的数量": "* Get the amount of stored data", "* @returns {number} 存储数据的数量": "* @returns {number} The number of stored data", "* 获取所有数据": "* Get all data", "* @returns {Object} 包含所有键值对的对象": "* @returns {Object} An object containing all key-value pairs", "* 批量设置数据": "* Batch set data", "* @param {Object} data - 键值对对象": "* @param {Object} data - key-value pair object", "* @returns {boolean} 是否全部设置成功": "* @returns {boolean} Whether all settings are successful", "* 批量获取数据": "* Get data in batches", "* @param {Array} keys - 键名数组": "* @param {Array} keys - array of key names", "* @returns {Object} 包含请求的键值对的对象": "* @returns {Object} An object containing the requested key-value pairs", "* 批量移除数据": "* Remove data in batches", "* @returns {boolean} 是否全部移除成功": "* @returns {boolean} Whether all removals were successful", "* 检查 localStorage 是否可用": "* Check if localStorage is available", "* 获取存储空间使用情况": "* Get storage space usage", "假设 5MB 总容量(不同浏览器可能不同)": "Assuming 5MB total capacity (may vary between browsers)", "加载工具类": "Load tool class", "测试脚本": "test script", "/ Prompt 初始化请求事件(支持自定义 Model)": "/ Prompt initialization request event (supports custom Model)", "可选:用户选择的 Model ID": "Optional: User-selected Model ID", "/ Prompt 初始化响应事件": "/Prompt initializes response events", "/ Prompt 优化请求事件": "/Prompt optimization request event", "/ Prompt 优化响应事件": "/Prompt optimizes response events", "🆕 创建后立即打靶(默认 true)": "🆕 Target shooting immediately after creation (default true)", "🆕 打靶后 AI 评分(默认 false)": "🆕 AI scoring after target practice (default false)", "/ 优化后的参数": "/Optimized parameters", "这个事件定义属于 PromptRange 的契约": "This event definition belongs to the PromptRange contract", "新增/编辑对话框": "New/Edit dialog box", "/ 任务状态枚举": "/Task status enumeration", "/ 出错": "/ error", "/ 等待开始": "/wait to start", "/ 进行中": "/ in progress", "/ 已完成": "/ Completed", "/ SenMapic 爬虫任务实体类": "/ SenMapic crawler task entity class", "/ 起始URL": "/Start URL", "/ 最大线程数": "/Maximum number of threads", "/ 单站点最大爬取时间(分钟)": "/ Maximum crawling time of a single site (minutes)", "/ 最大爬取深度": "/Maximum crawl depth", "/ 最大页面数量": "/Maximum number of pages", "/ 任务状态": "/task status", "/ 开始时间": "/ start time", "/ 完成时间": "/completion time", "/ 已爬取的页面数量": "/Number of pages crawled", "/ 错误信息": "/ error message", "/ 2、运行:PM> add-migration [更新名称] -c SenMapicSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ": "/ 2. Run: PM> add-migration [update name] -c SenMapicSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ", "/ 2、运行:PM> add-migration [更新名称] -c SenMapicSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ": "/ 2. Run: PM> add-migration [update name] -c SenMapicSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ", "/ 2、运行:PM> add-migration [更新名称] -c SenMapicSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ": "/ 2. Run: PM> add-migration [update name] -c SenMapicSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ", "/ 2、运行:PM> add-migration [更新名称] -c SenMapicSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ": "/ 2. Run: PM> add-migration [update name] -c SenMapicSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ", "/ 2、运行:PM> add-migration [更新名称] -c SenMapicSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ": "/ 2. Run: PM> add-migration [update name] -c SenMapicSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ", "/ 2、运行:PM> add-migration [更新名称] -c SenMapicSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ": "/ 2. Run: PM> add-migration [update name] -c SenMapicSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ", "/ URL包含HTML内容信息,及爪录统计结果": "/URL contains HTML content information, and claw statistics results", "提取标题": "Extract title", "提取 meta description": "Extract meta description", "提取 meta keywords": "Extract meta keywords", "处理图片": "Process pictures", "处理链接": "Handle links", "处理标题标签": "Handling title tags", "处理段落和换行": "Handle paragraphs and line breaks", "处理强调": "deal with stress", "处理列表": "Process list", "移除 script、style 标签及内容": "Remove script, style tags and content", "移除注释": "Remove comments", "移除 header、footer、nav、广告相关区域": "Remove header, footer, nav, and advertising related areas", "移除常见无意义内容区域": "Remove common meaningless content areas", "移除其他HTML标签,但保留一些有语义的换行": "Remove other HTML tags but keep some semantic line breaks", "处理特殊字符": "Handle special characters", "清理空白字符": "Clean whitespace characters", "处理引用块": "Handle quoted blocks", "处理代码块": "Process code blocks", "处理表格": "Processing forms", "处理水平线": "Handling Horizontal Lines", "处理删除线": "Handle strikethrough", "处理下划线": "Handling underscores", "处理有序列表和无序列表": "Handle ordered and unordered lists", "/ 存储不带HTML标记的纯文本内容,转换为Markdown格式": "/ Store plain text content without HTML tags and convert to Markdown format", "原来是:@\"(?<=[.\\w\\W\\s\\S]*)([.\\w\\W\\s\\S][^)\"": "It turns out to be: @\"(?<=[.\\w\\W\\s\\S]*)([.\\w\\W\\s\\S][^)\"", "第二版本:@\"(?<=[.\\w\\W\\s\\S]*)([.\\w\\W\\s\\S][^<]*)(?=)\"": "Second version: @\"(?<=[.\\w\\W\\s\\S]*)([.\\w\\W\\s\\S][^<]*)(?=)\"", "清空TitleHtml": "Clear TitleHtml", "如果已经是绝对路径,直接返回": "If it is already an absolute path, return directly", "/ 分层搜索Url集合": "/ Hierarchical search Url collection", "/ Url状态": "/URL status", "/ 搜索下一层": "/Search for next level", "/ 可使用Url": "/ can use Url", "/ 过滤关键字": "/ filter keywords", "/ 过滤关键字类型": "/ Filter keyword types", "/ 当前正在被爬行的Url集合": "/ The collection of URLs currently being crawled", "/ 判断是否包含Key": "/ Determine whether Key is included", "/ 移除": "/remove", "/ 模拟系统Semaphore(为兼容Silverlight)": "/ Simulation system Semaphore (for Silverlight compatibility)", "TODO:目前为Beta 2": "TODO: Currently in Beta 2", "/ SenMapicEngine版本": "/SenMapicEngine version", "TODO:过滤js中此类情形:\"
\";Joomla中,url有;出现": "TODO: Filter such situations in js: \"\"; In Joomla, the url has; appears", "最大线程数": "Maximum number of threads", "最大超时页面数(超过此数量,终止收集)": "Maximum number of timeout pages (if this number is exceeded, collection will be terminated)", "页面请求超时时间(毫秒)": "Page request timeout (milliseconds)", "默认最长收集时间(每一个站点)": "Default maximum collection time (per site)", "带收录的列队Url": "Queue Url with included", "当前搜索的Url": "Current search URL", "停止当前站点搜索": "Stop current site search", "当前原始完整的域名(http://www.senparc.com)": "Current original complete domain name (http://www.senparc.com)", "当前搜索的域名": "Domain name currently searched", "协议(http | https)": "Protocol (http|https)", "当前收录深度": "Current collection depth", "当前域名下收录页面数量": "The number of pages included under the current domain name", "当前可用Url集合 TODO:lock": "Currently available URL collection TODO:lock", "当前域域名收集的所有URL": "All URLs collected by the current domain name", "搜索深度(指通过页面进入网址的搜索层次,非网页在网站中的实际路径深度)": "Search depth (referring to the search level of entering the URL through the page, not the actual path depth of the web page in the website)", "最大允许收录页面数": "Maximum number of pages allowed to be included", "路径": "path", "请求页面数量": "Number of pages requested", "请求页面时间": "Request page time", "当前使用线程数": "Number of threads currently used", "/ 正在使用中的线程数": "/Number of threads in use", "锁": "Lock", "剩余未处理域名": "Remaining unprocessed domain names", "剩余未收录url数量": "Number of remaining uncollected urls", "所有域名收集的Url": "Urls collected from all domain names", "当前搜索站点开始时间": "Current search site start time", "单个站点对大允许时间": "Maximum allowed time for a single site", "到达最大页面数": "Maximum number of pages reached", "过滤Url中的关键字": "Filter keywords in Url", "/ 开始收集Url及页面信息": "/ Start collecting Url and page information", "搜索所有网址": "Search all URLs", "加上末尾/(为了获得更完整的URL,以便判断下属链接URL是否等于域名根目录)": "Add the trailing / (in order to obtain a more complete URL, so as to determine whether the subordinate link URL is equal to the domain name root directory)", "_domainAll += (_domainAll == null ? \"\" : \"+\") + _currentDomain;//所有域名": "_domainAll += (_domainAll == null ? \"\" : \"+\") + _currentDomain;//All domain names", "如:http://www.senparc.com": "Such as: http://www.senparc.com", "深度还原": "Deep restoration", "页面计数还原": "Page count restoration", "_currentFilterOmitWords = new Dictionary();//过滤关键字还原": "_currentFilterOmitWords = new Dictionary();//Filter keyword restoration", "当前域名收集URL还原": "Current domain name collection URL restoration", "初始化UrlCollection集合": "Initialize the UrlCollection collection", "_semaphorePool = new SenMapicSemaphore(_maxThread);//使用Semaphore控制当前线程数量": "_semaphorePool = new SenMapicSemaphore(_maxThread);//Use Semaphore to control the current number of threads", "开始收集(核心方法)": "Start collection (core method)", "TotalUrlDataCollection.Add(_currentUrl, _currentUrls);//添加UrlData结果到汇总集合": "TotalUrlDataCollection.Add(_currentUrl, _currentUrls);//Add UrlData results to the summary collection", "确保收录数量正确(由于deep的原因,可能导致实际收录url数量比设定值要多)": "Make sure the number of included URLs is correct (due to deep reasons, the actual number of included URLs may be more than the set value)", "写入日志": "write log", "剩余未处理域名数减少": "The number of remaining unprocessed domain names decreases", "LogUtility.SitemapLogger.Error(\"生成报表及sitemap文件出错:{0}\".With(e.Message), e);": "LogUtility.SitemapLogger.Error(\"Error in generating report and sitemap file: {0}\".With(e.Message), e);", "/ 爬行站点(SenMapic核心启动方法)": "/ Crawl site (SenMapic core startup method)", "/ 域名或首页地址": "/ Domain name or homepage address", "首页加入待选": "Home page added to be selected", "开始从第0层(首页)抓取": "Start crawling from layer 0 (home page)", "每层结束前,会等待所有线程结束,threadInUsing > 0 说明此时还有线程在工作": "Before the end of each layer, it will wait for all threads to end. threadInUsing > 0 means there are still threads working at this time.", "收集已满,或被呼叫停止。": "The collection is full, or was called to stop.", "_urlPath[_currentDeep].Url = url;//记录当前层次Url": "_urlPath[_currentDeep].Url = url;//Record the current level Url", "当前层所有有效,且为开始收集的Url": "All valid URLs in the current layer are the Url to start collecting.", "所有页面已完成。": "All pages are completed.", "分线程开始爪录,添加到线程池": "Start recording in separate threads and add to thread pool", "尝试3次加入列队": "Tried 3 times to join the queue", "尝试加入列队": "Try to join the queue", "尝试放入列队": "try to queue", "这里如果一次全部将线程加入到列队,使用_semaphorePool.WaitOne()等待时,": "Here, if all threads are added to the queue at once, when waiting using _semaphorePool.WaitOne(),", "当某一层链接数量非常多(如几百个)时,会使整个应用程序无响应。": "When the number of links in a certain layer is very large (such as hundreds), the entire application will become unresponsive.", "每一层结束的时候确保没有线程被占用": "At the end of each layer, ensure that no threads are occupied", "每个线程页面采集大于大于0.5分钟,强制退出": "Page collection for each thread takes more than 0.5 minutes and forced exit", "还有线程没有执行完": "There are still threads that have not finished executing", "CrawlUrlEventHandler(url);//不使用多线程,单线程": "CrawlUrlEventHandler(url);//Do not use multi-threading, single thread", "所有链接库中搜索完毕之后,开始下一层。": "After searching in all link libraries, start the next level.", "休息一下:)": "take a break:)", "/ 提供多线程同时爪录": "/ Provide multi-threaded simultaneous recording", "/ 完整URL,格式如:http://www.senparc.com/xx.html": "/ Full URL, in the format: http://www.senparc.com/xx.html", "LogUtility.WebLogger.DebugFormat(\"url:{0} 进入列队等待开始。\", url);": "LogUtility.WebLogger.DebugFormat(\"url:{0} has entered the queue and is waiting to start.\", url);", "_semaphorePool.WaitOne();//等待加入semaphore列队": "_semaphorePool.WaitOne();//Waiting to join the semaphore queue", "LogUtility.WebLogger.DebugFormat(\"url:{0} 线程正式开始。当前运行线程数:{1}\", url, threadInUsing);": "LogUtility.WebLogger.DebugFormat(\"url:{0} thread officially started. Current number of running threads: {1}\", url, threadInUsing);", "加入全局同步信息": "Add global synchronization information", "爬行Url,获取Url,HTML等重要信息": "Crawl Url and obtain Url, HTML and other important information", "爬行完成": "Crawl completed", "不符合爬行条件": "Not eligible for crawling", "父页面Url": "Parent page Url", "Key大写": "Key capitalized", "记录到已完成表中。": "Record in completed table.", "Url已经经过跳转,需要更新avaliableUrl": "The URL has been redirected and the availableUrl needs to be updated.", "未到最后一层,收集改页面面下一层的所有可用网页": "Before reaching the last level, collect all available web pages in the next level of the changed page.", "获取 href 属性值": "Get href attribute value", "获取超链接文本": "Get hyperlink text", "存入有效链接库": "Save to valid link library", "当前Url": "CurrentUrl", "属于下一层": "Belongs to the next level", "超链接文字": "hyperlink text", "标记未开始": "Marking not started", "continue;//无效Url,或超出范围": "continue;//Invalid Url, or out of range", "LogUtility.WebLogger.DebugFormat(\"url:{0} 线程退出。\", url);": "LogUtility.WebLogger.DebugFormat(\"url:{0} thread exited.\", url);", "Thread.Sleep(80);//休息一下:)": "Thread.Sleep(80);//Take a rest :)", "/ 重置": "/reset", "定义域名级别": "Define domain name level", "/ 获取带协议完整域名,如Http://www.senparc.com": "/ Get the complete domain name with protocol, such as http://www.senparc.com", "/ 获取域名,如www.senparc.com": "/ Get a domain name, such as www.senparc.com", "/ 获取协议,如http": "/ Get the protocol, such as http", "/ 去掉Url末尾的/。如果为../或/,则不过滤": "/ Remove the / at the end of Url. If ../ or /, no filtering", "去掉末尾的“/”": "Remove the \"/\" at the end", "/ 去掉Url开头的/,如果字符串等于/,则返回/": "/ Remove the / at the beginning of Url. If the string is equal to /, return /", "/ 获取包含[协议]://[域名]/[地址]的完整Url": "/ Get the complete Url containing [protocol]://[domain name]/[address]", "parentUrl = this.RemoveUrlEndingSlash(parentUrl);//parent末尾有没有/要分情况": "parentUrl = this.RemoveUrlEndingSlash(parentUrl);//It depends on the situation whether there is / at the end of parent", "过滤特殊协议": "Filter special protocols", "同skype:,格式:callto://szw2003/": "Same as skype:, format: callto://szw2003/", "TODO:更多协议过滤": "TODO: More protocol filtering", "以http(s)开头": "Start with http(s)", "不符合要求的url,如http://non": "URLs that do not meet the requirements, such as http://non", "新域名为空,或连接到其他域名": "The new domain name is empty or connected to another domain name", "TODO:继续完善允许二级域名的情况(需要考虑.com.cn和.com等域名的情况)": "TODO: Continue to improve the situation of allowing second-level domain names (need to consider the situation of domain names such as .com.cn and .com)", "string topDomain = domain.Substring(domain.IndexOf(\".\"), domain.Length - domain.IndexOf(\".\"));//如:senparc.com过滤后为http://.com": "string topDomain = domain.Substring(domain.IndexOf(\".\"), domain.Length - domain.IndexOf(\".\"));//For example: senparc.com is filtered to http://.com", "topDomain = domain;//TODO:仍无法过滤类似abc.(com.cn)的情况": "topDomain = domain;//TODO: Still unable to filter situations like abc.(com.cn)", "if (domain == null || (domain != _currentDomain && !_currentDomain.ToUpper().EndsWith(topDomain.ToUpper())))//二级域名予以承认": "if (domain == null || (domain != _currentDomain && !_currentDomain.ToUpper().EndsWith(topDomain.ToUpper())))//Second-level domain name is recognized", "return null;//domain==null的情况会发生在href=“http://”或“http://#/a.htm”的情况下": "return null;//domain==null will occur when href=\"http://\" or \"http://#/a.htm\"", "return null;//跳转到不同网站": "return null;//Jump to different websites", "当前父层Url(不能去掉末尾/)": "Current parent layer Url (the trailing / cannot be removed)", "复层链接完整Url": "Multi-layer link complete URL", "本地路径": "local path", "绝对路径(e.g. http://www.senparc.com/)": "Absolute path (e.g. http://www.senparc.com/)", "参数形式,直接加到当前路径结尾": "Parameter form, added directly to the end of the current path", "其他开头,如相对路径": "Other beginnings, such as relative paths", "上级目录以“/”结尾": "The parent directory ends with \"/\"", "TODO:一般搜索引擎会认为这是两个不同的Url,这里不处理也可以": "TODO: Generally, search engines will think that these are two different URLs, so it is okay not to process them here.", "出现http://www.senparc.com的情况,补足为http://www.senparc.com/": "If http://www.senparc.com appears, the supplement is http://www.senparc.com/", "(TODO:可以考虑中间出现./的情况)": "(TODO: You can consider the situation where ./ appears in the middle)", "或newUrl.Contains(\"../\")": "or newUrl.Contains(\"../\")", "如果当前查询到的URL是当前域名根目录,则过滤": "If the currently queried URL is the root directory of the current domain name, filter", "出现../的Index": "The Index of ../ appears", "出现了http://www.senparc.com/../这样的情况,过滤掉就行": "If a situation like http://www.senparc.com/../ occurs, just filter it out.", "../之前的最近一个/": "../most recent one before/", "上一级": "Previous level", "剩下部分": "remaining part", "路径描述有误,默认为当前域名根目录,但是由于已经收录,所以过滤。(如出现了http://www.senparc.com/../../的情况)": "The path description is incorrect. It defaults to the root directory of the current domain name, but since it has been included, it is filtered. (For example, http://www.senparc.com/../../ appears)", "/ 抓取单个Url(无统计数据)": "/ Fetch a single URL (no statistics)", "/ 爬行Url,并带回UrlData信息": "/ Crawl Url and bring back UrlData information", "/ 返回UrlData信息,如果不符合爬行条件(如CotentType类型不符合等),则返回null": "/ Returns UrlData information. If the crawling conditions are not met (such as CotentType type does not meet, etc.), null is returned", "BMK:此方法收集时有的网站会发生“未将对象引用设置到对象的实例”错误,如:http://www.w2tx.cn/batch.common.php?action=viewnews&op=up&itemid=77&catid=70": "BMK: When collecting with this method, some websites will encounter the error \"object reference is not set to an instance of the object\", such as: http://www.w2tx.cn/batch.common.php?action=viewnews&op=up&itemid=77&catid=70", "已改为在外部判断": "Has been changed to be judged externally", "return null;//超出范围,不予收录": "return null;//Out of range, not included", "非标准Url": "Non-standard URL", "包含过滤关键字": "Contains filter keywords", "已经收录": "Already included", "添加到待选Url": "Add to selected Url", "已经由别的线程开始,或已经完成": "Has been started by another thread, or has been completed", "开始访问": "Start visiting", "爬行获取webReponse": "Crawl to get webReponse", "标记完成": "Mark complete", "比对URL,如果发生302或301(自动添加末尾/)的情况": "Compare the URL, if 302 or 301 occurs (automatically add the trailing /)", "发生改变": "change", "更新AvaliableUrlTemp中的信息。": "Update the information in AvailableUrlTemp.", "url调整为新的url": "url adjusted to new url", "已经收录过,或包含过滤词,停止收录": "Already included, or contains filter words, stop collecting", "如果跳转到其他网站网页,则终止": "If it jumps to other website pages, it will be terminated.", "判断Gzip压缩": "Determine Gzip compression", "判断字符集": "Determine character set", "从Content-Type中获取charterSet。出于效率考虑不使用LINQ": "Get charterSet from Content-Type. Not using LINQ for efficiency reasons", "获取html TODO:如果是301跳转,且html无内容,可能导致htmlTotal中为空": "Get html TODO: If it is a 301 jump and the html has no content, the htmlTotal may be empty.", "取Html前一部分(只用于取网页Title),否则当网页非常大的时候(出现过790k),服务器运算时间过长": "Get the first part of Html (only used to get the title of the web page), otherwise when the web page is very large (over 790k), the server operation time will be too long", "LogUtility.SitemapLogger.Debug(\"url:{0},接收时间:{1},字符数:{2}。平均1000字符耗时:{3}\".With(url,": "LogUtility.SitemapLogger.Debug(\"url:{0}, receiving time: {1}, number of characters: {2}. Average time consuming for 1000 characters: {3}\".With(url,", "return urlData;//正常返回、收录": "return urlData;//Normal return and collection", "if (e.Status == WebExceptionStatus.UnknownError)//不使用Timeout,为了兼容SL": "if (e.Status == WebExceptionStatus.UnknownError)//Do not use Timeout for SL compatibility", "如果网页超时严重,则强制退出": "If the web page times out seriously, force exit", "LogUtility.WebLogger.Error(\"SitemapDebug异常({0}):{1}\".With(url, e.Message), e);": "LogUtility.WebLogger.Error(\"SitemapDebug exception ({0}): {1}\".With(url, e.Message), e);", "记录异常": "Log exception", "关闭链接": "Close link", "添加时差": "Add time difference", "返回收集完成的UrlData": "Return the collected UrlData", "/ 从Content-Type中获取CharterSet,如果无法获取,返回null。出于效率考虑不使用LINQ。": "/ Get the CharterSet from Content-Type, if it cannot be obtained, return null. Not using LINQ for efficiency reasons.", "/ 获取Encoding": "/ Get Encoding", "/ charterSet字符串,如utf-8": "/ charterSet string, such as utf-8", "/ 当为True时,如果获取Encoding失败,返回Encoding.Default,否则返回null": "/ When True, if obtaining Encoding fails, return Encoding.Default, otherwise return null", "SL中没有Default,这里也可以使用utf-8(国内网站建议使用gb312)": "There is no Default in SL, and utf-8 can also be used here (domestic websites recommend using gb312)", "/ 从响应流中以正确的编码返回内容": "/ Return content from the response stream in the correct encoding", "判断编码": "judgment coding", "匹配Html中的charset": "Match charset in Html", "这里不使用BodyName是为了兼容SL。SL中没有BodyName": "BodyName is not used here for SL compatibility. There is no BodyName in SL", "编码相同": "The encoding is the same", "编码不同": "Encoding is different", "不加0,,html.Length参数,在SL下会发生错误:CS0122 Encoding.GetString(byte[]) is inaccessible due to its protection level": "Without adding 0,,html.Length parameter, an error will occur under SL: CS0122 Encoding.GetString(byte[]) is inaccessible due to its protection level", "转换编码": "Convert encoding", "TODO:如果301跳转,并且只靠Header信息,没有html说明,可能导致无法获取html内容": "TODO: If 301 jumps and only relies on Header information without html description, it may result in failure to obtain html content.", "////最终确认Encoding": "////Finally confirm Encoding", "////获取前10个buffer,使用系统查询出来的encoding,查看是否有其他编码(权衡效率)": "////Get the first 10 buffers, use the encoding queried by the system to see if there are other encodings (weighing efficiency)", "// encode = finalEncoding;//编码不同则根据后者确定": "// encode = finalEncoding;//If the encoding is different, it will be determined based on the latter", "// htmlSB.Append(encode.GetString(item));//TODO:內容完整性有損失,有待調整": "// htmlSB.Append(encode.GetString(item)); //TODO: Content integrity has been lost and needs to be adjusted", "/ 停止收集(要等下一个Url开始才会响应)": "/ Stop collection (will not respond until the next Url starts)", "/ 以任意关键字结尾": "/ ends with any keyword", "/ 是否符合Url格式标准": "/ Whether it complies with Url format standards", "上一版本正则:^HTTP(S)?://([\\w-]+\\.)+[\\w-]+(:\\d+)?(/[\\w- ./?%&=]*)?": "Regular version of the previous version: ^HTTP(S)?://([\\w-]+\\.)+[\\w-]+(:\\d+)?(/[\\w- ./?%&=]*)?", "^表示必须以Http开始": "^ means it must start with Http", "/ 获取过滤关键字列表": "/ Get filter keyword list", "转成大写": "Convert to uppercase", "所有关键字": "all keywords", "必须要加关键字的操作符": "Operators that require keywords", "表达式为空": "expression is empty", "表达式错误": "Expression error", "第一个字符": "first character", "包含操作符": "contains operator", "关键字": "Keywords", "普通情况": "Normal situation", "/ 是否包含过滤关键字": "/ Whether to include filter keywords", "/ 请求网页,获取webResponse": "/Request web page and get webResponse", "开始请求": "Start request", "结束请求": "end request", "静态SearchCache": "StaticSearchCache", "返回Nested类中的静态成员instance": "Returns the static member instance in the Nested class", "将instance设为一个初始化的SearchCache新实例": "Set instance to an initialized new instance of SearchCache", "/// 只生成一次": "/// Only generated once", "/// 不要自动发送Email的用户名": "/// Do not automatically send email username", "private readonly int maxBuildThreadCount = 2;//Sitemap Build中最大同时工作线程数(收集页面线程,非当前类同时运行站点线程)": "private readonly int maxBuildThreadCount = 2;//The maximum number of simultaneous working threads in Sitemap Build (collection page threads, non-current classes run site threads at the same time)", "private readonly int maxVipBuidThreadCount = 6;//Sitemap Build中最大同时工作线程数(收集页面线程,非当前类同时运行站点线程)": "private readonly int maxVipBuidThreadCount = 6;//The maximum number of simultaneous working threads in Sitemap Build (collection page threads, non-current classes run site threads at the same time)", "private int maxAutoAlertThread = 2;//AutoAlert最大同时收集站点(线程)数": "private int maxAutoAlertThread = 2;//AutoAlert maximum number of simultaneous collection sites (threads)", "/// 正在使用中的线程数": "/// Number of threads in use", "private object syncLock = new object();//锁": "private object syncLock = new object();//lock", "Thread.Sleep(TimeSpan.FromSeconds(10));//延时": "Thread.Sleep(TimeSpan.FromSeconds(10));//Delay", "SenparcTrace.SendCustomLog(\"SiteMap\", \"一次Sitemap循环开始\");": "SenparcTrace.SendCustomLog(\"SiteMap\", \"A Sitemap cycle begins\");", "CleanStatisticsFiles();//清理历史统计文件": "CleanStatisticsFiles();//Clean historical statistics files", "AutoCheckOrderApply();//自动审核定制服务申请": "AutoCheckOrderApply();//Automatically review custom service applications", "SenparcTrace.SendCustomLog(\"SiteMap\", \"一次Sitemap循环结束\");": "SenparcTrace.SendCustomLog(\"SiteMap\", \"A Sitemap cycle ends\");", "Thread.Sleep(TimeSpan.FromMinutes(10));//执行间隔10分钟": "Thread.Sleep(TimeSpan.FromMinutes(10));//Execution interval is 10 minutes", ".Where(z => z.UserInfo.UserId == 2)//暂时只处理zsu用户的信息": ".Where(z => z.UserInfo.UserId == 2)//Only process zsu user information for now", "? true//全部": "? true//all", ": (order.LastCreateTime.AddMinutes(order.BuildFrequencyMinutes) < DateTime.Now && order.InUse)//自动循环,只筛选符合条件的记录,避免线程卡死": ": (order.LastCreateTime.AddMinutes(order.BuildFrequencyMinutes) < DateTime.Now && order.InUse)//Automatically loop, only filter records that meet the conditions to avoid thread stuck", ").ToList();//所有符合条件的列表": ").ToList();//All lists that meet the conditions", "var queueOrders = new List();//实际轮询操作的列表": "var queueOrders = new List();//List of actual polling operations", "//在网站访问量较大的时间段内,每次轮询只处理少数个": "//During the period of time when the website has a large number of visits, only a few will be processed in each poll.", "int takeEndCount = Math.Max(allQueueOrders.Count / 5, 2);//取ID最大的末尾数量(符合条件订单的1/5或2中的较大值)": "int takeEndCount = Math.Max(allQueueOrders.Count / 5, 2);//Take the largest end number of IDs (the larger of 1/5 or 2 of the eligible orders)", "queueOrders = allQueueOrders.OrderByDescending(z => z.Id).Take(takeEndCount).ToList();//取ID最大的最后N个": "queueOrders = allQueueOrders.OrderByDescending(z => z.Id).Take(takeEndCount).ToList();//Take the last N with the largest ID", "int takeBeginingCount = 1;//取ID最小的最前面N个": "int takeBeginingCount = 1; //Take the first N numbers with the smallest ID", "int finalTakeBeginingCount = Math.Min(listedAllOrdersCount - takeEndCount, takeBeginingCount);//最终获取ID最小的个数": "int finalTakeBeginingCount = Math.Min(listedAllOrdersCount - takeEndCount, takeBeginingCount);//Finally get the smallest number of IDs", "queueOrders.AddRange(allQueueOrders.OrderBy(z => z.Id).Take(finalTakeBeginingCount).ToList());//取ID最多ID最小的前N个": "queueOrders.AddRange(allQueueOrders.OrderBy(z => z.Id).Take(finalTakeBeginingCount).ToList());//Get the top N with the largest ID and the smallest ID", "queueOrders = allQueueOrders.ToList();//不再限制时间段内,全部处理": "queueOrders = allQueueOrders.ToList();//No longer limited time period, all processes", "SenparcTrace.SendCustomLog(\"SiteMap\", $\"Sitemap预定总数:{siteMapOrders.Count},符合条件列队数:{listedAllOrdersCount},实际处理数:{queueOrders.Count}\");": "SenparcTrace.SendCustomLog(\"SiteMap\", $\"Total number of Sitemap reservations: {siteMapOrders.Count}, number of qualifying queues: {listedAllOrdersCount}, actual number of processes: {queueOrders.Count}\");", "remainQueueOrderCount++;//还剩下没处理的Order": "remainQueueOrderCount++;//There are still unprocessed Orders left", "//semaphorePoolPreviousCount = this._semaphorePool.Release(maxAutoAlertThread);//释放所有线程": "//semaphorePoolPreviousCount = this._semaphorePool.Release(maxAutoAlertThread);//Release all threads", "SenparcTrace.SendCustomLog(\"SiteMap\", \"等待线程结束已有5分钟。\",": "SenparcTrace.SendCustomLog(\"SiteMap\", \"It has been 5 minutes waiting for the thread to end.\",", "new Exception($\"总列队数:{queueOrders.Count},使用中的线程数量:{autoAlertThreadInUsing}\"));": "new Exception($\"Total number of queues: {queueOrders.Count}, number of threads in use: {autoAlertThreadInUsing}\"));", "//收录时间较长,则记录": "//The collection time is longer, then record", "SenparcTrace.SendCustomLog(\"SiteMap\", $\"所有站点线程结束,耗时{(DateTime.Now - dtStartWait).TotalMinutes.ToString(\"#.#\")}分钟。\",": "SenparcTrace.SendCustomLog(\"SiteMap\", $\"All site threads ended, taking {(DateTime.Now - dtStartWait).TotalMinutes.ToString(\"#.#\")} minutes.\",", "SenparcTrace.SendCustomLog(\"SiteMap\", $\"AutoAlert线程超出最大值,当前线程数:{autoAlertThreadInUsing}。\");": "SenparcTrace.SendCustomLog(\"SiteMap\", $\"AutoAlert thread exceeds the maximum value, current number of threads: {autoAlertThreadInUsing}.\");", "SenparcTrace.SendCustomLog(\"SiteMap\", $\"AutoAlertSitemap严重异常:{e.Message}。下一个AutoAlert循环将继续进行。\", e);": "SenparcTrace.SendCustomLog(\"SiteMap\", $\"AutoAlertSitemap serious exception: {e.Message}. The next AutoAlert loop will continue.\", e);", "_semaphorePool.WaitOne();//列队等待": "_semaphorePool.WaitOne();//Waiting in queue", "LogUtility.WebLogger.Debug(\"自动收集Sitemap:{0} (ID:{1})开始\".With(order.Url, order.Id.ToString()));": "LogUtility.WebLogger.Debug(\"Automatic collection of Sitemap:{0} (ID:{1}) starts\".With(order.Url, order.Id.ToString()));", "//收集Url": "//Collect Url", "//当前网站允许最大线程数": "//The maximum number of threads allowed by the current website", "#region 生成新的SiteMapCollection,加入数据库": "#region Generate a new SiteMapCollection and add it to the database", "order.LastCreateTime = DateTime.Now;//记录开始的时间。": "order.LastCreateTime = DateTime.Now;//The time when the record starts.", "order.Downloaded = null;//清空下载状态信息": "order.Downloaded = null;//Clear download status information", "//只有非VIP且收录上限少于2000的用户,才会增加。": "//Only non-VIP users with an upper limit of less than 2,000 will be added.", "//如果所有Url份额全部完成,则增加一定数量的页面": "//If all Url shares are completed, add a certain number of pages", "order.MaxPageCount += 2;//TODO:添加响应时间参数": "order.MaxPageCount += 2;//TODO: Add response time parameter", "awardRecord += \"收录页面达到最大值,且错误页面小于5%,奖励2个页面数量。
\";": "awardRecord += \"The included pages have reached the maximum value, and the error pages are less than 5%, and the number of pages awarded is 2.
\";", "//奖励或惩罚平均页面响应时间": "//Reward or punish average page response time", "//扩充vip页面数量,保证有40%富余": "//Expand the number of VIP pages to ensure 40% surplus", "LogUtility.SitemapLogger.InfoFormat(\"VIP Sitemap:{0},最高页面设定为:{1} -> {2}\", order.Url, oldOrderMaxPageCount, order.MaxPageCount);": "LogUtility.SitemapLogger.InfoFormat(\"VIP Sitemap: {0}, the highest page setting is: {1} -> {2}\", order.Url, oldOrderMaxPageCount, order.MaxPageCount);", "//LogUtility.WebLogger.Debug(\"自动收集Sitemap:{0}存入数据库完成\".With(order.Url));": "//LogUtility.WebLogger.Debug(\"Automatic collection of Sitemap: {0} is completed and stored in the database\".With(order.Url));", "#region 保存文件": "#region Save file", "//保存文件": "//save file", ".Match(File.OpenText(reportFileName).ReadToEnd()).Value.Trim();//通过正则查找内容部分": ".Match(File.OpenText(reportFileName).ReadToEnd()).Value.Trim();//Find the content part through regular expressions", "LogUtility.SitemapLogger.InfoFormat(\"Sitemap Callback成功:#{0},Url:{1},Callback:{2}\", order.Id, order.Url, order.Callback);": "LogUtility.SitemapLogger.InfoFormat(\"Sitemap Callback successful: #{0}, Url: {1}, Callback: {2}\", order.Id, order.Url, order.Callback);", "LogUtility.SitemapLogger.Error(\"Sitemap Callback错误:\" + e.Message, e);": "LogUtility.SitemapLogger.Error(\"Sitemap Callback error: \" + e.Message, e);", "#region 发送Email": "#region Send Email", "//不在过滤范围内的用户,且不是只发送一次,则自动发送Email": "//For users who are not within the filtering range, and if the email is not sent only once, the email will be automatically sent.", "awardRecord = \"无\";": "awardRecord = \"None\";", "punishRecord = \"无\";": "punishRecord = \"None\";", "//只发送非系统用户的": "//Only send non-system users", "LogUtility.EmailLogger.InfoFormat(\"VIP用户收集Sitemap完成,未发送Email:{0},{1}\", order.UserName, order.Url);": "LogUtility.EmailLogger.InfoFormat(\"VIP user Sitemap collection completed, but no email sent: {0}, {1}\", order.UserName, order.Url);", "CheckLoginAndAlert(order);//判断是否超时未登录": "CheckLoginAndAlert(order);//Determine whether the timeout has not logged in", "//TODO:跟踪莫名其妙自动收录停止": "//TODO: Tracking is inexplicably stopped automatically.", "LogUtility.SitemapLogger.Debug(\"跟踪莫名其妙自动收录停止,到达catch(AutoAlertSitemapUtility.cs第350行)\", e);": "LogUtility.SitemapLogger.Debug(\"Tracking inexplicably stopped automatically, reaching catch (AutoAlertSitemapUtility.cs line 350)\", e);", "LogUtility.SitemapLogger.Error(\"AutoAlertSitemap自动发送Email出错:{0}.URL:{1}\".With(e.Message, siteMapCollection != null ? siteMapCollection.Url : order.Url), e);": "LogUtility.SitemapLogger.Error(\"AutoAlertSitemap automatically sent Email error: {0}.URL:{1}\".With(e.Message, siteMapCollection != null ? siteMapCollection.Url : order.Url), e);", "LogUtility.SitemapLogger.Error(\"AutoAlertSitemapUtility.cs第356行错误!!catch中再次发生错误\", ex);": "LogUtility.SitemapLogger.Error(\"Error on line 356 of AutoAlertSitemapUtility.cs!! An error occurred again in catch\", ex);", "//LogUtility.WebLogger.Debug(\"自动收集Sitemap:{0}发送Email完成\".With(order.Url));": "//LogUtility.WebLogger.Debug(\"Automatic collection of Sitemap: {0} completion of sending email\".With(order.Url));", "/ 检查是否超时未登录,如果是,则发送Email提示": "/ Check whether the user has not logged in after timeout, if so, send an email prompt", "return;//指定用户不提示": "return;//Specify the user not to prompt", "//已超时": "//Timed out", "//超时15天,自动删除": "//Timeout 15 days, automatically deleted", "//删除文件": "//delete file", "//ctx.DeleteObject(order);//从数据库删除order,下属orderCollection会被连带删除": "//ctx.DeleteObject(order);//Delete order from the database, and the subordinate orderCollection will be deleted simultaneously", "LogUtility.SitemapLogger.InfoFormat(\"Sitemap超过登陆天数被自动删除:{0},{1}\", order.UserName, order.Url);": "LogUtility.SitemapLogger.InfoFormat(\"Sitemap will be automatically deleted after the number of login days: {0}, {1}\", order.UserName, order.Url);", "//超时5天,自动关闭": "//Timeout 5 days, automatically shut down", "/// 奖励或惩罚平均页面响应时间": "/// Reward or punish average page response time", "/// 当前最新的收录结果": "/// The latest collection results", "//页面奖励或惩罚": "//Page rewards or penalties", "int minMillsecond = 350;//平均响应时间最小值,小于此数值奖励": "int minMillsecond = 350; //Minimum average response time, rewards less than this value", "int maxMillisecond = 1200;//平均响应时间最大值,大于此数值惩罚": "int maxMillisecond = 1200; //Maximum average response time, penalty for greater than this value", "int lookBackRecord = 2;//向前看2条记录": "int lookBackRecord = 2;//Look forward 2 records", "//查看前两次都大于最大值": "//Check that the first two times are greater than the maximum value", "List averageRequestMillisecondList = new List();//响应时间集合": "List averageRequestMillisecondList = new List();//Response time collection", "averageRequestMillisecondList[i] = 99999;//如果无响应,视为响应时间过长": "averageRequestMillisecondList[i] = 99999; //If there is no response, it is considered that the response time is too long", "#region 此方法无效。由于Int是值类型,这里赋值不会影响到averageRequestMillisecondList中的值": "#region This method has no effect. Since Int is a value type, the assignment here will not affect the value in averageRequestMillisecondList.", "// z = 999999;//没有响应视为响应时间无限长": "// z = 999999; //No response is considered an infinite response time", "//检查是大雨最大只或小于最小值": "//Check whether the maximum heavy rain is only or less than the minimum value", "int cutPageCount = 10;//扣除页面数": "int cutPageCount = 10;//Deduct the number of pages", "int maxMS = 1200;//最大毫秒": "int maxMS = 1200;//Maximum milliseconds", "//分3个等级判断": "//Judge in 3 levels", "punishRecord += \"连续{0}次响应时间大于{1}(毫秒),页面收录数扣减{2}个。
\".With(averageRequestMillisecondList.Count, maxMS, cutPageCount);": "punishRecord += \"{0} consecutive response times are greater than {1} (milliseconds), {2} pages will be deducted from the number of included pages.
\".With(averageRequestMillisecondList.Count, maxMS, cutPageCount);", "order.MaxPageCount = Math.Max(1, order.MaxPageCount - cutPageCount);//减N个页面": "order.MaxPageCount = Math.Max(1, order.MaxPageCount - cutPageCount);//Decrease N pages", "LogUtility.SitemapLogger.InfoFormat(\"订单 #{0}({1})连续{2}次响应时间大于{3}(毫秒),最大收录数被扣减{4}页面\"": "LogUtility.SitemapLogger.InfoFormat(\"Order #{0}({1}) has received {2} consecutive response times greater than {3} (milliseconds), and the maximum number of included pages has been deducted by {4} pages\"", "awardRecord += \"连续{0}次响应时间小于{1}(毫秒),奖励{2}个页面数量。
\".With(lookBackRecord + 1, minMillsecond, 2);": "awardRecord += \"If the response time is less than {1} (milliseconds) for {0} consecutive times, award {2} pages.
\".With(lookBackRecord + 1, minMillsecond, 2);", "LogUtility.SitemapLogger.InfoFormat(\"订单 #{0}({1})连续{2}次响应时间小于{3}(毫秒),奖励2个最大页面收录数量\"": "LogUtility.SitemapLogger.InfoFormat(\"Order #{0}({1}) has {2} consecutive response times less than {3} (milliseconds), and will be rewarded with 2 maximum page inclusions\"", "/// 清理自动生成的sitemap及报告文件,并统计到info.text中": "/// Clean up the automatically generated sitemap and report files, and add statistics to info.text", "return;//只在1号执行": "return; //Only executed on No. 1", "LogUtility.SitemapLogger.Info(\"清理并统计日志开始\");": "LogUtility.SitemapLogger.Info(\"Start cleaning and counting logs\");", ".Where(z => Path.GetFileName(z) != DateTime.Now.ToString(\"yyyy-MM\"))//当月信息不清理": ".Where(z => Path.GetFileName(z) != DateTime.Now.ToString(\"yyyy-MM\"))//The information of the current month is not cleared", "LogUtility.SitemapLogger.InfoFormat(\"创建{0}/info.txt\", Path.GetFileNameWithoutExtension(dir));": "LogUtility.SitemapLogger.InfoFormat(\"Create {0}/info.txt\", Path.GetFileNameWithoutExtension(dir));", "//计算每天sitemap.xml文件数量": "//Calculate the number of sitemap.xml files per day", "DateTime dt = DateTime.Parse(Path.GetFileNameWithoutExtension(dir) + \"-1\");//获取当前月1号": "DateTime dt = DateTime.Parse(Path.GetFileNameWithoutExtension(dir) + \"-1\");//Get the 1st of the current month", "int daysInMonth = DateTime.DaysInMonth(dt.Year, dt.Month);//本月天数": "int daysInMonth = DateTime.DaysInMonth(dt.Year, dt.Month);//Number of days in this month", "Dictionary> dicFilesInDay = new Dictionary>();//分类": "Dictionary> dicFilesInDay = new Dictionary>();//Category", "dicFilesInDay.Last().Value.Add(file);//如果不存在,加到最后一天(通常发生在月末,跨两个月的收录)": "dicFilesInDay.Last().Value.Add(file);//If it does not exist, add it to the last day (usually occurs at the end of the month, spanning two months)", "//输出统计结果": "//Output statistical results", "LogUtility.SitemapLogger.InfoFormat(\"清理统计文件结束,信息:\\r\\n{0}\", text.ToString());": "LogUtility.SitemapLogger.InfoFormat(\"Cleaning of statistics files ended, information:\\r\\n{0}\", text.ToString());", "LogUtility.SitemapLogger.ErrorFormat(\"清理SItemap历史记录出错,数字不匹配,请检查。文件夹:{0}\", Path.GetFileName(dir));": "LogUtility.SitemapLogger.ErrorFormat(\"Error cleaning SItemap history, numbers do not match, please check. Folder: {0}\", Path.GetFileName(dir));", "//核算正确,删除多余文件": "//Accounting is correct, delete redundant files", "LogUtility.SitemapLogger.InfoFormat(\"删除自动生成的文件 开始。\");": "LogUtility.SitemapLogger.InfoFormat(\"Delete automatically generated files. Start.\");", "LogUtility.SitemapLogger.InfoFormat(\"删除自动生成的文件 结束。\");": "LogUtility.SitemapLogger.InfoFormat(\"Delete automatically generated files. End.\");", "LogUtility.SitemapLogger.ErrorFormat(\"删除{0}目录文件失败。\", Path.GetFileName(dir));": "LogUtility.SitemapLogger.ErrorFormat(\"Failed to delete {0} directory file.\", Path.GetFileName(dir));", "LogUtility.SitemapLogger.InfoFormat(\"清理{0}目录完成。\", Path.GetFileName(dir));": "LogUtility.SitemapLogger.InfoFormat(\"Cleaning {0} directory completed.\", Path.GetFileName(dir));", "LogUtility.SitemapLogger.Info(\"清理并统计日志结束\");": "LogUtility.SitemapLogger.Info(\"End of cleaning and counting logs\");", "/// 自动审核Sitemap定制申请": "/// Automatically review Sitemap customization applications", "DateTime dtLimit = DateTime.Now.AddDays(-3);//搜索3天以内的": "DateTime dtLimit = DateTime.Now.AddDays(-3);//Search within 3 days", "//查看是否能访问": "//Check if it can be accessed", "//发送申请通过通知": "//Send application approval notification", "//修改Order信息": "//Modify Order information", "order.InUse = true;//启用定制": "order.InUse = true;//Enable customization", "order.LastCreateTime = order.LastCreateTime.AddDays(-1);//设为上一天,在下次轮询时自动收集": "order.LastCreateTime = order.LastCreateTime.AddDays(-1);//Set to the previous day and automatically collect it during the next polling", "LogUtility.SitemapLogger.Info(\"Sitemap定制服务自动审核通过:(#{0}) {1}\".With(order.Id, order.Url));": "LogUtility.SitemapLogger.Info(\"Sitemap customization service automatically approved: (#{0}) {1}\".With(order.Id, order.Url));", "LogUtility.SitemapLogger.Error(\"Sitemap定制服务审核失败:(#{0}) {1}\".With(order.Id, order.Url));": "LogUtility.SitemapLogger.Error(\"Sitemap customization service review failed: (#{0}) {1}\".With(order.Id, order.Url));", "//TODO:发邮件通知": "//TODO: Send email notification", "LogUtility.SitemapLogger.Error(\"自动审核定制服务异常:(#{0}) {1}\"": "LogUtility.SitemapLogger.Error(\"Automatic audit customization service exception: (#{0}) {1}\"", "/// Sitemap自动更新Callback": "/// Sitemap automatically updates Callback", "throw new Exception(\"URL地址无法匹配!\");": "throw new Exception(\"URL address cannot match!\");", "urls = (string.IsNullOrEmpty(urls) || !urls.StartsWith(\"http\")) ? \"http://\" + HttpContext.Current.Request.Url.Host : urls.Trim();//当格式错误或为空时,使用当前域名": "urls = (string.IsNullOrEmpty(urls) || !urls.StartsWith(\"http\")) ? \"http://\" + HttpContext.Current.Request.Url.Host : urls.Trim();//When the format is wrong or empty, use the current domain name", "_domainAll += (i == 0 ? \"\" : \"+\") + this.GetDomain(urlList[i]);//所有域名": "_domainAll += (i == 0 ? \"\" : \"+\") + this.GetDomain(urlList[i]);//All domain names", "//指定储存目录": "//Specify storage directory", "//指定文件名": "//Specify file name", "//查看储存文件目录是否存在,并赋予权限": "//Check whether the storage file directory exists and grant permissions", "string physicalDirPath = Server.GetMapPath(saveDirectory);//物理路径": "string physicalDirPath = Server.GetMapPath(saveDirectory);//Physical path", "Directory.CreateDirectory(physicalDirPath);//如果目录不存在则创建": "Directory.CreateDirectory(physicalDirPath); //Create the directory if it does not exist", "//创建sitemap.xml": "//Create sitemap.xml", "//创建sitemap.html和report.html": "//Create sitemap.html and report.html", "// LogUtility.SitemapLogger.Error(\"BuildGoogleSitemapWithReport出错:{0}\".With(e.Message, e));": "// LogUtility.SitemapLogger.Error(\"BuildGoogleSitemapWithReport error: {0}\".With(e.Message, e));", "//组成XML文件": "//Compose XML file", "@\"http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\");//取消了换行": "@\"http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\");//Cancel line breaks", "//页面更新时间": "//Page update time", "//优先级": "//Priority", "//更新频率": "//update frequency", "//保存文件完整路径(包含文件名)": "//Save the full path of the file (including file name)", "//如果文件已存在,则生成一个新的文件名": "//If the file already exists, generate a new file name", "reportFileName = FileSaveUtility.GetAvailableFileName(_sitemapXmlFileName + \"-report.html\");//如果文件已存在,则生成一个新的文件名": "reportFileName = FileSaveUtility.GetAvailableFileName(_sitemapXmlFileName + \"-report.html\");//If the file already exists, generate a new file name", "sitemapHtmlFileName = FileSaveUtility.GetAvailableFileName(_sitemapXmlFileName + \"-sitemap.html\");//如果文件已存在,则生成一个新的文件名": "sitemapHtmlFileName = FileSaveUtility.GetAvailableFileName(_sitemapXmlFileName + \"-sitemap.html\");//If the file already exists, generate a new file name", "//读取Report模板": "//Read Report template", "using (StreamWriter rwReportHtml = System.IO.File.CreateText(reportFileName))//创建文件": "using (StreamWriter rwReportHtml = System.IO.File.CreateText(reportFileName))//Create file", "//输入数据": "//Input data", "this.InsertReportTemplateValue(\"frequent\", changefreq != \"none\" ? changefreq : \"不设置\");": "this.InsertReportTemplateValue(\"frequent\", changefreq != \"none\" ? changefreq : \"Do not set\");", "this.InsertReportTemplateValue(\"updatetime\", updateDate != null ? DateTime.Now.ToString() : \"不设置\");": "this.InsertReportTemplateValue(\"updatetime\", updateDate != null ? DateTime.Now.ToString() : \"Do not set\");", "this.InsertReportTemplateValue(\"priority\", priority != \"none\" ? priority : \"不设置\");": "this.InsertReportTemplateValue(\"priority\", priority != \"none\" ? priority : \"Do not set\");", "this.InsertReportTemplateValue(\"frequent\", priority != \"none\" ? priority : \"不设置\");": "this.InsertReportTemplateValue(\"frequent\", priority != \"none\" ? priority : \"Do not set\");", "//过滤关键字": "//Filter keywords", "filterWords.Append(\"无\");": "filterWords.Append(\"None\");", "failPageReport.Append(\"\");": "failPageReport.Append(\"
错误页面URL错误类型错误页面父页URL
\");", "failPageReport.Append(\"恭喜!无错误页面!\");": "failPageReport.Append(\"Congratulations! No error page!\");", "//生成sitemap.html": "//Generate sitemap.html", "//读取Html模板": "//Read Html template", "using (StreamWriter rwSitemapHtml = System.IO.File.CreateText(sitemapHtmlFileName))//创建文件": "using (StreamWriter rwSitemapHtml = System.IO.File.CreateText(sitemapHtmlFileName))//Create file", "//TODO:整个过程有时候会执行几个小时!!持续观察!(如http://www.shoestog.com)": "//TODO: The entire process sometimes takes several hours! ! Keep watching! (such as http://www.shoestog.com)", "//记录统计数据": "//Record statistics", "SenparcTrace.SendCustomLog(\"SiteMap\", $\"BuildGoogleSitemapWithReport出错:{e.Message}\", e);": "SenparcTrace.SendCustomLog(\"SiteMap\", $\"BuildGoogleSitemapWithReport error: {e.Message}\", e);", "/// 获取域名,如www.senparc.com": "/// Get the domain name, such as www.senparc.com", "#region 替换模板值": "#region replace template value", "throw new Exception(\"报表模板未载入!\");": "throw new Exception(\"Report template not loaded!\");", "//删除目录": "//delete directory", "创建爬虫引擎": "Create a crawler engine", "异步执行爬虫任务": "Execute crawler tasks asynchronously", "var crawTask = await _senMapicTaskService.CreateTaskAsync(\"爬虫任务\"+SystemTime.Now.Ticks,": "var crawlTask ​​= await _senMapicTaskService.CreateTaskAsync(\"Crawler Task\"+SystemTime.Now.Ticks,", "logger.Append(\"爬虫任务创建成功\");": "logger.Append(\"Crawler task created successfully\");", "logger.Append(\"爬虫任务ID:\"+crawTask.Id);": "logger.Append(\"Crawler task ID:\"+crawTask.Id);", "logger.Append(\"爬虫任务名称:\"+crawTask.Name);": "logger.Append(\"Crawler task name:\"+crawTask.Name);", "logger.Append(\"爬虫任务开始时间:\"+crawTask.StartTime);": "logger.Append(\"Crawler task start time:\"+crawTask.StartTime);", "logger.Append(\"爬虫任务结束时间:\"+crawTask.EndTime);": "logger.Append(\"Crawler task end time:\"+crawTask.EndTime);", "logger.Append(\"爬虫任务状态:\"+crawTask.Status);": "logger.Append(\"Crawler task status:\"+crawTask.Status);", "为每个程序集创建文档": "Create documentation for each assembly", "//分组显示 https://www.cnblogs.com/toiv/archive/2018/07/28/9379249.html": "//Group display https://www.cnblogs.com/toiv/archive/2018/07/28/9379249.html", "return false;//不符合要求的都不显示": "return false;//Nothing that does not meet the requirements will be displayed", "规避错误:InvalidOperationException: Can't use schemaId \"$JsApiTicketResult\" for type \"$Senparc.Weixin.Open.Entities.JsApiTicketResult\". The same schemaId was already used for type \"$Senparc.Weixin.MP.Entities.JsApiTicketResult\"": "Avoid error: InvalidOperationException: Can't use schemaId \"$JsApiTicketResult\" for type \"$Senparc.Weixin.Open.Entities.JsApiTicketResult\". The same schemaId was already used for type \"$Senparc.Weixin.MP.Entities.JsApiTicketResult\"", "需要登陆,暂不考虑 —— Jeffrey Su 2021.06.17": "Requires login, will not be considered for now - Jeffrey Su 2021.06.17", "//添加授权": "//Add authorization", "//以下是 appPurachase 的 Id,实际应该是 appId": "//The following is the Id of appPurachase, which should actually be appId", "//以下是正确的 appId": "//The following is the correct appId", "Description = \"请输入带有Bearer开头的Token\",": "Description = \"Please enter a Token starting with Bearer\",", "//认证方式,此方式为全局添加": "//Authentication method, this method is added globally", "//c.OperationFilter();//AuthorizeAttribute过滤": "//c.OperationFilter();//AuthorizeAttribute filtering", "分组显示 https://www.cnblogs.com/toiv/archive/2018/07/28/9379249.html": "Group display https://www.cnblogs.com/toiv/archive/2018/07/28/9379249.html", "获取方法上的特性": "Get attributes on a method", "获取类上的特性": "Get attributes on a class", "不符合要求的都不显示": "Those that do not meet the requirements will not be displayed.", "分组 -- 当前分组策略无效,会导致所有 API 都不显示,暂时停用 —— Jeffrey Su 2021.7.22": "Grouping -- The current grouping strategy is invalid, which will cause all APIs to not be displayed and temporarily disabled - Jeffrey Su 2021.7.22", "//版本": "//Version", "TODO:真实的动态版本号": "TODO: real dynamic version number", "发布为虚拟站点时使用": "Used when publishing as a virtual site", "/ 处理接口文档的用户认证": "/ Handle user authentication of interface documents", "非访问接口时直接返回": "Return directly when not accessing the interface", "登录": "Log in", "登出": "Sign out", "退出": "quit", "/ 使用自定义首页": "/ Use custom homepage", "/ 按约定分配对文档的操作": "/ Assign operations to the document according to agreement", "/ 可以按照以下约定以根据控制器名称空间将操作分配给文档": "/ You can follow the following convention to assign operations to documents based on the controller namespace", "/ 需在Startup.ConfigureServices中加入以下配置": "/ The following configuration needs to be added to Startup.ConfigureServices", "10.0.0 版本核心命名空间": "10.0.0 version core namespace", "/ 添加 swagger 接口版本默认值筛选器 (适配 10.0.0 只读属性版本)": "/ Add swagger interface version default value filter (adapted to 10.0.0 read-only attribute version)", "因为要替换集合中的元素,使用 for 循环通过索引操作": "Because you want to replace elements in the collection, use a for loop to operate by indexing", "查找对应的 API 描述信息": "Find the corresponding API description information", "计算新的属性值": "Calculate new attribute values", "判断是否需要“修改”(即替换对象)": "Determine whether \"modification\" (i.e. replacement of the object) is required", "如果值没变,则无需替换以保持性能": "If the value has not changed, no replacement is needed to maintain performance", "创建新实例,并从旧实例拷贝所有不需要改动的属性": "Create a new instance and copy all properties from the old instance that do not need to be changed", "在 10.0.0 版本中,这些属性通过对象初始化器设置": "In version 10.0.0, these properties are set via object initializers", "核心模型所在": "where the core model is", "--- 注意:由于 Properties 是只读的,我们直接操作集合 ---": "--- Note: Since Properties is read-only, we operate the collection directly ---", "1. 清理:移除 IFormFile 默认属性": "1. Cleanup: Remove IFormFile default properties", "2. 注入新属性": "2. Inject new attributes", "使用索引器:如果存在则覆盖,不存在则添加": "Use indexer: overwrite if exists, add if not", "因为属性是只读的,所以不能用 schema.Properties = ...": "Because properties are read-only, you cannot use schema.Properties = ...", "3. 处理必填项 (Required 也是只读的 ISet)": "3. Process required items (Required is also a read-only ISet)", "通常框架会预先初始化好这个集合,如果没初始化(即为 null),": "Usually the framework will pre-initialize this collection. If it is not initialized (that is, it is null),", "在这种只读设计下通常会提供一个构造函数或初始化方法。": "In this read-only design, a constructor or initialization method is usually provided.", "但绝大多数情况下,schema.Required 在此时已经被实例化了。": "But in most cases, schema.Required has already been instantiated at this time.", "/ 项目发布路径 子应用程序的目录名(虚拟站点)": "/ Project release path Directory name of the sub-application (virtual site)", "/ 直接发布为站点时,请保持默认值": "/ When publishing directly as a site, keep the default value", "/ 项目名称": "/project name", "/ 接口文档显示版本": "/ Interface document display version", "/ 接口文档访问路由前缀": "/Interface document access route prefix", "/ 允许匿名访问": "/Allow anonymous access", "/ 使用主项目的Admin模块认证登录": "/ Use the Admin module of the main project to authenticate and log in", "/ swagger login账号,未指定则不启用": "/ swagger login account, if not specified, it will not be enabled.", "/ 配置": "/config", "/ 使用目录筛选": "/ Use directory filtering", "/ 允许访问的用户分组,留空则不做判断": "/ User group allowed to access, leave blank and no judgment will be made", "/ Api 默认文档地址,返回 null 则不生成 XML 文档": "/Api default document address, return null to not generate XML document", "接口文档": "Interface documentation", "检查 Cookie 认证": "Check Cookie Authentication", "检查是否有有效的 JWT Token": "Check if there is a valid JWT Token", "既没有 Cookie 认证也没有有效的 JWT,重定向到登录页": "Neither cookie authentication nor valid JWT, redirect to login page", "/ Configuration帮助类": "/Configuration helper class", "/ 全局配置": "/global configuration", "/ Swagger模块的配置": "/Configuration of Swagger module", "/ Swagger模块的自定义配置信息": "/ Custom configuration information for Swagger module", "/ 托管环境信息": "/ Hosting environment information", "/ Web托管环境信息": "/Web hosting environment information", "/ 内部访问(项目根路径)": "/Internal access (project root path)", "/ web外部访问(wwwroot)": "/web external access (wwwroot)", "/ Cookie认证名称": "/Cookie authentication name", "/ 获取AppsettingsJson的值": "/ Get the value of AppsettingsJson", "/ 键路径,如:ConnectionStrings:SQLServerConn": "/ Key path, such as: ConnectionStrings:SQLServerConn", "/ 类型": "/ Type", "/ 键路径": "/ Key path", "/ 扫描整个应用程序获取实现ApiVersion的方法,从而获取使用过的版本": "/ Scan the entire application for methods that implement ApiVersion to obtain the used version", "/ 效率较低,ApiVersion是否有提供所有使用过的版本接口???": "/ Low efficiency. Does ApiVersion provide all used version interfaces? ? ?", "类上的ApiVersion": "ApiVersion on class", "//方法上的apiversion": "//apiversion on method", "/ 中文转换": "/ Chinese conversion", "定时执行检测是否转换成中文,最多执行500次 即500*50/1000=25s": "Scheduled execution to detect whether to convert into Chinese, can be executed up to 500 times, that is, 500*50/1000=25s", "中文语言包": "Chinese language pack", "定时执行转换": "Schedule conversion", "设置控制器注释": "Set controller annotation", "尝试将英文转换成中文": "Try to convert English to Chinese", "设置控制器描述": "Set controller description", "执行转换": "perform conversion", "/ 执行cmd.exe命令": "/ Execute cmd.exe command", "/ 命令文本": "/ Command text", "/ 命令输出文本": "/ Command output text", "/ 执行多条cmd.exe命令": "/ Execute multiple cmd.exe commands", "/ 命令文本数组": "/ Command text array", "切换到发布状态": "Switch to publishing status", "切换到开发状态": "Switch to development status", "TODO:需要限制一下执行的命令": "TODO: Need to limit the executed commands", "/// 注册当前模块需要支持的功能模块": "/// Register the function modules that the current module needs to support", "新增 / 编辑 模态框": "Add/edit modal box", "return RenderError(\"公众号配置不存在:\" + mpId);": "return RenderError(\"The official account configuration does not exist: \" + mpId);", "/ 只添加未添加的用户": "/ Add only unadded users", "/ 添加未添加的用户,同时更新所有信息(时间较长)": "/ Add unadded users and update all information at the same time (long time)", "\"olPjZjsbk4WzEbbGDkWWHuwhpg1M\"//测试ID": "\"olPjZjsbk4WzEbbGDkWWHuwhpg1M\"//Test ID", "同步Tag": "Sync Tag", "添加或更新 UserTag": "Add or update UserTag", "? _userTagService.Mapper.Map(tag)//直接用tag": "? _userTagService.Mapper.Map(tag)//Use tag directly", "从数据库获取": "Get from database", "名称不一样,手动更新": "The names are different, please update manually", "移除删除的 Tag": "Remove deleted Tag", "最大并发线程数": "Maximum number of concurrent threads", "需要更新,获取用户信息": "Need to update, obtain user information", "查看是否有更新,对数据进行对比": "Check if there are any updates and compare the data", "SenparcTrace.SendCustomLog(\"weixinUser 实体信息\", $\"{weixinUser.ToJson(false, new Newtonsoft.Json.JsonSerializerSettings() { ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore })}\");": "SenparcTrace.SendCustomLog(\"weixinUser entity information\", $\"{weixinUser.ToJson(false, new Newtonsoft.Json.JsonSerializerSettings() { ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore })}\");", "添加或删除个人的 Tag": "Add or delete personal tags", "这种情况应该很少": "This should be rare", "添加未添加的Tag": "Add unadded Tag", "处理需要删除的Tag": "Process tags that need to be deleted", "TODO:更新group信息": "TODO: Update group information", "只开启N个线程,等待": "Only open N threads and wait", "清空已完成的线程": "Clear completed threads", "base.SetMessager(Ncf.Core.Enums.MessageType.success, \"更新成功!\");": "base.SetMessager(Ncf.Core.Enums.MessageType.success, \"Update successful!\");", "/ 微信表情缓存": "/ WeChat emoticon cache", "遍历FaceImage属性": "Traverse FaceImage properties", "/ 用户是否订阅该公众号标识,值为0时,代表此用户没有关注该公众号,拉取不到其余信息。": "/ Whether the user subscribes to the official account. When the value is 0, it means that the user does not follow the official account and cannot obtain other information.", "/ 用户的标识,对当前公众号唯一": "/ User’s identification, unique to the current official account", "/ 用户的昵称": "/ user's nickname", "/ 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知": "/ The gender of the user, when the value is 1, it is male, when the value is 2, it is female, when the value is 0, it is unknown", "/用户的语言,简体中文为zh_CN": "/User's language, simplified Chinese is zh_CN", "/ 用户所在城市": "/User's city", "/ 用户所在省份": "/Province where the user is located", "/ 用户所在国家": "/user's country", "/ 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。": "/ User avatar, the last value represents the size of the square avatar (there are 0, 46, 64, 96, and 132 values ​​​​available, 0 represents a 640*640 square avatar), this item is empty when the user does not have an avatar. If the user changes their avatar, the original avatar URL will be invalid.", "/ 用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间": "/ The time the user paid attention to is the timestamp. If the user has followed multiple times, the last follow time will be used", "/ 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。": "/ This field will only appear after the user binds the official account to the WeChat Open Platform account.", "/ 公众号运营者对粉丝的备注,公众号运营者可在微信公众平台用户管理界面对粉丝添加备注": "/ Official account operators’ notes to fans. Official account operators can add notes to fans on the WeChat public platform user management interface.", "/ 用户所在的分组ID(兼容旧的用户分组接口)": "/ The group ID of the user (compatible with the old user group interface)", "/// 用户标签": "/// User tag", "/ 返回用户关注的渠道来源,ADD_SCENE_SEARCH 公众号搜索,ADD_SCENE_ACCOUNT_MIGRATION 公众号迁移,ADD_SCENE_PROFILE_CARD 名片分享,ADD_SCENE_QR_CODE 扫描二维码,ADD_SCENEPROFILE LINK 图文页内名称点击,ADD_SCENE_PROFILE_ITEM 图文页右上角菜单,ADD_SCENE_PAID 支付后关注,ADD_SCENE_OTHERS 其他": "/ Return to the channel source that the user is concerned about, ADD_SCENE_SEARCH public account search, ADD_SCENE_ACCOUNT_MIGRATION public account migration, ADD_SCENE_PROFILE_CARD business card sharing, ADD_SCENE_QR_CODE scan QR code, ADD_SCENEPROFILE LINK click on the name in the image and text page, ADD_SCENE_PROFILE_ITEM Menu in the upper right corner of the graphic page, ADD_SCENE_PAID, follow after payment, ADD_SCENE_OTHERS others", "/ 二维码扫码场景(开发者自定义)": "/ QR code scanning scenario (developer customized)", "/ 二维码扫码场景描述(开发者自定义)": "/ QR code scanning scene description (developer-defined)", "/ 用户标签": "/user tag", "/ 微信公众号信息": "/ WeChat public account information", "/ 微信接口的 tagid": "/ tagid of WeChat interface", "关于 EF 多对多的做法:https://www.entityframeworktutorial.net/efcore/configure-many-to-many-relationship-in-ef-core.aspx": "About EF many-to-many approach: https://www.entityframeworktutorial.net/efcore/configure-many-to-many-relationship-in-ef-core.aspx", "/ UserTag - WeixinUser 多对多关联表": "/UserTag - WeixinUser many-to-many association table", "/ 微信用户": "/ WeChat user", "/ 对应微信API tagid_list 属性": "/ Corresponding to WeChat API tagid_list attribute", "/ 用于生成 DM(DaMeng) 数据库 Migration 信息的类,请勿修改": "/ Class used to generate DM(DaMeng) database Migration information, please do not modify it", "/ 2、运行:PM> add-migration [更新名称] -C WeixinSenparcEntities_SqlServer -o Migrations/Migrations.SqlServer ": "/ 2. Run: PM> add-migration [update name] -C WeixinSenparcEntities_SqlServer -o Migrations/Migrations.SqlServer ", "/ 用于生成 MySql 数据库 Migration 信息的类,请勿修改": "/ Class used to generate MySql database Migration information, please do not modify it", "/ 2、运行:PM> add-migration [更新名称] -C WeixinSenparcEntities_MySql -o Migrations/Migrations.MySql ": "/ 2. Run: PM> add-migration [update name] -C WeixinSenparcEntities_MySql -o Migrations/Migrations.MySql ", "/ 2、运行:PM> add-migration [更新名称] -C WeixinSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ": "/ 2. Run: PM> add-migration [update name] -C WeixinSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ", "/ 用于生成 SQLite 数据库 Migration 信息的类,请勿修改": "/ Class used to generate SQLite database migration information, please do not modify it", "哭 = 5,": "cry = 5,", "笑 = 13,": "laugh = 13,", "天使": "Angel", "/ 获取指定的 MpAccount 对象": "/ Get the specified MpAccount object", "表情中有<>之类符号,需要考虑到进来的content已经HtmlEncode的情况": "If there are symbols such as <> in the expression, you need to consider that the incoming content has been HtmlEncoded.", "整理keywords格式": "Organize keywords format", "/ 相对路径": "/ Relative path", "创建目录": "Create directory", "/ 下载微信临时素材Image": "/ Download WeChat temporary material Image", "保存到文件": "save to file", "判断是否上传成功": "Determine whether the upload is successful", "TODO:写到配置文件里": "TODO: Write to the configuration file", "/ 审核结果通知": "/ Notification of audit results", "审核事项:{{keyword1.DATA}}": "Review matters: {{keyword1.DATA}}", "审核状态:{{keyword2.DATA}}": "Review status: {{keyword2.DATA}}", "审核时间:{{keyword3.DATA}}": "Review time: {{keyword3.DATA}}", "你的审核请求已经处理。": "Your review request has been processed.", "审核事项:机构申请注册": "Review matters: Organization application for registration", "审核状态:通过": "Review status: Passed", "审核时间:2017年2月5日18:17": "Review time: 18:17 on February 5, 2017", "点击查看详情": "Click to view details", "/ 认证详情": "/ Authentication details", "/ 认证结果": "/ Authentication result", "/ 获取唯一 Key。": "/ Get the unique Key.", "/ 使用 PromptRangeCode 参与到 Key 的标记中,可以实现实时 Prompt 的更新": "/ Use PromptRangeCode to participate in Key tagging to achieve real-time Prompt updates", "/ 构建并获取 IWantToRun 对象": "/ Build and get the IWantToRun object", "只有靶场,自动选择最好的版本": "Range only, automatically selecting the best version", "AI 模型参数": "AI model parameters", "配置和初始化模型": "Configure and initialize the model", "/ 用自动识别在系统中绑定的公众号 MessageHandler": "/ Use MessageHandler to automatically identify the official account bound in the system", "TODO:判断是否为“文生图”的请求": "TODO: A request to determine whether it is a \"Vincent Picture\"", "//定义 AI 模型": "//Define AI model", "获取 AI 处理器": "Get AI Processor", "发送到 AI 模型,获取结果": "Send to AI model and get results", "var resultMsg = $\"{result.Output}\\r\\n -- AI 计算耗时:{SystemTime.DiffTotalMS(dt)}毫秒\";": "var resultMsg = $\"{result.Output}\\r\\n -- AI calculation time: {SystemTime.DiffTotalMS(dt)} milliseconds\";", "Console.WriteLine(\"公众号客服消息:\" + resultMsg);": "Console.WriteLine(\"Official account customer service message: \" + resultMsg);", "获取到结果后执行方法": "Execute the method after getting the result", "异步发送 AI 结果到用户": "Asynchronously send AI results to users", "_ = Senparc.Weixin.MP.AdvancedAPIs.CustomApi.SendTextAsync(_mpAccountDto.AppId, requestMessage.FromUserName, $\"总共耗时:{SystemTime.DiffTotalMS(dt)}ms\");": "_ = Senparc.Weixin.MP.AdvancedAPIs.CustomApi.SendTextAsync(_mpAccountDto.AppId, requestMessage.FromUserName, $\"Total time spent: {SystemTime.DiffTotalMS(dt)}ms\");", "responseMessage.Content = \"您发送了文字:\" + requestMessage.Content;": "responseMessage.Content = \"You sent text:\" + requestMessage.Content;", "不返回任何信息": "Do not return any information", "返回明文提示": "Return clear text prompt", "启用 WebApi(可选)": "Enable WebApi (optional)", "AutoMap映射不能在这里做,因为执行到此处时,相关过程已经执行完毕": "AutoMap mapping cannot be done here, because when the execution reaches here, the relevant process has already been executed.", "需要引入中间件的模块": "Modules that need to introduce middleware", "/ 创建 messageHandlerFunc 对象": "/ Create messageHandlerFunc object", "/ 由于泛型 TMC 在编程时未知,因此创建此方法,提供给反射使用,用于构造最终的 messageHandlerFunc 对象": "/ Since the generic TMC is unknown during programming, this method is created and provided for reflection to construct the final messageHandlerFunc object", "注册全局缓存信息": "Register global cache information", "使用反射构造 MessageHandler 对象": "Constructing a MessageHandler object using reflection", "未注册": "Not registered", "消息上下文泛型": "message context generic", "TODO:校验上下文类型正确性": "TODO: Verify context type correctness", "注册中间件": "Register middleware", "获取 MessageHandlerFunc 方法(因为 TMC 类型未知,因此使用反射获取泛型方法后得到带泛型的 MessageHandler>)": "Get the MessageHandlerFunc method (because the TMC type is unknown, use reflection to get the generic method and get the generic MessageHandler>)", "当前站点的微信 URL 接口地址": "WeChat URL interface address of the current site", "微信配置": "WeChat configuration", "说明:此代码块中演示了较为全面的功能点,简化的使用可以参考下面小程序和企业微信": "Note: This code block demonstrates relatively comprehensive function points. For simplified use, please refer to the following mini programs and enterprise WeChat", "此处为委托,可以根据条件动态判断输入条件(必须)": "Here is the delegation, which can dynamically determine the input conditions based on the conditions (required)", "TODO:注册 Config.SenparcWeixinSetting": "TODO: Register Config.SenparcWeixinSetting", "方法二:使用指定配置:": "Method 2: Use specified configuration:", "方法三:结合 context 参数动态判断返回Setting值": "Method 3: Combined with the context parameter to dynamically determine and return the Setting value", "对 MessageHandler 内异步方法未提供重写时,调用同步方法(按需)": "When no overriding is provided for asynchronous methods in MessageHandler, call synchronous methods (on demand)", "对发生异常进行处理(可选)": "Handle exceptions (optional)", "逻辑处理...": "Logical processing...", "系统层面抛出异常": "Exception thrown at system level", "获取微信中间件方法": "Get WeChat middleware method", "调用 app.UseMessageHandlerForMp() 扩展方法": "Call the app.UseMessageHandlerForMp() extension method", "//说明:此代码块中演示了较为全面的功能点,简化的使用可以参考下面小程序和企业微信": "//Note: This code block demonstrates relatively comprehensive function points. For simplified use, please refer to the following mini program and enterprise WeChat.", "#region 配置 SenparcWeixinSetting 参数,以自动提供 Token、EncodingAESKey 等参数": "#region Configure SenparcWeixinSetting parameters to automatically provide Token, EncodingAESKey and other parameters", "//此处为委托,可以根据条件动态判断输入条件(必须)": "//Here is the commission, which can dynamically determine the input conditions based on the conditions (required)", "//TODO:注册 Config.SenparcWeixinSetting": "//TODO: Register Config.SenparcWeixinSetting", "//方法二:使用指定配置:": "//Method 2: Use specified configuration:", "//方法三:结合 context 参数动态判断返回Setting值": "//Method 3: Combined with context parameters to dynamically determine and return the Setting value", "//对 MessageHandler 内异步方法未提供重写时,调用同步方法(按需)": "//When no overriding is provided for the asynchronous method in MessageHandler, call the synchronous method (on demand)", "//对发生异常进行处理(可选)": "//Handle exceptions (optional)", "//逻辑处理...": "//Logical processing...", "return false;//系统层面抛出异常": "return false;//Exception thrown at system level", "需要使用 RazorRuntimeCompilation,在开发环境下实时更新 Razor Page": "Need to use RazorRuntimeCompilation to update Razor Page in real time in the development environment", "注册 XNCF 基础模块接口(必须)": "Register XNCF basic module interface (required)", "//根据条件生成不同的PostModel": "//Generate different PostModels based on conditions", "是否自动生成API": "Whether to automatically generate API", "如果重写此方法,必须调用基类方法": "If you override this method, you must call the base class method", "TODO:可以在基础模块里给出选项是否删除": "TODO: You can give the option whether to delete it in the basic module", "按照删除顺序排序": "Sort by deletion order", "等待数据库注册完成后运行": "Wait for database registration to complete before running", "注册微信": "Register WeChat", "开始查询数据库": "Start querying the database", "未安装数据库表的情况下可能会出错,因此需要try": "An error may occur if the database table is not installed, so try", "批量自动注册公众号": "Automatically register public accounts in batches", "TODO:更多执行过程中的动态注册": "TODO: More dynamic registration during execution", "//TODO:真实的动态版本号": "//TODO: real dynamic version number", "每次切换定义,都需要经过比较长的时间才到达这里": "Every time you switch definitions, it takes a long time to get here.", "移除非当前模块的API对象": "Remove API objects that are not in the current module", "移除Schema对象": "Remove Schema object", "var toRemoveSchema = context.SchemaRepository.Schemas.Where(z => !z.Key.Contains(platformType)).ToList();//结果为全部删除,仅测试": "var toRemoveSchema = context.SchemaRepository.Schemas.Where(z => !z.Key.Contains(platformType)).ToList();//The result is all deletion, only testing", "//获取是否添加登录特性": "//Get whether to add login features", "operation.Responses.Add(\"401\", new OpenApiResponse { Description = \"暂无访问权限\" });": "operation.Responses.Add(\"401\", new OpenApiResponse { Description = \"No access rights yet\" });", "operation.Responses.Add(\"403\", new OpenApiResponse { Description = \"禁止访问\" });": "operation.Responses.Add(\"403\", new OpenApiResponse { Description = \"Access Forbidden\" });", "/ XncfBuilder 的 Prompt 类型": "/ Prompt type of XncfBuilder", "/ 实体类": "/ Entity class", "/ 实体类 DTO": "/ Entity class DTO", "/ 更新 SenparcEntities": "/Update SenparcEntities", "/ Sln 文件路径": "/Sln file path", "/ 组织名称": "/organization name", "/ 2、运行:PM> add-migration [更新名称] -C XncfBuilderSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ": "/ 2. Run: PM> add-migration [update name] -C XncfBuilderSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ", "/ 2、运行:PM> add-migration [更新名称] -C XncfBuilderSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ": "/ 2. Run: PM> add-migration [update name] -C XncfBuilderSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ", "/ 2、运行:PM> add-migration [更新名称] -C XncfBuilderSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ": "/ 2. Run: PM> add-migration [update name] -C XncfBuilderSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ", "/ 2、运行:PM> add-migration [更新名称] -C XncfBuilderSenparcEntities_PostgreSQL -o Domain/Migrations/Migrations.PostgreSQL ": "/ 2. Run: PM> add-migration [update name] -C XncfBuilderSenparcEntities_PostgreSQL -o Domain/Migrations/Migrations.PostgreSQL ", "/ 文件生成结果": "/File generation results", "/ 文件名(可能会同时包含路径)": "/ filename (may also include path)", "/ 代码或文件内容": "/ code or file content", "TODO:文件修改(从文件中抽取,然后给到 LLM 进行修改)": "TODO: File modification (extracted from the file and then sent to LLM for modification)", "\"//DOT REMOVE OR MODIFY THIS LINE 请勿移除或修改本行 - Entities Point\";": "\"//DOT REMOVE OR MODIFY THIS LINE Do not remove or modify this line - Entities Point\";", "\"//DON'T REMOVE OR MODIFY THIS LINE 请勿移除或修改本行 - Entities Point\";": "\"//DON'T REMOVE OR MODIFY THIS LINE Do not remove or modify this line - Entities Point\";", "/ 运行提示内容": "/ Run prompt content", "选择需要执行的生成方式": "Select the generation method to be executed", "可能会生成转义后的注释": "May generate escaped comments", "需要保存文件": "Need to save file", "输入生成文件的项目路径": "Enter the project path to the generated file", "var context = _promptService.IWantToRun.Kernel.CreateNewContext();//TODO:简化": "var context = _promptService.IWantToRun.Kernel.CreateNewContext();//TODO: Simplify", "TODO:简化": "TODO: Simplify", "添加保存文件的 Plugin": "Add Plugin for saving files", "/ 初始化数据库中 PromptRange 的 XncfBuilderPlugin 靶场信息": "/ Initialize the XncfBuilderPlugin range information of PromptRange in the database", "需要删除": "Need to delete", "删除所有 PromptItem": "Delete all PromptItems", "添加 PromptRange": "Add PromptRange", "添加 PromptItem": "AddPromptItem", "注意:仍然会被自动重置": "NOTE: Will still be automatically reset", "添加 PromptItem-GenerateEntityDtoClass": "Add PromptItem-GenerateEntityDtoClass", "添加 PromptItem-Pluralize": "Add PromptItem-Pluralize", "[McpServerTool, Description(\"生成 XNCF 模块\")]": "[McpServerTool, Description(\"Generate XNCF module\")]", "[FunctionRender(\"生成 XNCF\", \"根据配置条件生成 XNCF\", typeof(Register))]": "[FunctionRender(\"Generate XNCF\", \"Generate XNCF according to configuration conditions\", typeof(Register))]", "[Required,Description(\"解决方案文件路径\")]": "[Required,Description(\"Solution file path\")]", "new Ncf.XncfBase.Functions.SelectionItem(\"1\",\"使用Web\",\"使用Web\",true),": "new Ncf.XncfBase.Functions.SelectionItem(\"1\",\"Use Web\",\"Use Web\",true),", "new Ncf.XncfBase.Functions.SelectionItem(\"1\",\"使用WebApi\",\"使用WebApi\",true),": "new Ncf.XncfBase.Functions.SelectionItem(\"1\",\"Use WebApi\",\"Use WebApi\",true),", "[McpServerTool, Description(\"获取前端代码模板示例\")]": "[McpServerTool, Description(\"Get front-end code template example\")]", "[McpServerTool, Description(\"获取后端代码模板示例\")]": "[McpServerTool, Description(\"Get backend code template example\")]", "[McpServerTool, Description(\"获取文件内容\")]": "[McpServerTool, Description(\"Get file content\")]", "[McpServerTool, Description(\"创建或更新文件内容,文件不存在时会自动创建\")]": "[McpServerTool, Description(\"Create or update file content, the file will be created automatically if it does not exist\")]", "TODO: 使用 SHA1 验证指纹,把旧文件内容进行缓存或差量备份": "TODO: Use SHA1 to verify fingerprints and cache or differentially back up old file contents", "/ AI 生成数据库实体": "/AI generates database entities", "开始安装模块(创建数据库相关表)": "Start installing the module (create database related tables)", "使用 PromptRange 时不需要指定 AI 模型": "No need to specify an AI model when using PromptRange", "检查是否已经初始化": "Check if it has been initialized", "如果不使用 PromptRange,则需要指定 AI 模型": "If you do not use PromptRange, you need to specify the AI ​​model", "从 promptGroupFileContent 分析获得类名": "Obtain class name from promptGroupFileContent analysis", "目标项目的文件夹名称": "The folder name of the target project", "目标项目的 csproj 项目文件名": "csproj project file name of the target project", "获取 元素": "Get the element", "删除所有的 ProjectReference": "Delete all ProjectReferences", "添加新的 ProjectReference": "Add new ProjectReference", "保存修改后的 .csproj 文件": "Save the modified .csproj file", "载入数据": "Load data", "指定项目路径": "Specify project path", "选中所有数据库": "Select all databases", "选中输出详情": "Check output details", "为了加快响应速度,不等待": "For faster response time, do not wait", "TODO:生成实体的过程中默认检查是否自动载入Prompt,提供手动指定地址选项": "TODO: During the process of generating entities, it checks whether Prompt is automatically loaded by default, and provides the option of manually specifying the address.", "初始化 PromptRange 方法(需要先确保已经安装)": "Initialize the PromptRange method (need to ensure it is installed first)", "[FunctionRender(\"[AI] 生成 AppService\", \"使用 AI 指令生成 AppService\", typeof(Register))]": "[FunctionRender(\"[AI] Generate AppService\", \"Use AI instructions to generate AppService\", typeof(Register))]", "/ 执行模板生成": "/Execute template generation", "采用一个独立的进程": "use a separate process", "$\"dotnet new XNCF \",//仅作为标记": "$\"dotnet new XNCF \",//Only as a mark", "执行模板生成": "Perform template generation", "生成 .sln": "Generate .sln", "是否创建新的 .sln 文件": "Whether to create a new .sln file", "/ 获取迁移文件生成目录": "/ Get the migration file generation directory", "设置代码页为 UTF-8": "Set code page to UTF-8", "添加停机坪引用(直接引用会有问题)": "Add tarmac reference (direct reference will cause problems)", "进入项目目录": "Enter the project directory", "执行迁移": "Execute migration", "数据库上下文实体名称": "Database context entity name", "会自动拼接数据类型": "Data types will be automatically spliced", "把 request.DatabasePlantPath 中独立存在的 \\ 替换为 \\\\": "Replace the independent \\ in request.DatabasePlantPath with \\\\", "如需指定框架,可以追加上述参数,也可以支持更多参数,如net5.0": "If you need to specify a framework, you can add the above parameters, and you can also support more parameters, such as net5.0", "//移除停机坪引用(直接引用会有问题)": "//Remove the apron reference (direct reference will cause problems)", "//Pomelo-MySQL 命名有不统一的情况,需要处理": "//Pomelo-MySQL naming is inconsistent and needs to be dealt with", "base.RecordLog(sb, $\"扫描到不兼容常规格式的 Pomelo.EntityFrameworkCore.MySql 的快照文件:{pomeloFileName},已将默认文件删除({defaultFileName})!\");": "base.RecordLog(sb, $\"Scanned the snapshot file of Pomelo.EntityFrameworkCore.MySql that is incompatible with the regular format: {pomeloFileName}, the default file has been deleted ({defaultFileName})!\");", "更新版本号": "Update version number", "获取 Register.cs 文件内容": "Get the contents of the Register.cs file", "获取版本号": "Get version number", "更新代码": "Update code", "保存代码": "save code", "/ 生成 生成 AppService 接口代码": "/Generate Generate AppService interface code", "Endpoint 可能未配置": "Endpoint may not be configured", "扫描当前解决方案包含的所有领域项目": "Scan all domain projects contained in the current solution", "[Description(\"领域||指定需要生成到的领域\")]": "[Description(\"Realm||Specify the domain to be generated\")]", "[Description(\"生成 AppService 及其方法的具体需求||请输入尽量完整的需求,也可以指定所需要的方法名称等\")]": "[Description(\"Specific requirements for generating AppService and its methods || Please enter as complete requirements as possible, and you can also specify the required method names, etc.\")]", "//扫描当前解决方案包含的所有领域项目": "//Scan all domain projects included in the current solution", "new SelectionItem(\"netstandard2.1\",\"netstandard2.1\",\"使用 .NET Standard 2.1(兼容 .NET Core 3.1 和 .NET 5.0-8.0)\",true),": "new SelectionItem(\"netstandard2.1\",\"netstandard2.1\",\"Use .NET Standard 2.1 (compatible with .NET Core 3.1 and .NET 5.0-8.0)\",true),", "new SelectionItem(\"netcoreapp3.1\",\"netcoreapp3.1\",\"使用 .NET Core 3.1\",false),": "new SelectionItem(\"netcoreapp3.1\",\"netcoreapp3.1\",\"Use .NET Core 3.1\",false),", "new SelectionItem(\"net6.0\",\"net6.0\",\"使用 .NET 6.0\",false),": "new SelectionItem(\"net6.0\",\"net6.0\",\"Use .NET 6.0\",false),", "new SelectionItem(\"net7.0\",\"net7.0\",\"使用 .NET 7.0\",false),": "new SelectionItem(\"net7.0\",\"net7.0\",\"Use .NET 7.0\",false),", "[Description(\"Uid||必须确保全局唯一,生成后必须固定\")]": "[Description(\"Uid|| must be globally unique and must be fixed after generation\")]", "/ 预载入数据": "/ preload data", "低版本没有数据库,此处需要try": "The lower version does not have a database, you need to try here", "/ 判断当前路径下是否包含 .sln 文件": "/ Determine whether the current path contains a .sln file", "/ 获取当前解决方案文件路径": "/ Get the current solution file path", "当前程序目录": "Current program directory", "向上查找,直到找到": "Search upwards until you find", "TODO:单独生成一个表来记录": "TODO: Generate a separate table to record", "添加“停机坪”路径": "Add a \"tarmac\" path", "添加当前解决方案的项目选项": "Add project options for current solution", "添加 NcfPackageSource 项目的解决方案的项目选项": "Add project options to solution for NcfPackageSource project", "/ 获取项目路径": "/ Get project path", "services.AddScoped();//注意:此处不能直接这样自动配置数据库实体,基类中已经统一配置 implementationFactory": "services.AddScoped();//Note: The database entity cannot be automatically configured directly here. The implementationFactory has been configured uniformly in the base class.", "删除数据库表": "Delete database table", "Senparc.Xncf.AIKernel 模块": "Senparc.Xncf.AIKernel module", "新增字段测试动态更新": "Add new fields to test dynamic updates", "新增属性测试 Response.cs 独立修改": "New attribute test Response.cs independently modified", "获取配置文件": "Get configuration file", "获取所有需要处理的文件": "Get all files that need to be processed", "组合配置和文件": "Combine configuration and files", "注册源代码生成": "Register source code generation", "如果没有配置文件,使用默认行为": "If there is no configuration file, use the default behavior", "处理每个配置的文件": "Process files for each configuration", "生成代码": "Generate code", "首先尝试从 AdditionalFiles 中查找": "First try to find from AdditionalFiles", "添加文件头注释": "Add file header comments", "添加命名空间": "Add namespace", "添加类定义": "Add class definition", "按类型分组生成常量": "Generate constants grouped by type", "生成每个文件的常量": "Generate constants for each file", "生成类型常量": "Generate type constant", "生成文件信息数组": "Generate file information array", "生成辅助方法": "Generate helper methods", "默认行为:处理所有 .cs 文件": "Default behavior: Process all .cs files", "/ 对所有扩展 Area 进行注册": "/Register all extension areas", "/ 自动注册所有 Area": "/ Automatically register all Areas", "/ 遍历到每一个 Register 额外的操作": "/ Traverse to each Register additional operations", "进行注册": "Register", "执行额外的操作": "perform additional actions", "已经不需要重新扫描": "No need to rescan anymore", "//进行过滤": "//Filter", "register.AuthorizeConfig(builder, env);//进行注册": "register.AuthorizeConfig(builder, env);//Register", "eachRegsiterAction?.Invoke(register);//执行额外的操作": "eachRegsiterAction?.Invoke(register);//Perform additional operations", "SenparcTrace.BaseExceptionLog(new BaseException($\"{registerType.Name} 类型没有实现接口 IAreaRegister!\"));": "SenparcTrace.BaseExceptionLog(new BaseException($\"{registerType.Name} type does not implement the interface IAreaRegister!\"));", "var title = \"AddNcfAreas() 自动扫描程序集报告(非程序异常):\" + assembly.FullName;": "var title = \"AddNcfAreas() automatic scan assembly report (non-program exception):\" + assembly.FullName;", "所有 AuthorizeConfig 方法已经执行完成": "All AuthorizeConfig methods have been executed", "/ 启动带 Web 功能的 NCF 引擎(如不需要使用 Web,如 RazorPage,可以直接使用 )": "/ Start the NCF engine with Web function (if you do not need to use the Web, such as RazorPage, you can directly use )", "/ services.AddRazorPages() 的内部委托": "/ Internal delegate for services.AddRazorPages()", "/ 被包含的 dll 的文件名,“.Xncf.”会被必定包含在里面": "/ The file name of the included dll, \".Xncf.\" will definitely be included", "注册所有 Ncf 的 Area 模块(必须)": "Register all Ncf Area modules (required)", "/ 数据库类型": "/ Database type", "添加 RazorPage 和 Area": "Add RazorPage and Area", "提供网站根目录": "Provide website root directory", "忽略循环引用": "Ignore circular references", "不使用驼峰样式的key": "Do not use camel case keys", "设置时间格式": "Set time format", "忽略JSON序列化过程中的循环引用:https://stackoverflow.com/questions/7397207/json-net-error-self-referencing-loop-detected-for-type": "Ignore circular references during JSON serialization: https://stackoverflow.com/questions/7397207/json-net-error-self-referencing-loop-detected-for-type", "自动注册 防止跨站请求伪造(XSRF/CSRF)攻击": "Automatic registration to prevent cross-site request forgery (XSRF/CSRF) attacks", "Razor启用运行时编译,多个项目不需要手动编译。": "Razor enables runtime compilation, eliminating the need for manual compilation for multiple projects.", "//自动索引所有需要使用 RazorRuntimeCompilation 的模块": "//Automatically index all modules that need to use RazorRuntimeCompilation", "/ 菜单": "/ menu", "/ 菜单下面的按钮": "/ button below the menu", "/ 2、运行:PM> add-migration [更新名称] -c MenuSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ": "/ 2. Run: PM> add-migration [update name] -c MenuSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ", "/ 2、运行:PM> add-migration [更新名称] -c MenuSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ": "/ 2. Run: PM> add-migration [update name] -c MenuSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ", "/ 2、运行:PM> add-migration [更新名称] -c MenuSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ": "/ 2. Run: PM> add-migration [update name] -c MenuSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ", "/ 2、运行:PM> add-migration [更新名称] -c MenuSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ": "/ 2. Run: PM> add-migration [update name] -c MenuSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ", "/ 2、运行:PM> add-migration [更新名称] -c MenuSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ": "/ 2. Run: PM> add-migration [update name] -c MenuSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ", "/ 2、运行:PM> add-migration [更新名称] -c MenuSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ": "/ 2. Run: PM> add-migration [update name] -c MenuSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ", "安装或升级数据库": "Install or upgrade database", "TODO: 如果后期没有明显变化(如额外扩展),考虑合并 SenparcEntities,并取代之": "TODO: If there are no obvious changes later (such as additional extensions), consider merging SenparcEntities and replacing them", "/ 当前 Entities 只为帮助 SenparcEntities 生成 Migration 信息而存在,没有特别的操作意义。": "/ Current Entities only exist to help SenparcEntities generate Migration information and have no special operational significance.", "/ 2、将当前项目设为启动项": "/ 2. Set the current project as the startup item", "/ 3、打开【程序包资源管理器控制台】,默认项目设为当前项目": "/ 3. Open the [Package Explorer Console] and set the default project to the current project", "/ 4、运行:PM> add-migration [更新名称] -Context BasePoolEntities_Dm -o SystemEntities/Migrations/Migrations.Dm.SystemEntities": "/ 4. Run: PM> add-migration [update name] -Context BasePoolEntities_Dm -o SystemEntities/Migrations/Migrations.Dm.SystemEntities", "/ 4、运行:PM> add-migration [更新名称] -Context BasePoolEntities_MySql -o SystemEntities/Migrations/Migrations.MySql.SystemEntities": "/ 4. Run: PM> add-migration [update name] -Context BasePoolEntities_MySql -o SystemEntities/Migrations/Migrations.MySql.SystemEntities", "默认配置": "Default configuration", "/ 4、运行:PM> add-migration [更新名称] -Context BasePoolEntities_Oracle -o SystemEntities/Migrations/Migrations.Oracle.SystemEntities": "/ 4. Run: PM> add-migration [update name] -Context BasePoolEntities_Oracle -o SystemEntities/Migrations/Migrations.Oracle.SystemEntities", "/ 4、运行:PM> add-migration [更新名称] -Context BasePoolEntities_PostgreSQL -o SystemEntities/Migrations/Migrations.PostgreSQL.SystemEntities": "/ 4. Run: PM> add-migration [update name] -Context BasePoolEntities_PostgreSQL -o SystemEntities/Migrations/Migrations.PostgreSQL.SystemEntities", "/ 4、运行:PM> add-migration [更新名称] -Context BasePoolEntities_SqlServer -o SystemEntities/Migrations/Migrations.SqlServer.SystemEntities": "/ 4. Run: PM> add-migration [update name] -Context BasePoolEntities_SqlServer -o SystemEntities/Migrations/Migrations.SqlServer.SystemEntities", "/ 4、运行:PM> add-migration [更新名称] -Context BasePoolEntities_SQLite -o SystemEntities/Migrations/Migrations.SQLite.SystemEntities": "/ 4. Run: PM> add-migration [update name] -Context BasePoolEntities_SQLite -o SystemEntities/Migrations/Migrations.SQLite.SystemEntities", "Senparc.Ncf.Core.Config.SiteConfig.SenparcCoreSetting.DatabaseName = \"Local\";//默认配置": "Senparc.Ncf.Core.Config.SiteConfig.SenparcCoreSetting.DatabaseName = \"Local\";//Default configuration", "/// 当前 Entities 只为帮助 SenparcEntities 生成 Migration 信息而存在,没有特别的操作意义。": "/// The current Entities only exist to help SenparcEntities generate Migration information and have no special operational significance.", "/// 设计时 DbContext 创建(仅在开发时创建 Code-First 的数据库 Migration 使用,在生产环境不会执行)": "/// DbContext creation at design time (Code-First database migration is only used during development and will not be executed in the production environment)", "/// 1、切换至 Debug 模式": "/// 1. Switch to Debug mode", "/// 2、将当前项目设为启动项": "/// 2. Set the current project as the startup item", "/// 3、打开【程序包资源管理器控制台】,默认项目设为当前项目": "/// 3. Open the [Package Explorer Console] and set the default project to the current project", "/// 4、运行:PM> add-migration [更新名称] -Context BasePoolEntities_SqlServer -o SystemEntities/Migrations/Migrations.SqlServer.SystemEntities": "/// 4. Run: PM> add-migration [update name] -Context BasePoolEntities_SqlServer -o SystemEntities/Migrations/Migrations.SqlServer.SystemEntities", "//指定其他数据库": "//Specify other databases", "/* Debug模式下项目根目录": "/* Project root directory in Debug mode", "/* 用于寻找 App_Data 文件夹,从而找到数据库连接字符串配置信息 */": "/* Used to find the App_Data folder to find the database connection string configuration information */", "/ 关闭连接(长时间保持一个连接操作会导致数据库操作时间逐渐变长)": "/ Close the connection (maintaining a connection operation for a long time will cause the database operation time to gradually become longer)", "COCONET 升级到.net core的过程中注释掉": "Comment out COCONET when upgrading to .net core.", "TODO:当前采用注入可以保证HttpContext单例,如果要全局单例,可采用单件模式(需要先解决释放的问题)": "TODO: Currently, injection can ensure the HttpContext singleton. If you want a global singleton, you can use the singleton mode (you need to solve the release problem first)", "System.Web.HttpContext.Current.Response.Write(dataContext.GetHashCode() + \"
\");//测试同一Request只有一个SenparcEntities实例": "System.Web.HttpContext.Current.Response.Write(dataContext.GetHashCode() + \"
\");//Test that there is only one SenparcEntities instance for the same Request", "/ 【注意】SenparcEntities 不存放任何实体,也不生成任何迁移文件": "/ [Note] SenparcEntities does not store any entities or generate any migration files.", "计算所有类型名称的最大长度": "Calculate the maximum length of all type names", "使用字符串格式化,使输出对齐": "Use string formatting to align output", "注册所有 XncfAutoConfigurationMapping 动态模块": "Register all XncfAutoConfigurationMapping dynamic modules", "基类中的系统表处理": "System table handling in base classes", "NcfDatabaseMigrationHelper.SYSTEM_UNIQUE_PREFIX;//系统表,": "NcfDatabaseMigrationHelper.SYSTEM_UNIQUE_PREFIX;//System table,", "参考信息": "Reference information", "* 错误信息:": "* error message:", "* 中文:EnableRetryOnFailure 解决短暂的数据库连接失败": "* Chinese: EnableRetryOnFailure solves temporary database connection failure", "* 英文:Win32Exception: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond": "* English: Win32Exception: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond", "* 问题解决方案说明:https://www.colabug.com/2329124.html": "* Problem solution description: https://www.colabug.com/2329124.html", "* 非常重要!!": "* Very important! !", "* SenparcEntities 工厂配置": "* SenparcEntities factory configuration", "* SYSTEM 为特定标记,将直接定位到 __EFMigrationsHistory": "* SYSTEM is a specific tag and will be located directly to __EFMigrationsHistory", "继承自 DbContext": "Inherited from DbContext", "继承自 SenparcEntitiesMultiTenantBase": "Inherited from SenparcEntitiesMultiTenantBase", "BasePoolEntities 工厂配置(上层应用实际不会用到,构建 NcfClientDbData 时需要,NcfClientDbData 也不需要在正式系统中被使用到)": "BasePoolEntities factory configuration (the upper-layer application will not actually use it, it is needed when building NcfClientDbData, and NcfClientDbData does not need to be used in the official system)", "预加载 EntitySetKey": "Preload EntitySetKey", "暂时关闭多租户状态": "Temporarily turn off multi-tenancy status", "尝试执行更新": "Try to perform an update", "SenparcEntities 不进行任何数据库实体的构建,只作为容器": "SenparcEntities does not build any database entities, only serves as a container", "//更新数据库": "//update database", "basePoolEntities.ResetMigrate();//重置合并状态": "basePoolEntities.ResetMigrate();//Reset merge status", "basePoolEntities.Migrate();//进行合并": "basePoolEntities.Migrate();//Merge", "TODO:应该提供一个 BeforeUninstall 方法,阻止卸载。": "TODO: A BeforeUninstall method should be provided to prevent uninstallation.", "读取Log配置文件": "Read Log configuration file", "注册 CO2NET 基础引擎所需服务": "Register the services required by the CO2NET basic engine", "解决中文进行编码问题": "Solve the problem of encoding in Chinese", "注册 Lazy": "Register Lazy", "//自动依赖注入扫描": "//Automatic dependency injection scanning", "//已经添加完所有程序集自动扫描的委托,立即执行扫描(必须)": "//The delegates for automatic scanning of all assemblies have been added and the scan will be executed immediately (required)", "忽略某些 API": "Ignore some APIs", "用于解决HttpContext.Connection.RemoteIpAddress为null的问题": "Used to solve the problem that HttpContext.Connection.RemoteIpAddress is null", "FormFieldName = \"X-Http-Method-Override\"//此为默认值": "FormFieldName = \"X-Http-Method-Override\"//This is the default value", "//APM Ending 数据统计": "//APM Ending data statistics", "全部运行": "Run all", "更多 XNCF 模块线程已经集成到 Senparc.Ncf.XncfBase.Register.ThreadCollection 中": "More XNCF module threads have been integrated into Senparc.Ncf.XncfBase.Register.ThreadCollection", "/ 地区代码": "/area code", "/ 缩写(去掉“省”“市”“自治区”等)": "/ Abbreviation (remove \"province\", \"city\", \"autonomous region\", etc.)", "/ 意见反馈": "/Feedback", "/// 用户": "/// User", "/ 2、运行:PM> add-migration [更新名称] -c SystemManagerSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ": "/ 2. Run: PM> add-migration [update name] -c SystemManagerSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ", "/ 2、运行:PM> add-migration [更新名称] -c SystemManagerSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ": "/ 2. Run: PM> add-migration [update name] -c SystemManagerSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ", "/ 2、运行:PM> add-migration [更新名称] -c SystemManagerSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ": "/ 2. Run: PM> add-migration [update name] -c SystemManagerSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ", "/ 2、运行:PM> add-migration [更新名称] -c SystemManagerSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ": "/ 2. Run: PM> add-migration [update name] -c SystemManagerSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ", "/ 2、运行:PM> add-migration [更新名称] -c SystemManagerSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ": "/ 2. Run: PM> add-migration [update name] -c SystemManagerSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ", "/ 2、运行:PM> add-migration [更新名称] -c SystemManagerSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ": "/ 2. Run: PM> add-migration [update name] -c SystemManagerSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ", "/ 当前上下文不应该和租户无关": "/ The current context should not be irrelevant to the tenant", "/ 系统设置": "/system settings", "/ 更新 NeuChar 账号": "/Update NeuChar account", "校验并获取 NeuCharDeveloperId": "Verify and obtain NeuCharDeveloperId", "示范同步缓存锁": "Demonstration of synchronized cache locks", "删除缓存": "Delete cache", "分钟": "minute", "/ 权限": "/permissions", "/ 系统角色管理员": "/ system role administrator", "/ 租户 TenantInfo 的专用 TenantInfoClientRepositoryBase": "/ Private TenantInfoClientRepositoryBase for tenant TenantInfo", "需要关注及优化:": "Need attention and optimization:", "* 1、当前依赖注入的对象 INcfDbData 仍然会注入 SenparcEntitiesBase,": "* 1. The current dependency injected object INcfDbData will still be injected into SenparcEntitiesBase.", "* 而 SenparcEntitiesBase 的 OnModelCreating() 一旦被执行,会尝试读取此处的缓存,如果缓存未被初始化,就会陷入死循环。": "* Once OnModelCreating() of SenparcEntitiesBase is executed, it will try to read the cache here. If the cache is not initialized, it will fall into an infinite loop.", "* 因此当前缓存内禁止调用 INcfDbData 下的查询。": "* Therefore, calling queries under INcfDbData is prohibited in the current cache.", "* 2、避免使用 INcfDbData,或者避免使用 BaseCache(因为 INcfDbData 默认注入对象为 NCF 模板中的 SenparcEntities,继承自 SenparcEntitiesBase)": "* 2. Avoid using INcfDbData, or avoid using BaseCache (because the default injection object of INcfDbData is SenparcEntities in the NCF template, inherited from SenparcEntitiesBase)", "TODO:临时增加的租户,也需要加入到缓存中": "TODO: Temporarily added tenants also need to be added to the cache.", "/ 多租户信息缓存": "/Multi-tenant information cache", "/ 获取当前启用状态的 TenantInfo": "/ Get the currently enabled TenantInfo", "TODO:此处有线程安全问题,TenantInfos不具备多租户属性可以直接查询": "TODO: There is a thread safety issue here. TenantInfos does not have multi-tenant properties and can be queried directly.", "当前可能正在 Middleware 的获取过程中,还没有完成多租户获取": "It may be currently in the process of obtaining Middleware, and multi-tenant acquisition has not yet been completed.", "如果从旧版本升级,表不存在,则需要更新": "If upgrading from an older version, the table does not exist and needs to be updated", "TODO:_senparcEntitiesMultiTenant 当前没有做 Migration,所有实际上无法进行更新": "TODO:_senparcEntitiesMultiTenant is currently not doing Migration, so it cannot actually be updated.", "var HttpContextAccessor = _serviceProvider.GetRequiredService();//注意:这里不能使用 TenantInfoService.SetScopedRequestTenantInfoAsync(), 否则会引发死循环": "var HttpContextAccessor = _serviceProvider.GetRequiredService();//Note: TenantInfoService.SetScopedRequestTenantInfoAsync() cannot be used here, otherwise it will cause an infinite loop", "全部大写": "all caps", "/ 2、运行:PM> add-migration [更新名称] -c TenantSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ": "/ 2. Run: PM> add-migration [update name] -c TenantSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ", "/ 2、运行:PM> add-migration [更新名称] -c TenantSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ": "/ 2. Run: PM> add-migration [update name] -c TenantSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ", "/ 2、运行:PM> add-migration [更新名称] -c TenantSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ": "/ 2. Run: PM> add-migration [update name] -c TenantSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ", "/ 2、运行:PM> add-migration [更新名称] -c TenantSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ": "/ 2. Run: PM> add-migration [update name] -c TenantSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ", "/ 2、运行:PM> add-migration [更新名称] -c TenantSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ": "/ 2. Run: PM> add-migration [update name] -c TenantSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ", "/ 2、运行:PM> add-migration [更新名称] -c TenantSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ": "/ 2. Run: PM> add-migration [update name] -c TenantSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ", "说明:": "illustrate:", "* 把 SenparcEntitiesMultiTenantBase 独立出来做一层的意义在于剥离必须需要支持多租户的对象(如系统表),": "* The significance of making SenparcEntitiesMultiTenantBase independent as a layer is to separate objects that must support multi-tenancy (such as system tables).", "* 这样可以单独初始化一个只有 TenantInfo,而没有任何其他系统表的 DbContext。": "* This allows you to separately initialize a DbContext with only TenantInfo and no other system tables.", "* 这么做可以避免一种死循环的情况:": "* This can avoid an infinite loop situation:", "* 1、在缓存第一次录入租户信息(TenantInfos)的过程中,需要初始化所有表,并执行 OnModelCreating(),": "* 1. During the process of caching tenant information (TenantInfos) entered for the first time, all tables need to be initialized and OnModelCreating() executed.", "* 而 OnModelCreating() 在整个系统服务生命周期中只执行一次,而此时马上要确定依赖多租户对象(如系统表)的租户信息,": "* OnModelCreating() is only executed once in the entire system service life cycle. At this time, the tenant information that relies on multi-tenant objects (such as system tables) must be determined immediately.", "* 2、但多租户信息此时尚未生成,如果尝试获取,就进入到 1 的循环中。": "* 2. However, the multi-tenant information has not been generated yet. If you try to obtain it, you will enter the loop of 1.", "/ 多租户 EF Core DbContext": "/Multi-tenant EF Core DbContext", "/ 多租户信息": "/Multi-tenant information", "/ 租户信息": "/tenant information", "TODO:可以直接继承不使用多租户的基类": "TODO: You can directly inherit the base class without using multi-tenancy", "/ 全局唯一编号(自动分配)": "/ Globally unique number (automatically assigned)", "/ 匹配域名、URL、Head的参数": "/ Match parameters of domain name, URL, and Head", "/ 此属性已经取消和数据库的映射": "/ This attribute has been unmapped from the database", "/ 更新数据": "/ update data", "/ 清除缓存": "/ clear cache", "/ 设置当前 Request 范围内的 RequestTenantInfo 值": "/ Set the RequestTenantInfo value within the current Request scope", "/ 如果为 null,则自动从 IHttpContextAccessor 中获取": "/ If null, automatically obtained from IHttpContextAccessor", "Console.WriteLine(\"\\t\\t进入 SetScopedRequestTenantInfoAsync -> TenantRule.DomainName\");": "Console.WriteLine(\"\\t\\tEnter SetScopedRequestTenantInfoAsync -> TenantRule.DomainName\");", "检查是否存在 TenantKey": "Check if TenantKey exists", "读取 TenantKey 的值": "Read the value of TenantKey", "Console.WriteLine(\"\\t\\t准备进入 _serviceProvider.GetRequiredService()\");": "Console.WriteLine(\"\\t\\tReady to enter _serviceProvider.GetRequiredService()\");", "数据库读取失败,且未登录任何租户": "Database reading failed and no tenant was logged in", "数据库错误,通常为系统未安装": "Database error, usually the system is not installed", "Console.WriteLine($\"\\t\\t已获取 tenantInfoCollection:{tenantInfoCollection.ToJson()}\");": "Console.WriteLine($\"\\t\\tObtained tenantInfoCollection: {tenantInfoCollection.ToJson()}\");", "Console.WriteLine($\"\\t\\t 匹配到 tenantKey:{tenantKey}\");": "Console.WriteLine($\"\\t\\t matches tenantKey: {tenantKey}\");", "Console.WriteLine($\"\\t\\t 未匹配到 tenantKey:{tenantKey}\");": "Console.WriteLine($\"\\t\\t tenantKey not matched: {tenantKey}\");", "/ 创建租户信息": "/Create tenant information", "/ 如果 Name 或 TenantKey 已存在,则抛出异常": "/ Throws exception if Name or TenantKey already exists", "/ Name 或 TenantKey 已存在": "/ Name or TenantKey already exists", "所有涉及到租户信息的修改,都清除租户信息,重新更新": "For all modifications involving tenant information, clear the tenant information and update again.", "/ 创建默认 TenantInfo 信息": "/ Create default TenantInfo information", "/ 价差 Name 是否存在": "/ Does the spread Name exist?", "/ 价差 TenantKey 是否存在": "/ Does the spread TenantKey exist?", "/ 获取 RequestTenantInfo": "/ Get RequestTenantInfo", "/ 多租户中间件": "/Multi-tenant middleware", "判断是否需要使用数据库": "Determine whether you need to use a database", "没有数据库,跳过": "No database, skip", "判断是否启用了租户": "Determine whether the tenant is enabled", "设置当前 Request 的 RequestTenantInfo 参数": "Set the RequestTenantInfo parameter of the current Request", "如果数据库出错": "If there is an error in the database", "系统表,": "system tables,", "注册多租户(按需)": "Register for multi-tenancy (on demand)", "注册多租户数据库的对象(按需)": "Register objects for multi-tenant database (on demand)", "EntitySetKeys.TryLoadSetInfo(typeof(TenantSenparcEntities));//注册多租户数据库的对象(按需)": "EntitySetKeys.TryLoadSetInfo(typeof(TenantSenparcEntities));//Register objects for multi-tenant databases (on demand)", "引入当前系统": "Introduce current system", "如果不启用多租户功能,可以删除此配置": "This configuration can be removed if multi-tenancy is not enabled", "msg = \"已成功合并\";": "msg = \"Merge successfully\";", "//暂时关闭多租户状态": "//Temporarily close the multi-tenant state", "var result = await GenerateCreateScript(serviceProvider);//尝试执行更新": "var result = await GenerateCreateScript(serviceProvider);//Try to perform updates", "/ 添加多租户": "/Add multi-tenancy", "这个配置面相基类,不属于任何模块": "This configuration is based on the base class and does not belong to any module.", "/ 获取用户可见菜单": "/ Get the user-visible menu", "/ 获取所有菜单(仅包含菜单 和 页面)": "/ Get all menus (only menus and pages)", "/ 获取用户可见的菜单": "/ Get the menu visible to the user", "获取可见的菜单": "Get the visible menu", "获取用户的角色信息": "Get user role information", "获取有效的角色信息": "Get valid role information", "获取用户的角色Id": "Get the user's role ID", "/ 兼容 Cookie 和 JWT 认证的权限认证属性": "/ Authorization authentication attributes compatible with Cookie and JWT authentication", "/ 支持两种认证方式:NcfAdminAuthorizeScheme (Cookie) 或 Bearer_Backend (JWT)": "/ Supports two authentication methods: NcfAdminAuthorizeScheme (Cookie) or Bearer_Backend (JWT)", "/ 只要其中一种认证通过即可访问": "/ You can access it as long as one of the authentication passes", "/ JWT 认证方案名称": "/JWT authentication scheme name", "/ 构造函数,配置支持两种认证方案": "/ Constructor, configure to support two authentication schemes", "用逗号分隔多个认证方案,只要其中一个通过即可": "Separate multiple authentication schemes with commas, as long as one of them passes", "NcfAdminAuthorizeScheme (Cookie 认证) + Bearer_Backend (JWT 认证)": "NcfAdminAuthorizeScheme (Cookie authentication) + Bearer_Backend (JWT authentication)", "/ 构造函数,配置支持两种认证方案并指定 Policy": "/ Constructor, configure to support two authentication schemes and specify Policy", "/ 授权策略": "/ Authorization policy", "左侧会话历史": "Session history on the left", "右侧对话窗口": "Dialog window on the right", "/ 与 SenparcTrace/Index 等页一致: 跳过菜单 URL 校验;": "/ Consistent with pages such as SenparcTrace/Index: Skip menu URL verification;", "/ 登录与 AdminOnly 由 与 Cookie 中间件统一处理,不在此页写 Login 跳转。": "/ Login and AdminOnly are handled uniformly by and Cookie middleware, and no Login jump is written on this page.", "/ 会话ID(URL参数)": "/Session ID (URL parameter)", "/ 初始消息(URL参数,可选)": "/ Initial message (URL parameters, optional)", "/ 当前用户ID": "/Current user ID", "/ 模块 UID 列表(逗号分隔的字符串)": "/ List of module UIDs (comma separated string)", "分配角色": "Assign roles", "第二行": "second line", "右侧图表区域": "Right chart area", "AI 对话入口": "AI dialogue entrance", "功能模块": "Function module", "[Required(ErrorMessage = \"请输入用户名\")]": "[Required(ErrorMessage = \"Please enter username\")]", "[Required(ErrorMessage = \"请输入密码\")]": "[Required(ErrorMessage = \"Please enter password\")]", "绑定参数": "Bind parameters", "是否已经登录": "Have you logged in?", "var logined = await base.CheckLoginedAsync(AdminAuthorizeAttribute.AuthenticationScheme);//判断登录": "var logined = await base.CheckLoginedAsync(AdminAuthorizeAttribute.AuthenticationScheme);//Determine login", "//errorMsg = \"账号或密码错误!错误代码:101。\";": "//errorMsg = \"Wrong account or password! Error code: 101.\";", "ModelState.AddModelError(nameof(this.Password), \"账号或密码错误!错误代码:101。\");": "ModelState.AddModelError(nameof(this.Password), \"Wrong account or password! Error code: 101.\");", "//errorMsg = \"账号或密码错误!错误代码:102。\";": "//errorMsg = \"Wrong account or password! Error code: 102.\";", "ModelState.AddModelError(nameof(this.Password), \"账号或密码错误!错误代码:102。\");": "ModelState.AddModelError(nameof(this.Password), \"Wrong account or password! Error code: 102.\");", "移除不必要的验证,因为ValidateTenant总是返回true": "Remove unnecessary validation because ValidateTenant always returns true", "租户名称不是必填项,无需验证": "Tenant name is not required and does not require verification", "ModelState.AddModelError(nameof(this.Password), \"账号或密码错误!\");": "ModelState.AddModelError(nameof(this.Password), \"Wrong account or password!\");", "TODO 需要把 userInfo 获取过程封装到下面的方法中,统一处理账号锁定": "TODO needs to encapsulate the userInfo acquisition process into the following method to handle account locking in a unified manner", "其他异常,不返回错误信息": "Other exceptions, no error message is returned", "租户名称不是必填的,即使在多租户模式下也是可选的": "Tenant name is not required and is optional even in multi-tenant mode", "直接返回true表示验证总是通过": "Directly returning true means that the verification always passes", "选择图标": "Select icon", "授权": "Authorize", "菜单层级": "menu hierarchy", "Functions列表": "Functions list", "引入样式": "Introduce styles", "菜单栏": "menu bar", "页面js": "Page js", "初始化结果": "Initialization result", "/ 新增、编辑租户": "/ Add and edit tenants", "/ 初始化租户": "/Initialize tenant", "设置租户信息": "Set tenant information", "TODO:从其他模块获得,或独立到对应模块的API": "TODO: Obtained from other modules, or independent to the API of the corresponding module", "/ 数据库已存的XncfModules": "/ XncfModules stored in the database", "更新菜单缓存": "Update menu cache", "/ 扫描新模块": "/Scan for new modules", "始终到详情页": "Always go to the details page", "/ 隐藏“模块管理”功能": "/ Hide \"Module Management\" function", "TODO:使用DTO操作": "TODO: use DTO operation", "/ 隐藏“模块管理”功能 handler=HideManagerAjax": "/ Hide the \"Module Management\" function handler=HideManagerAjax", "/ 获取已安装模块 handler=Modules": "/ Get installed modules handler=Modules", "/ 获取未安装模块 handler=UnModules": "/ Get uninstalled modules handler=UnModules", "所有已安装的模块": "All installed modules", "未安装或版本已更新(不同)的模块": "Modules that are not installed or have updated (different) versions", "/ 获取待更新模块 handler=UpdatedModules": "/ Get the module to be updated handler=UpdatedModules", "/ 扫描新模块 handler=ScanAjax": "/Scan new module handler=ScanAjax", "/ 根据名称安装模块": "/Install module by name", "查找并安装模块": "Find and install modules", "开启模块": "Open module", "/ 是否必须更新(常规读取失败)": "/ Whether it must be updated (regular read fails)", "throw new Exception(\"模块编号未提供!\");": "throw new Exception(\"Module number not provided!\");", "throw new Exception(\"模块未添加!\");": "throw new Exception(\"Module not added!\");", "throw new Exception($\"模块丢失或未加载({Senparc.Ncf.XncfBase.Register.RegisterList.Count})!\");": "throw new Exception($\"Module is missing or not loaded ({Senparc.Ncf.XncfBase.Register.RegisterList.Count})!\");", "var function = _serviceProvider.GetService(functionType) as FunctionBase;//如:Senparc.Xncf.ChangeNamespace.Functions.ChangeNamespace": "var function = _serviceProvider.GetService(functionType) as FunctionBase;//For example: Senparc.Xncf.ChangeNamespace.Functions.ChangeNamespace", "SenparcTrace.SendCustomLog(\"模块读取失败\", @$\"模块:{XncfModule.Name} / {XncfModule.MenuName} / {XncfModule.Uid}": "SenparcTrace.SendCustomLog(\"Module reading failed\", @$\"Module: {XncfModule.Name} / {XncfModule.MenuName} / {XncfModule.Uid}", "请尝试更新此模块后刷新页面!\");": "Please try refreshing the page after updating this module! \");", "/ 更新状态": "/update status", "/ 提交信息,执行方法": "/ Submit information, execute method", "不处理": "Not processed", "方案一:": "Option one:", "参考:https://stackoverflow.com/questions/48033760/cast-taskt-to-taskobject-in-c-sharp-without-having-t/48033780": "Reference: https://stackoverflow.com/questions/48033760/cast-taskt-to-taskobject-in-c-sharp-without-having-t/48033780", "方案二:": "Option two:", "方案效率对比见:https://www.cnblogs.com/szw/p/dynamic-vs-reflect.html": "For a comparison of solution efficiency, see: https://www.cnblogs.com/szw/p/dynamic-vs-reflect.html", "已经在 AppService 中记录": "Already recorded in AppService", "//记录日志缓存": "//Record log cache", "await cache.SetAsync(tempId, result.Data.ToJson(), TimeSpan.FromMinutes(5));//TODO:可设置": "await cache.SetAsync(tempId, result.Data.ToJson(), TimeSpan.FromMinutes(5));//TODO: can be set", "/ 获取日志": "/ Get log", "/ 删除模块": "/ delete module", "删除菜单": "delete menu", "删除权限数据": "Delete permission data", "尝试从已加载的模块中执行删除过程": "Try to perform the removal process from the loaded module", "直接删除,如dll已经不存在,可能引发此问题,只能在当前系统内直接执行删除": "Direct deletion, if the dll no longer exists, may cause this problem, the deletion can only be performed directly in the current system.", "遍历某个 Register 下所有的方法 TODO:未来可添加分组": "Traverse all methods under a Register TODO: Grouping can be added in the future", "TODO:页面上需要给提示": "TODO: Prompts need to be given on the page", "/ 权限认证": "/Permission authentication", "暂时取消权限验证": "Temporarily cancel permission verification", "全局模型验证": "Global model validation", "/ AdminChatMessage:管理后台聊天消息": "/AdminChatMessage: Manage background chat messages", "/ 所属会话ID(外键到 AdminChatSession)": "/ Owning session ID (foreign key to AdminChatSession)", "/ 消息角色类型(User/Assistant/System)": "/ Message role type (User/Assistant/System)", "/ 消息内容(支持 Markdown 格式)": "/ Message content (supports Markdown format)", "/ 消息序号(用于保持对话顺序)": "/ Message sequence number (used to maintain conversation order)", "/ 用户反馈(Like/Dislike/None)": "/ User feedback (Like/Dislike/None)", "/ 使用的模型标识符(例如:\"gpt-4\", \"claude-3\")": "/ The model identifier to use (eg: \"gpt-4\", \"claude-3\")", "/ 导航属性:关联的会话": "/ Navigation properties: associated sessions", "/ 私有构造函数(供 EF Core 使用)": "/ Private constructor (for use by EF Core)", "/ 创建新的聊天消息": "/Create new chat message", "/ 会话ID": "/ Session ID", "/ 角色类型": "/ Role type", "/ 消息内容": "/ Message content", "/ 消息序号": "/ Message sequence number", "/ 模型标识符(可选)": "/ Model identifier (optional)", "/ 设置用户反馈": "/Set user feedback", "/ 更新消息内容(通常用于流式输出场景的追加)": "/ Update message content (usually used for appending in streaming output scenarios)", "/ 聊天消息角色类型": "/Chat message role type", "/ AI 助手消息": "/ AI Assistant Message", "/ 消息反馈类型": "/Message feedback type", "/ 无反馈": "/ no feedback", "/ 点赞": "/ Like", "/ 点踩": "/ click to dislike", "/ AdminChatSession:管理后台聊天会话": "/AdminChatSession: Manage background chat sessions", "/ 会话标题(从首条消息自动提取,最多150字符)": "/ Conversation title (automatically extracted from the first message, up to 150 characters)", "/ 用户ID(外键到 AdminUserInfo)": "/UserID (foreign key to AdminUserInfo)", "/ 会话状态": "/ session state", "/ 最后一条消息时间": "/Last message time", "/ 创建新的聊天会话": "/Create new chat session", "/ 会话标题": "/ Session title", "/ 用户ID": "/ User ID", "/ 更新会话标题": "/update session title", "/ 更新最后消息时间": "/ Update last message time", "/ 归档会话": "/archive session", "/ 删除会话(软删除,修改状态)": "/ Delete session (soft delete, modify status)", "/ 恢复会话": "/restore session", "/ 聊天会话状态": "/Chat session status", "/ 活跃中": "/active", "/ 已归档": "/archived", "/ 已删除": "/ deleted", "/ AdminChatSessionModule:管理后台聊天会话-模块关联": "/AdminChatSessionModule: Administers background chat sessions - module association", "/ 会话ID(外键到 AdminChatSession)": "/Session ID (foreign key to AdminChatSession)", "/ XNCF 模块唯一标识符": "/XNCF module unique identifier", "/ 模块名称(冗余存储,便于快速查询)": "/Module name (redundant storage for quick query)", "/ 模块版本(冗余存储)": "/Module version (redundant storage)", "/ 添加到会话的时间": "/ time added to session", "/ 创建新的会话-模块关联": "/Create new session-module association", "/ XNCF 模块 UID": "/ XNCF module UID", "/ 模块名称": "/ Module name", "/ 模块版本": "/ Module version", "/ 管理后台聊天会话": "/Manage background chat sessions", "/ 管理后台聊天消息": "/Manage background chat messages", "/ 管理后台聊天会话-模块关联": "/Manage background chat session-module association", "/ 获取加盐后的 MD5 密码": "/ Get the salted MD5 password", "/ MD5 作为登陆凭证已经缺少安全性,请使用 GetSHA512Password() 方法": "/MD5 is no longer secure as a login credential, please use the GetSHA512Password() method", "原始MD5": "Original MD5", "再加密": "Re-encrypt", "/// 用户名为空时会按照SenparcCoreAdmin+两位数字的格式随机生成": "/// When the user name is empty, it will be randomly generated in the format of SenparcCoreAdmin+two digits", "/// 真实姓名": "/// Real name", "/// 电话号码": "/// Phone number", "/// 备注": "/// Note", "/ 创建管理员账号": "/ Create an administrator account", "/ 用户名为空时会按照SenparcCoreAdmin+两位数字的格式随机生成": "/ If the username is empty, it will be randomly generated in the format of SenparcCoreAdmin+two digits", "/ 密码为空时会生成16字符长度的随机字符串作为密码": "/ When the password is empty, a random string of 16 characters in length will be generated as the password", "记录用户名": "Record username", "记录明文密码": "Record clear text password", "生成密码盐": "Generate password salt", "生成密码": "Generate password", "TODO:用户名及密码合规性验证": "TODO: Username and password compliance verification", "/ 检查密码是否正确": "/ Check if the password is correct", "/ 生成用户名": "/ Generate username", "/ 生成随机密码": "/ Generate random password", "/ 生成密码盐": "/ Generate password salt", "/ 验证码": "/ Verification code", "/ 角色列表": "/ role list", "/ 操作代码": "/Operation code", "/ AdminChatMessageDto:管理后台聊天消息数据传输对象": "/AdminChatMessageDto: Management background chat message data transfer object", "/ 所属会话ID": "/ belongs to the session ID", "/ 消息角色类型": "/ message role type", "/ 消息序号": "/ message sequence number", "/ 用户反馈": "/ User feedback", "/ 使用的模型标识符": "/ model identifier to use", "/ 从实体映射到 DTO": "/ Mapping from entities to DTOs", "明确复制基类属性": "Explicitly copy base class properties", "复制业务属性": "Copy business attributes", "/ 发送聊天消息的请求 DTO": "/ Request to send chat message DTO", "/ 会话ID": "/ session id", "/ AdminChatSessionDto:管理后台聊天会话数据传输对象": "/AdminChatSessionDto: Admin background chat session data transfer object", "/ 会话标题": "/ session title", "/ 用户ID": "/userID", "/ 关联的消息列表(可选,用于嵌套查询)": "/ Associated message list (optional, for nested queries)", "/ 关联的模块列表(可选,用于嵌套查询)": "/ List of associated modules (optional, for nested queries)", "/ 创建聊天会话的请求 DTO": "/ Request DTO to create a chat session", "/ 初始消息内容": "/Initial message content", "/ 关联的模块 UID 列表": "/ List of associated module UIDs", "/ AdminChatSessionModuleDto:管理后台聊天会话-模块关联数据传输对象": "/AdminChatSessionModuleDto: Administers background chat session - module associated data transfer object", "/ 模块版本": "/module version", "/ 用于前端显示的模块名称(优先菜单名)": "/ Module name used for front-end display (priority menu name)", "/ 模块简要说明": "/Module brief description", "/ XNCF 模块显示 DTO": "/XNCF module display DTO", "/ 是否有新版本": "/ Is there a new version?", "/ 新版本号": "/ new version number", "/ 模块Functions": "/ModuleFunctions", "/ 4、运行:PM> add-migration [更新名称] -Context AdminSenparcEntities_Dm -o SystemEntities/Migrations/Migrations.Dm.SystemEntities": "/ 4. Run: PM> add-migration [update name] -Context AdminSenparcEntities_Dm -o SystemEntities/Migrations/Migrations.Dm.SystemEntities", "/ 4、运行:PM> add-migration [更新名称] -Context AdminSenparcEntities_MySql -o SystemEntities/Migrations/Migrations.MySql.SystemEntities": "/ 4. Run: PM> add-migration [update name] -Context AdminSenparcEntities_MySql -o SystemEntities/Migrations/Migrations.MySql.SystemEntities", "/ 4、运行:PM> add-migration [更新名称] -Context AdminSenparcEntities_Oracle -o SystemEntities/Migrations/Migrations.Oracle.SystemEntities": "/ 4. Run: PM> add-migration [update name] -Context AdminSenparcEntities_Oracle -o SystemEntities/Migrations/Migrations.Oracle.SystemEntities", "/ 4、运行:PM> add-migration [更新名称] -Context AdminSenparcEntities_PostgreSQL -o SystemEntities/Migrations/Migrations.PostgreSQL.SystemEntities": "/ 4. Run: PM> add-migration [update name] -Context AdminSenparcEntities_PostgreSQL -o SystemEntities/Migrations/Migrations.PostgreSQL.SystemEntities", "/ 4、运行:PM> add-migration [更新名称] -Context AdminSenparcEntities_SqlServer -o SystemEntities/Migrations/Migrations.SqlServer.SystemEntities": "/ 4. Run: PM> add-migration [update name] -Context AdminSenparcEntities_SqlServer -o SystemEntities/Migrations/Migrations.SqlServer.SystemEntities", "/ 4、运行:PM> add-migration [更新名称] -Context AdminSenparcEntities_SQLite -o SystemEntities/Migrations/Migrations.SQLite.SystemEntities": "/ 4. Run: PM> add-migration [update name] -Context AdminSenparcEntities_SQLite -o SystemEntities/Migrations/Migrations.SQLite.SystemEntities", "/ 当前项目供前端(非Areas)使用的PageModel全局基类": "/ The current project's PageModel global base class for front-end (non-Areas) use", "/ 全局所有的Error_ExceptionVD必须实现这个接口": "/ All Error_ExceptionVD globally must implement this interface", "/// 给Error_ExceptionVD用": "/// For Error_ExceptionVD", "/ 给Error_Error404VD用": "/ for Error_Error404VD", "/ 给Error_ExceptionVD用": "/ For Error_ExceptionVD", "/ 获取所有日期列表": "/ Get a list of all dates", "/ 获取指定日期的日志": "/ Get logs for a specified date", "备份文件名": "Backup file name", "读取备份文件,以免资源占用": "Read backup files to avoid resource usage", "一个片段的开始(异常)": "Beginning of a fragment (Exception)", "记录标题": "record title", "其他自定义类型": "Other custom types", "一个片段的开始": "start of a segment", "线程": "thread", "时间": "time", "一直读到底": "Read to the end", "POST请求": "POST request", "GET请求": "GET request", "异常信息处理": "Exception information processing", "“errcode:”保留": "\"errcode:\" reserved", "删除备份文件": "Delete backup files", "翻转序列": "flip sequence", "/ 标题": "/title", "/ 时间": "/ time", "/ 线程": "/thread", "/ 在日志中的行数": "/Number of lines in the log", "/ 日志类型": "/ log type", "/ 是否是一条异常日志": "/ Is it an exception log?", "/ 结果": "/ result", "/ 返回结果,通常为JSON": "/ Returns the result, usually JSON", "/ 消息类型": "/ message type", "/ ModuleAssistantPlugin:针对会话关联模块的 AI Function Calling 插件。": "/ModuleAssistantPlugin: AI Function Calling plugin for session association modules.", "/ 当用户提问涉及模块信息、数据库结构、已安装功能等时,AI 将自动调用对应函数。": "/ When the user asks a question involving module information, database structure, installed functions, etc., AI will automatically call the corresponding function. ", "/ 初始化模块助手插件。": "/ Initialize module helper plugin.", "/ 当前会话关联模块列表。": "/ List of modules associated with the current session. ", "/ 列出当前会话关联的所有 XNCF 模块": "/ List all XNCF modules associated with the current session", "/ 获取指定模块的详细信息(含 Functions 列表)": "/ Get detailed information of the specified module (including Functions list)", "获取 FunctionRender 注册的功能列表": "Get the list of functions registered by FunctionRender", "/ 获取指定模块的数据库结构信息": "/ Get the database structure information of the specified module", "列出实体的公开属性(简要字段清单)": "List the public properties of an entity (brief field list)", "/ 列出系统中所有已注册模块": "/ List all registered modules in the system", "/ 根据 UID 或名称关键字从会话模块中查找": "/ Find from session module based on UID or name keyword", "/ AdminChatAiService:管理后台聊天 AI 调用服务(直接使用 appsettings 的 SenparcAiSetting)": "/ AdminChatAiService: Manage background chat AI call service (use SenparcAiSetting of appsettings directly)", "/ 初始化管理后台聊天 AI 服务。": "/ Initialize the management background chat AI service.", "/ 聊天消息服务。": "/ Chat message service. ", "/ 会话模块服务。": "/ Session module service. ", "/ 服务提供器。": "/ Service provider. ", "/ 日志记录器。": "/ Logger. ", "/ 生成 AI 回复内容并返回所使用的模型标识。": "/ Generates the AI ​​reply content and returns the model id used.", "/ 会话 Id。": "/ Session Id. ", "/ 用户 Id。": "/ User Id. ", "/ 用户输入消息。": "/ User input message. ", "/ 返回回复文本与模型标识。": "/ Returns the reply text and model ID. ", "注册模块信息 Function Calling 插件": "Register module information Function Calling plug-in", "自动加载会话关联模块中的 FunctionRender([#sym:FunctionRender])插件对象": "Automatically load the FunctionRender ([#sym:FunctionRender]) plug-in object in the session association module", "使用 FunctionChoiceBehavior.Auto() 让 AI 根据需要自动调用 ModuleAssistantPlugin 函数": "Use FunctionChoiceBehavior.Auto() to let AI automatically call the ModuleAssistantPlugin function as needed", "/ AdminChatMessageService:管理后台聊天消息服务": "/AdminChatMessageService: Manage background chat message service", "/ 获取会话的所有消息(按序号正序)": "/ Get all messages of the session (in positive order by serial number)", "/ 页码(从1开始,0表示获取全部)": "/ Page number (starting from 1, 0 means getting all)", "/ 每页数量": "/ Number per page", "/ 获取下一个序号(用于新消息)": "/ Get the next sequence number (for new messages)", "/ 添加新消息": "/Add new message", "/ 设置消息反馈": "/Set message feedback", "/ 更新消息内容(用于流式输出场景)": "/ Update message content (for streaming output scenarios)", "/ 删除会话的所有消息(物理删除,慎用)": "/ Delete all messages in the session (physical deletion, use with caution)", "/ 按消息ID批量删除会话消息(物理删除)": "/ Batch delete session messages by message ID (physical deletion)", "/ 获取会话的最后一条消息": "/ Get the last message of the session", "/ 获取会话的消息数量": "/ Get the number of messages in the session", "/ AdminChatSessionModuleService:管理后台聊天会话-模块关联服务": "/AdminChatSessionModuleService: Manage background chat session-module associated service", "/ 获取会话关联的所有模块": "/ Get all modules associated with the session", "/ 添加模块到会话": "/Add module to session", "检查是否已存在(防止重复添加)": "Check if it already exists (prevent duplicate addition)", "/ 批量添加模块到会话": "/ Batch add modules to session", "/ 从会话中移除模块": "/Remove module from session", "/ 清空会话的所有模块关联": "/Clear all module associations of the session", "/ 检查模块是否已关联到会话": "/ Check if the module is associated to the session", "/ 获取会话的模块数量": "/ Get the number of modules in the session", "/ 获取模块被使用的会话数量(统计用)": "/ Get the number of sessions used by the module (for statistics)", "/ AdminChatSessionService:管理后台聊天会话服务": "/AdminChatSessionService: Manage background chat session service", "/ 获取用户的活跃会话列表(按最后消息时间倒序)": "/ Get the user's active conversation list (in descending order of last message time)", "/ 页码(从1开始)": "/ Page number (starting from 1)", "/ 根据 ID 获取会话(包含验证用户权限)": "/ Get the session based on ID (including verifying user permissions)", "/ 更新会话的最后消息时间": "/ Update the last message time of the session", "/ 删除会话(软删除)": "/ Delete session (soft delete)", "/ 恢复已删除的会话": "/Restore deleted session", "/ 获取会话总数(按状态)": "/ Get the total number of sessions (by status)", "//TODO:放到 OHS 的 Service": "//TODO: put in the Service of OHS", "退出登录": "Log out", "/ 如果密码正确,则尝试登录": "/ If the password is correct, try to log in", "先检查是否在锁定期,避免不必要的数据库查询": "First check whether it is in the lock period to avoid unnecessary database queries.", "登录成功,清除失败记录": "Login successful, clear failure records", "记录失败次数,即使锁定期已过,也要累加之前的失败次数": "Record the number of failures. Even if the lockout period has expired, the number of previous failures will be accumulated.", "设置锁定时间": "Set lock time", "更新缓存,失败次数在24小时内有效": "Update cache, the number of failures is valid within 24 hours", "/ 创建管理员": "/Create administrator", "/ 更新用户信息": "/Update user information", "/ 初始化,随机生成密码": "/ Initialization, randomly generate password", "/ 用户名": "/ Username", "/ 密码": "/ Password", "/ 登录": "/ Log in", "/ 登录dto": "/ Login dto", "直接向上层抛出登录锁定异常": "Throw login lock exception directly to the upper layer", "/ 获取当前管理信息": "/ Get current management information", "开始安装权限模块": "Start installing permission module", "(必须放在第一个,其他模块操作都需要依赖此模块)": "(Must be placed first, other module operations need to rely on this module)", "开始安装菜单管理模块": "Start installing the menu management module", "(必须放在第二个,其他模块操作都需要依赖此模块)": "(Must be placed second, other module operations need to rely on this module)", "安装权限、菜单等默认模块和配置": "Install default modules and configurations such as permissions and menus", ".Init 内部还会执行一次": ".Init will be executed once internally", "开始安装模块理管理模块": "Start installing the module management module", "(必须放在第三个,其他模块操作都需要依赖此模块)": "(Must be placed in the third position, other module operations need to rely on this module)", "开始安装系统基础模块": "Start installing system basic modules", "开始安装系统管理管理模块": "Start installing the system management management module", "开始安装 Areas 模块": "Start installing the Areas module", "//开始安装 Installer 模块": "//Start installing the Installer module", "安装租户模块": "Install tenant module", "将权限模块进行安装": "Install the permission module", "将菜单模块进行安装": "Install the menu module", "TODO:选择性安装用户自定义模块": "TODO: Selectively install user-defined modules", "//开启模块": "//Open module", "if (docModule.State != Ncf.Core.Enums.XncfModules_State.开放)": "if (docModule.State != Ncf.Core.Enums.XncfModules_State.open)", "docModule.UpdateState(Ncf.Core.Enums.XncfModules_State.开放);": "docModule.UpdateState(Ncf.Core.Enums.XncfModules_State.Open);", "开始安装并启用系统模块(Admin)": "Start installing and enabling system modules (Admin)", "确保已被赋值": "Make sure it has been assigned", "一次性保存(所有)修改": "Save (all) changes at once", "初始化系统信息": "Initialize system information", "开始安装用户模块(如有) TODO:选择性安装": "Start installing user modules (if any) TODO: Selective installation", "/ 安装并且开放模块": "/Install and open the module", "/ 是否立即安装": "/ Whether to install now", "/ 是否添加菜单(必须 installNow 为 true 时生效)": "/ Whether to add a menu (must be installedNow is true to take effect)", "安装模块": "Install module", "启用模块": "enable module", "//TODO:判断是否已存在": "//TODO: Determine whether it already exists", "/ 递归获取树形结构": "/ Recursively obtain the tree structure", "/ 当前筛选列表": "/ Current filter list", "/ 完整列表数据": "/ Full list data", "查找子菜单": "Find submenu", "/ 获取所有菜单(不包含页面)": "/ Get all menus (excluding pages)", "/ TODO... 缓存": "/TODO... cache", "/ 获取完整菜单信息": "/ Get complete menu information", "/ 是否包含按钮信息": "/ Whether to include button information", "/ 前台jwt": "/ front desk jwt", "/ 管理后台jwt": "/ Management backend jwt", "/ token是谁颁发的": "/ Who issued the token?", "/ token可以给哪些客户端使用": "Which clients / token can be used by?", "/ 加密的key": "/ encrypted key", "/ 过期时间,单位【小时】": "/ Expiration time, unit [hour]", "绘制图片,并返回": "Draw the picture and return", "开始保存图片": "Start saving pictures", "保存微信图片素材": "Save WeChat picture material", "推送图片": "Push pictures", "/ AdminChatAppService:管理后台聊天功能 API 服务": "/AdminChatAppService: Manage background chat function API service", "/ 支持 Cookie 和 JWT 两种认证方式": "/Supports two authentication methods: Cookie and JWT", "/ 获取用户的会话列表": "/ Get the user's session list", "/ 获取会话详情(包含消息和模块)": "/ Get session details (including messages and modules)", "/ 删除会话": "/delete session", "/ 发送消息": "/send message", "/ 获取会话的消息列表": "/ Get the message list of the session", "/ 批量删除消息": "/Delete messages in batches", "/ 获取会话的模块列表": "/ Get the module list of the session", "/ 创建会话响应": "/Create session response", "/ 获取会话列表响应": "/ Get session list response", "/ 获取会话详情响应": "/ Get session details response", "/ 发送消息响应": "/Send message response", "/ 获取消息列表响应": "/ Get message list response", "/ 添加模块到会话请求": "/ Add module to session request", "/ 模块信息": "/module information", "/ 获取会话模块列表响应": "/ Get session module list response", "/ 管理员创建请求": "/ Administrator creates request", "/ 修改管理员": "/Modify administrator", "/ 管理员登录验证,并进行登录授权": "/ Administrator login verification and login authorization", "/ 获取当前管理员信息": "/ Get current administrator information", "/ 增加角色": "/Add role", "/ 获取当前用户的所有角色": "/ Get all roles of the current user", "/ 删除管理员": "/remove administrator", "/ 管理员 ID": "/ Administrator ID", "TODO:进行更多层级判断": "TODO: Make more levels of judgment", "/ 基类": "/ base class", "/ 获取当前用的Id": "/ Get the current ID", "/ 获取模块统计状态": "/ Get module statistics status", "所有已安装模块": "All installed modules", "未安装或待升级模块": "Modules not installed or to be upgraded", "未安装货代升级模块的版本": "The version of the freight forwarding upgrade module is not installed", "需要升级的版本号": "Version number that needs to be upgraded", "全新未安装的版本号": "New, uninstalled version number", "安装后缺失的模块": "Missing modules after installation", "/ 获取已安装模块信息": "/ Get installed module information", "/ 获取未安装或待更新(版本不同)模块信息": "/ Get module information that is not installed or needs to be updated (different versions)", "/ 安装模块": "/Install module", "/ 获取单个模块信息": "/ Get single module information", "/ 更改状态": "/ change status", "/ 模块 ID": "/ Module ID", "/ 切换到状态:关闭-0,开放-1,新增待审核-2,更新待审核-3": "/ Switch to state: closed-0, open-1, new pending review-2, updated pending review-3", "/// 获取日志": "/// Get log", "/// 日志ID": "/// Log ID", "return Content(\"日志文件不存在或已下载!\");": "return Content(\"The log file does not exist or has been downloaded!\");", "/ 提交参数": "/ Submit parameters", "/ 获取当天日志信息": "/ Get the day's log information", "/ 创建菜单": "/Create menu", "/ 获取菜单树 不包含按钮": "/ Get menu tree without buttons", "/ 是否包含按钮": "/ Whether to include buttons", "/ 获取菜单列表": "/ Get menu list", "/ 父级Id": "/ ParentId", "/ 获取菜单详情": "/ Get menu details", "/ 父级Id": "/ Parent Id", "/ 删除菜单": "/delete menu", "/ 创建or修改角色信息": "/Create or modify role information", "/ 获取详情": "/ Get details", "/ 删除角色": "/ delete role", "/ 增加权限": "/Add permissions", "/ 获取角色下面的所有菜单": "/ Get all menus under the character", "/ 获取全部系统信息": "/ Get all system information", "/ 创建or修改系统信息": "/Create or modify system information", "/ 打开或关闭 模块管理": "/ Turn module management on or off", "/ 是否隐藏管理模块": "/ Whether to hide the management module", "清理缓存": "clear cache", "分级缓存测试": "Hierarchical cache testing", "/ 管理员id": "/admin id", "/ 管理员新增": "/Administrator added", "/ 备注1": "/ Note 1", "/ 备注2": "/ Note 2", "/ 获取所有": "/ get all", "/ 已安装模块数量": "/Number of installed modules", "/ 待更新模块数量": "/Number of modules to be updated", "/ 新模块数量": "/Number of new modules", "/ 异常模块数量": "/Number of exception modules", "/ 必须刷新页面,MustUpdate 为 true 时,必定有异常信息": "/ The page must be refreshed. When MustUpdate is true, there must be exception information", "/ XNCF 模块注册信息": "/XNCF module registration information", "/ 主页 URL": "/Homepage URL", "/ 菜单显示名称": "/Menu display name", "/ 版本": "/ Version", "/ 唯一编号": "/ unique number", "/ 子菜单项目列表": "/ List of submenu items", "/ 接口": "/interface", "/ “执行方法”数量统计": "/ \"Execution method\" quantity statistics", "/ 线程信息": "/ thread information", "/ 线程状态详情": "/Thread status details", "/ 线程名称": "/thread name", "/ 线程故事": "/thread story", "/ 菜单创建请求": "/Menu creation request", "/ 菜单创建响应": "/menu creation response", "/ 创建后的主键编号": "/ Primary key number after creation", "/ 菜单树 不包含按钮": "/ Menu tree does not contain buttons", "/ 菜单列表项目": "/menu list item", "/ 菜单详情": "/menu details", "/ 授权操作": "/Authorize operation", "/ 详情": "/details", "/ 角色更新": "/Character update", "/ 角色下面的所有权限": "/ All permissions under the role", "* 特别注意:": "*Special note:", "* 当前注册类是比较特殊的底层系统支持模块,": "* The current registration class is a special underlying system support module.", "* 其中加入了一系列特殊处理的代码,并不适合所有模块使用,": "* A series of special processing codes are added, which are not suitable for all modules.", "* 如果需要学习扩展模块,请参考 【Senparc.ExtensionAreaTemplate】 项目的 Register.cs 文件!": "* If you need to learn extension modules, please refer to the Register.cs file of the [Senparc.ExtensionAreaTemplate] project!", "IXncfRazorRuntimeCompilation //需要使用 RazorRuntimeCompilation,在开发环境下实时更新 Razor Page": "IXncfRazorRuntimeCompilation //Need to use RazorRuntimeCompilation to update the Razor Page in real time in the development environment", "//只在未安装的情况下进行安装,InstallModuleAsync会访问到此方法,不做判断可能会引发死循环。": "//Only install if it is not installed. InstallModuleAsync will access this method. Failure to make a judgment may cause an infinite loop.", "//常规模块中请勿在此方法中自动安装模块!": "//Do not automatically install modules in this method in regular modules!", "TODO:可以提供一个 BeforeUninstall 方法,阻止卸载。": "TODO: You can provide a BeforeUninstall method to prevent uninstallation.", "配置管理后台jwt": "Configure management background jwt", "聊天功能相关服务注册": "Chat function related service registration", "/ 添加前后端认证": "/ Add front-end and back-end authentication", "Admin比较特殊,不需要全部输出": "Admin is special and does not need to output all", "鉴权配置": "Authentication configuration", "添加基于Cookie的权限验证:https://docs.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-2.1&tabs=aspnetcore2x": "Add cookie-based permission verification: https://docs.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-2.1&tabs=aspnetcore2x", "options.Conventions.AuthorizeAreaFolder(\"Admin\", \"/\", \"AdminOnly\");//必须登录": "options.Conventions.AuthorizeAreaFolder(\"Admin\", \"/\", \"AdminOnly\");//Must be logged in", "options.Conventions.AddAreaPageRoute(\"Admin\", \"/Login\", \"/Admin/Login\");//允许匿名": "options.Conventions.AddAreaPageRoute(\"Admin\", \"/Login\", \"/Admin/Login\");//Allow anonymity", "options.Conventions.AddAreaPageRoute(\"Admin\", \"/Index\", \"/Admin/Index\");//允许匿名": "options.Conventions.AddAreaPageRoute(\"Admin\", \"/Index\", \"/Admin/Index\");//Allow anonymity", "必须登录": "Must log in", "聊天页面必须登录": "You must log in to the chat page", "允许匿名": "Allow anonymity", "更多:https://learn.microsoft.com/en-us/aspnet/core/security/authorization/razor-pages-authorization?view=aspnetcore-8.0": "More: https://learn.microsoft.com/en-us/aspnet/core/security/authorization/razor-pages-authorization?view=aspnetcore-8.0", "NcfDatabaseMigrationHelper.SYSTEM_UNIQUE_PREFIX;//系统表,将会留空": "NcfDatabaseMigrationHelper.SYSTEM_UNIQUE_PREFIX;//System table, will be left blank", "//已在 SenparcEntities 中完成": "//Done in SenparcEntities", "#region 历史解决方案参考信息": "#region Historical Solution Reference Information", "/* 参考信息": "/* Reference information", "//预加载 EntitySetKey": "//Preload EntitySetKey", "//AutoMap映射": "//AutoMap mapping", "#region IXncfRazorRuntimeCompilation 接口": "#region IXncfRazorRuntimeCompilation interface", "TODO: Oracle 未升级到 .NET 8.0,此处会抛错": "TODO: Oracle has not been upgraded to .NET 8.0, an error will be thrown here", "树形菜单固定行样式": "Tree menu fixed row style", "隐藏克隆行中的操作按钮": "Hide action buttons in cloned rows", "确保表格可以显示固定元素": "Make sure the table can display fixed elements", "克隆元素样式调整": "Clone element style adjustment", "确保展开图标的样式一致": "Ensure expansion icons are styled consistently", "管理后台 AI 对话页面样式": "Manage background AI dialogue page style", "========== 左侧会话历史区域 ==========": "========== Session history area on the left ==========", "========== 右侧对话窗口区域 ==========": "========== Dialogue window area on the right ==========", "========== 消息列表区域 ==========": "========== Message list area ==========", "AI 思考中动画": "AI thinking animation", "========== 输入区域 ==========": "========== Input area ==========", "========== 响应式布局 ==========": "========== Responsive layout ==========", "========== 滚动条样式 ==========": "========== Scroll bar style ==========", "顶部": "top", "分页": "Pagination", "顶部筛选组": "top filter group", "模块管理": "Module management", "logo样式": "logo style", "保持与首页一致:Ctrl+Enter (Windows/Linux) 或 Cmd+Enter (Mac) 发送。": "Keep same as home page: Ctrl+Enter (Windows/Linux) or Cmd+Enter (Mac) Send.", "普通 Enter 保留换行行为。": "Normal Enter preserves newline behavior.", "乐观渲染:立即显示“我”的消息,避免等待接口返回期间出现空白。": "Optimistic rendering: Display \"I\" messages immediately to avoid blanks while waiting for the interface to return.", "用服务端正式消息替换临时消息,确保时间、ID等数据准确。": "Replace temporary messages with official messages from the server to ensure accuracy of time, ID and other data.", "callback(new Error('请再次输入密码'));": "callback(new Error('Please enter password again'));", "callback(new Error('两次输入密码不一致!'));": "callback(new Error('The password entered twice is inconsistent!'));", "设置角色dialog": "Set role dialog", "确认loading按钮": "Confirm loading button", "密码校验是否通过": "Password verification passed?", "密码需要校验": "Password needs to be verified", "再次校验密码": "Check password again", "获取数据": "Get data", "更新新增编辑": "Update new editor", "需要校验": "Need to verify", "校验不通过": "Verification failed", "更新设置角色": "Update settings role", "设置角色": "Set up roles", "所有角色": "All roles", "已有角色": "Already have a role", "显示在首页ifram": "Show on home page ifram", "基于 Element 的 el - pagination进行了二次封装,并拓展了自动滚动的功能。": "Based on Element's el-pagination, it is re-encapsulated and the automatic scrolling function is expanded.", "监听传进来的当前页和大小,有变化时更新 :page.sync=\"listQuery.page\"和:limit.sync=\"listQuery.limit\"": "Monitor the current page and size passed in, and update when there are changes: page.sync=\"listQuery.page\" and:limit.sync=\"listQuery.limit\"", "新增获取今日日志数据的方法": "Added method to obtain today's log data", "确保图表在获取到数据后更新": "Make sure the chart updates after getting the data", "添加点击事件监听器": "Add click event listener", "准备今日日志数据": "Prepare today’s log data", "自动输出所有类别": "Automatically output all categories", "租户名称不是必填的,直接通过验证": "The tenant name is not required and can be verified directly.", "检查是否启用多租户": "Check if multi-tenancy is enabled", "如果不是多租户模式,清空租户输入": "If not in multi-tenant mode, clear the tenant input", "处理剪切url id": "Handle cut url id", "选取图标": "Select icon", "更新授权": "Update authorization", "获取所有菜单": "Get all menus", "数据加载完成后初始化固定效果": "Initialize fixed effects after data loading is complete", "数据处理": "Data processing", "编辑 // 新增菜单 // 增加下一级": "Edit // Add new menu // Add next level", "初始化父节点固定效果": "Initialize parent node fixed effect", "创建克隆行并保持列宽": "Create cloned rows and keep column widths", "复制每列的宽度": "Copy the width of each column", "设置克隆行的总宽度": "Set the total width of cloned rows", "处理滚动事件": "Handle scroll events", "确保克隆行的列宽与原行保持一致": "Make sure the column widths of the cloned rows are consistent with the original rows", "在窗口大小改变时重新计算列宽": "Recalculate column widths when window size changes", "树结构字段": "tree structure fields", "所有权限": "All permissions", "当前权限": "Current permissions", "默认选中": "Selected by default", "父节点集合": "set of parent nodes", "权限": "Permissions", "打开dialog": "open dialog", "当前已有权限": "Currently has permission", "父节点的集合, 用于求默认和条件为父节点时的差集,解决element tree无半选问题。": "The set of parent nodes is used to find the difference set when the default and condition are parent nodes, and solve the problem of no half-selection in element tree.", "所有权限 格式后的数据(用于渲染tree)": "All permissions formatted data (used for rendering tree)", "初始化获取数据": "Initialize data acquisition", "'': 全部, true: 是, false: 否": "'': all, true: yes, false: no", "用于存储所有可用的线程ID": "Used to store all available thread IDs", "用于存储所有可用的类型": "Used to store all available types", "初始化可用的筛选选项": "Initialize available filtering options", "应用所有筛选条件": "Apply all filters", "异常筛选": "Exception filtering", "关键字筛选": "Keyword filter", "时间区间筛选": "Time interval filter", "线程筛选": "Thread filtering", "类型筛选": "Type filter", "异常状态筛选": "Exception status filtering", "高亮显示关键字": "Highlight keywords", "分页接口传参(只会有一个)": "Paging interface parameter passing (there will only be one)", "提交初始化": "Submit initialization", "显示结果弹窗": "Show result popup", "新模块数据": "new module data", "已安装模块": "Module installed", "待更新模块": "Modules to be updated", "是否切换状态": "Whether to switch status", "获取": "Get", "切换状态": "Switch status", "安装": "Install", "跳转到模块详情": "Jump to module details", "操作": "operate", "主页": "Home page", "数据": "data", "执行弹窗": "Execute pop-up window", "绑定数据": "Bind data", "查看线程": "View thread", "打开首页": "Open home page", "关闭状态返回": "Return to closed state", "打开执行": "open execution", "动态model绑定生成": "Dynamic model binding generation", "默认选择赋值": "Default selection assignment", "多选": "Multiple choice", "下拉框value": "drop-down box value", "如果没有默认给第一个": "If not, default to the first one", "输入框": "Input box", "this.runData数组结构": "this.runData array structure", "在接口传输时,将下拉单选转成数组": "When transmitting through the interface, convert the drop-down radio selection into an array", "// parameterType === 2 多选": "// parameterType === 2 multiple selection", "// parameterType === 1 下拉单选": "// parameterType === 1 drop-down radio selection", "执行": "implement", "物理路径校验": "Physical path verification", "设置 loading 状态": "Set loading state", "下拉框value为字符串,但接口要数组": "The value of the drop-down box is a string, but the interface requires an array.", "打开执行结果弹窗": "Open the execution result pop-up window", "无论成功失败都取消 loading 状态": "Regardless of success or failure, cancel the loading state", "关闭和开启": "off and on", "更新版本": "Updated version", "关闭执行弹窗": "Close the execution pop-up window", "格式化添加时间等 2020 - 06 - 19T09: 41: 51.1905692": "Formatting to add time, etc. 2020-06-19T09: 41: 51.1905692", "* 复制内容": "* Copy content", "* @param {any} value 值": "* @param {any} value value", "* @param {any} toastText 提示内容": "* @param {any} toastText prompt content", "执行浏览器复制命令": "Execute browser copy command", "* 将数值四舍五入(保留2位小数)后格式化成金额形式": "* Round the value (retaining 2 decimal places) and format it into an amount.", "* @param {any} num 数值(Number或者String)": "* @param {any} num value (Number or String)", "* @returns {any} 金额格式的字符串,如'1,234,567.45'": "* @returns {any} String in amount format, such as '1,234,567.45'", "切换菜单栏展开": "Toggle menu bar expansion", "解决刷新菜单状态复原问题": "Solve the problem of refreshing menu status recovery", "点击菜单,高亮": "Click on the menu to highlight", "菜单栏数据": "Menu bar data", "数据存起来": "Save data", "按钮权限存起来 使用:直接在dom上v-has=\" ['admin-add']\"": "Save the button permissions and use: v-has=\" ['admin-add']\" directly on the dom", "菜单栏数据递归": "Menu bar data recursion", "如果有需要单独设置的导航 (例如安装、卸载后激活特定的导航)": "If there is navigation that needs to be set up separately (such as activating specific navigation after installation or uninstallation)", "侧边栏数据": "Sidebar data", "背景色": "background color", "文字色": "text color", "激活颜色": "Activate color", "当前激活菜单的 index": "The index of the currently active menu", "切换菜单栏状态": "Toggle menu bar status", "保存菜单数据": "Save menu data", "注册一个全局自定义指令 `v-has`": "Register a global custom directive `v-has`", "当被绑定的元素插入到 DOM 中时触发bind钩子": "The bind hook is triggered when the bound element is inserted into the DOM", "特殊的构造函数": "special constructor", "判断是否需要自动进入到安装程序": "Determine whether you need to automatically enter the installation program", "以下数据库模块的命名空间根据需要添加或删除": "Namespaces for the following database modules are added or removed as needed", "using Senparc.Ncf.Database.MySql; //使用需要引用包: Senparc.Ncf.Database.MySql": "using Senparc.Ncf.Database.MySql; //Use requires reference package: Senparc.Ncf.Database.MySql", "using Senparc.Ncf.Database.Sqlite; //使用需要引用包: Senparc.Ncf.Database.Sqlite": "using Senparc.Ncf.Database.Sqlite; //Use requires reference package: Senparc.Ncf.Database.Sqlite", "using Senparc.Ncf.Database.PostgreSQL; //使用需要引用包: Senparc.Ncf.Database.PostgreSQL": "using Senparc.Ncf.Database.PostgreSQL; //Use requires reference package: Senparc.Ncf.Database.PostgreSQL", "using Senparc.Ncf.Database.Oracle; //使用需要引用包: Senparc.Ncf.Database.Oracle": "using Senparc.Ncf.Database.Oracle; //Use requires reference package: Senparc.Ncf.Database.Oracle", "using Senparc.Ncf.Database.SqlServer; //使用需要引用包: Senparc.Ncf.Database.SqlServer": "using Senparc.Ncf.Database.SqlServer; //Use requires reference package: Senparc.Ncf.Database.SqlServer", "添加(注册) NCF 服务(必须)": "Add (register) NCF service (required)", "添加 ServiceDefaults": "Add ServiceDefaults", "添加 Dapr": "Add Dapr", "Use NCF(必须)": "Use NCF (required)", "UseNcf() 泛型类型说明": "UseNcf() generic type description", "* 方法 | 说明": "* Method | Description", "* UseNcf() | 由 appsettings.json 决定配置": "* UseNcf() | The configuration is determined by appsettings.json", "* UseNcf() | 使用 SQLServer 数据库": "* UseNcf() | Use SQLServer database", "* UseNcf() | 使用 SQLite 数据库": "* UseNcf() | Use SQLite database", "* UseNcf() | 使用 MySQL 数据库": "* UseNcf() | Use MySQL database", "* UseNcf() | 使用 PostgreSQL 数据库": "* UseNcf() | Use PostgreSQL database", "* UseNcf() | 使用 Oracle 数据库(V12+)": "* UseNcf() | Use Oracle database (V12+)", "* UseNcf() | 使用 Oracle 数据库(V11+)": "* UseNcf() | Use Oracle database (V11+)", "* UseNcf() | 使用 DM(达梦)数据库": "* UseNcf() | Use DM (Dameng) database", "* 更多数据库可扩展,依次类推……": "* More databases can be expanded, and so on...", "非必须": "Not necessary", "显示系统准备成功提示": "Display system preparation success prompt", "/ 全局注册": "/ Global registration", "激活 Xncf 扩展引擎(必须)": "Activate Xncf extension engine (required)", "如果不需要启用 Areas,可以只使用 services.StartEngine() 或 services.StartEngine() 方法": "If you don't need to enable Areas, you can just use the services.StartEngine() or services.StartEngine() method", "注册 EventBus 并自动扫描所有模块的 EventHandler": "Register EventBus and automatically scan all modules for EventHandler", "必须在 StartWebEngine 之后,确保所有模块程序集已加载": "Must be after StartWebEngine to ensure all module assemblies are loaded", "如果项目中不引用 Senparc.Xncf.Swagger,需要使用下方代码手动启用 DynamicAPI。更多示例参考:": "If Senparc.Xncf.Swagger is not referenced in the project, you need to use the following code to manually enable DynamicAPI. For more examples reference:", "如果运行在IIS中,需要添加IIS配置": "If running in IIS, you need to add IIS configuration", "启用以下代码强制使用 https 访问": "Enable the following code to force https access", "注入DI对象": "Inject DI object", "启动 CO2NET 全局注册,必须!": "Start CO2NET global registration, must!", "关于 UseSenparcGlobal() 的更多用法见 CO2NET Demo:https://github.com/Senparc/Senparc.CO2NET/blob/master/Sample/Senparc.CO2NET.Sample.netcore3/Startup.cs": "For more usage of UseSenparcGlobal(), see CO2NET Demo: https://github.com/Senparc/Senparc.CO2NET/blob/master/Sample/Senparc.CO2NET.Sample.netcore3/Startup.cs", "全局注册": "Global registration", "配置全局使用Redis缓存(按需,独立)": "Configure global use of Redis cache (on-demand, independent)", "配置TraceLog": "Configure TraceLog", "XncfModules(必须)": "XncfModules (required)", "UseNcfDatabase() 泛型类型说明": "UseNcfDatabase() generic type description", "* UseNcf() | 使用 达梦 数据库": "* UseNcf() | Use Dameng database", "/ 配置全局使用Redis缓存(按需,独立)": "/ Configure global use of Redis cache (on-demand, independent)", "这里为了方便不同环境的开发者进行配置,做成了判断的方式,实际开发环境一般是确定的,这里的if条件可以忽略": "In order to facilitate the configuration of developers in different environments, a judgment method is made here. The actual development environment is generally determined, and the if condition here can be ignored.", "* 1、Redis 的连接字符串信息会从 Config.SenparcSetting.Cache_Redis_Configuration 自动获取并注册,如不需要修改,下方方法可以忽略": "* 1. Redis connection string information will be automatically obtained and registered from Config.SenparcSetting.Cache_Redis_Configuration. If no modification is required, the following method can be ignored.", "/* 2、如需手动修改,可以通过下方 SetConfigurationOption 方法手动设置 Redis 链接信息(仅修改配置,不立即启用)": "/* 2. If you need to modify it manually, you can manually set the Redis link information through the SetConfigurationOption method below (only modify the configuration, not enable it immediately)", "以下会立即将全局缓存设置为 Redis": "The following will immediately set the global cache to Redis", "键值对缓存策略(推荐)": "Key-value pair caching strategy (recommended)", "HashSet储存格式的缓存策略": "Cache strategy for HashSet storage format", "也可以通过以下方式自定义当前需要启用的缓存策略": "You can also customize the caching strategy that currently needs to be enabled in the following ways", "CacheStrategyFactory.RegisterObjectCacheStrategy(() => RedisObjectCacheStrategy.Instance);//键值对": "CacheStrategyFactory.RegisterObjectCacheStrategy(() => RedisObjectCacheStrategy.Instance);//key value pair", "如果这里不进行Redis缓存启用,则目前还是默认使用内存缓存": "If the Redis cache is not enabled here, the memory cache is still used by default.", "/ 输出启动成功标志": "/ Output startup success flag", "输出启动成功标志": "Output startup success flag", "/ 判断当前配置是否满足使用 Redis(根据是否已经修改了默认配置字符串判断)": "/ Determine whether the current configuration is sufficient to use Redis (determined based on whether the default configuration string has been modified)", "默认值,不启用": "Default value, not enabled", "/ 配置微信跟踪日志": "/ Configure WeChat tracking log", "这里设为Debug状态时,/App_Data/WeixinTraceLog/目录下会生成日志文件记录所有的API请求日志,正式发布版本建议关闭": "When this is set to Debug state, a log file will be generated in the /App_Data/WeixinTraceLog/ directory to record all API request logs. It is recommended to close it for the official release version.", "如果全局的IsDebug(Senparc.CO2NET.Config.IsDebug)为false,此处可以单独设置true,否则自动为true": "If the global IsDebug (Senparc.CO2NET.Config.IsDebug) is false, you can set true here separately, otherwise it will automatically be true", "只在Senparc.Weixin.Config.IsDebug = true的情况下生效": "Only takes effect when Senparc.Weixin.Config.IsDebug = true", "全局自定义日志记录回调": "Global custom logging callback", "加入每次触发Log后需要执行的代码": "Add the code that needs to be executed every time Log is triggered", "首页开始": "Start from home page", "模块": "module", "媒体查询": "media inquiries", "为防止在主流浏览器中出现“不带控制按钮的音频模块”这一问题。": "To prevent the \"audio module without control buttons\" issue from appearing in mainstream browsers.", "解决iOS5移动端显示多余高度的兼容性问题。": "Solve the compatibility problem of excess height display on iOS5 mobile terminal.", "屏幕阅读器和搜索引擎找到地址或电话号码,默认样式是 display: block": "For screen readers and search engines to find addresses or phone numbers, the default style is display: block", "默认字体": "Default font", "在用户调整窗口大小时,字体大小做相应调整。": "When the user resizes the window, the font size adjusts accordingly.", "IE浏览器": "Internet Explorer", "FireFox浏览器": "FireFox browser", "外边距": "margins", "内边距": "padding", "边框": "frame", "去除默认下划线": "Remove default underline", "处理“outline”在Chrome浏览器中和其它浏览器之间的不一致": "Address inconsistencies between \"outline\" in Chrome and other browsers", "使h1标签在section标签和article标签的留白和字体样式统一。可同时兼容Firefox 4+、Safari 5和Chrome等不同的浏览器": "Make the space and font style of the h1 tag uniform in the section tag and article tag. Compatible with different browsers such as Firefox 4+, Safari 5 and Chrome at the same time", "解决首字母样式在IE8/9、Safari 5和chrome浏览器中未定义的问题": "Solve the problem that the initial letter style is not defined in IE8/9, Safari 5 and chrome browsers", "添加加粗样式,应用于Firefox 4+、Safari 5和Chrome": "Add bold style to Firefox 4+, Safari 5 and Chrome", "添加斜体样式,应用于Safari 5和chrome": "Add italic style, applied to Safari 5 and Chrome", "解决其在Firefox中的兼容性问题": "Resolve its compatibility issues in Firefox", "解决其在IE8/9中样式未定义的问题": "Solve the problem of undefined styles in IE8/9", "更正关联字体在Safari 5和Chrome中的老式设置": "Corrected legacy settings for associated fonts in Safari 5 and Chrome", "提高pre标签格式化文本在所有浏览器中的可读性": "Improve readability of pre tag formatted text in all browsers", "设置相一致的引号类型": "Set consistent quote type", "统一所有浏览器中字体大小不一致的兼容性问题": "Unify compatibility issues with inconsistent font sizes in all browsers", "在所有浏览器中,防止“sub”和“sup”标签影响“line-height”属性": "In all browsers, prevent \"sub\" and \"sup\" tags from affecting the \"line-height\" attribute", "在IE8/9浏览器中,当img标签中包含a标签时,去除img边框属性。": "In the IE8/9 browser, when the img tag contains the a tag, the img border attribute is removed.", "定义一致的边框、内边距和外边距": "Define consistent borders, padding, and margins", "更改“color”属性在IE8/9浏览器中没有被继承的问题": "The problem of changing the \"color\" attribute is not inherited in IE8/9 browsers", "去除外边距,如此即使人们将字段集归零也不会失去样式": "Remove margins so people don't lose style if they zero out the fieldset", "更改关联字体属性在IE8/9浏览器中没有被继承的问题": "The problem of changing associated font attributes is not inherited in IE8/9 browsers", "更改字体大小属性在IE8/9浏览器中没有被继承的问题": "The problem of changing the font size attribute is not inherited in IE8/9 browsers", "调整边距设置在Firefox 4+, Safari 5, 和 Chrome浏览器中的兼容性问题": "Compatibility issue of adjusting margin settings in Firefox 4+, Safari 5, and Chrome browsers", "调整Firefox 4+浏览器下,客户端样式表中设置了“!important”的“line-height”属性的input表单": "Adjust the input form with the \"line-height\" attribute of \"!important\" set in the client style sheet under Firefox 4+ browser", "* 调整“button”和“select”的“text-transform”继承不一致性的问题": "* Adjust the inheritance inconsistency of \"text-transform\" between \"button\" and \"select\"", "* 其他表单控件元素不继承“text-transform”属性": "* Other form control elements do not inherit the \"text-transform\" attribute", "* 修正“button”标签在Chrome, Safari 5+, and IE 8+中的样式继承问题": "* Fixed the style inheritance problem of \"button\" tag in Chrome, Safari 5+, and IE 8+", "* 修正“select”标签在Firefox 4+ 和Opera中的样式继承问题": "* Fixed the style inheritance problem of \"select\" tag in Firefox 4+ and Opera", "改正iOS设备中“input”类型表单样式不可用的问题": "Corrected the problem that \"input\" type form style is not available in iOS devices", "增强光标样式在input表单和其他表单的可用性和一致性": "Enhance the usability and consistency of cursor styles in input forms and other forms", "为禁用表单重设定默认光标样式": "Reset default cursor style for disabled forms", "调整IE 8/9中尺寸属性设置为“内容框”的盒子模型": "Adjust the box model with the size attribute set to \"Content Box\" in IE 8/9", "去除IE 8/9中的多余的外边距留白部分": "Remove excess margins in IE 8/9", "兼容Safari 5 and Chrome上 “searchfield” 上设置 “appearance”属性": "Compatible with setting the \"appearance\" attribute on \"searchfield\" on Safari 5 and Chrome", "兼容Safari 5 and Chrome上 “border-box” 上设置 “box-sizing”属性": "Compatible with setting the “box-sizing” attribute on “border-box” on Safari 5 and Chrome", "去除OS X系统上Safari 5和Chrome中容器内边距和搜索取消按钮属性": "Remove container padding and search cancel button attributes in Safari 5 and Chrome on OS X", "IE 8/9中,去除默认垂直滚动条属性": "In IE 8/9, remove the default vertical scroll bar attribute", "提高所有浏览器中的文本可读性和版式": "Improve text readability and layout in all browsers", "删除表格单元格之间的间距。": "Remove spacing between table cells.", "标准的语法": "standard syntax", "如果不存在对应的 component model 则直接 merge": "If there is no corresponding component model, merge directly", "FIXME OPTION 同步是否要改回原来的": "FIXME OPTION Do you want to change the synchronization back to the original one?", "如果有 component model 则把具体的 merge 逻辑交给该 model 处理": "If there is a component model, the specific merge logic will be handed over to the model for processing.", "用于处理merge时无法遍历Date等对象的问题": "Used to deal with the problem of being unable to traverse objects such as Date when merging", "* @return {*} 拷贝后的新对象": "* @return {*} The new object after copying", "是否为 dom 对象": "Whether it is a dom object", "如果需要递归覆盖,就递归调用merge": "If recursive coverage is required, call merge recursively.", "否则只处理overwrite为true,或者在目标对象中没有此属性的情况": "Otherwise, only the case where overwrite is true or there is no such attribute in the target object is processed.", "NOTE,在 target[key] 不存在的时候也是直接覆盖": "NOTE, when target[key] does not exist, it will be overwritten directly.", "* 查询数组中元素的index": "* Query the index of the element in the array", "* 构造类继承关系": "* Construct class inheritance relationship", "* @param {Function} clazz 源类": "* @param {Function} clazz source class", "* @param {Function} baseClazz 基类": "* @param {Function} baseClazz base class", "* 数组或对象遍历": "* Array or object traversal", "* 数组映射": "* Array mapping", "* 数组过滤": "* Array filtering", "* 数组项查找": "* Search array items", "* 每三位默认加,格式化": "* Every three digits are added by default, formatted", "应该使用统计的空判断?还是在list里进行空判断?": "Should statistical null judgment be used? Or perform empty judgment in the list?", "date型,支持formatter,autoformatter(ec2 date.getAutoFormatter)": "date type, supports formatter, autoformatter (ec2 date.getAutoFormatter)", "判断是catesian还是polar": "Determine whether it is catesian or polar", "暂时随便写的": "Just writing casually for now", "这个逻辑和getOtherAxis里一致,但是写在这里是否不好": "This logic is consistent with getOtherAxis, but is it not good to write it here?", "* 频率控制 返回函数连续调用时,fn 执行频率限定为每多少时间执行一次": "* Frequency control When the return function is called continuously, the execution frequency of fn is limited to how many times it is executed.", "* 例如常见效果:": "* For example, common effects:", "* 频繁调用时,只保证最后一次执行": "* When called frequently, only the last execution is guaranteed.", "* 配成:trailing:true;debounce:true 即可": "* Match: trailing: true; debounce: true", "* 频繁调用时,按规律心跳执行": "* When called frequently, execute according to regular heartbeat", "* 配成:trailing:true;debounce:false 即可": "* Match: trailing: true; debounce: false", "* 注意:": "* Notice:", "* 根据model更新view的时候,可以使用throttle,": "* When updating the view based on the model, you can use throttle.", "* 但是根据view更新model的时候,避免使用这种延迟更新的方式。": "* But when updating the model based on the view, avoid using this delayed update method.", "* 因为这可能导致model和server同步出现问题。": "* Because this may cause synchronization problems between the model and the server.", "* @param {(Function|Array.)} fn 需要调用的函数": "* @param {(Function|Array.)} fn The function to be called", "* 如果fn为array,则表示可以对多个函数进行throttle。": "* If fn is an array, it means that multiple functions can be throttled.", "* 他们共享同一个timer。": "* They share the same timer.", "* @param {number} delay 延迟时间,单位毫秒": "* @param {number} delay delay time, in milliseconds", "* @param {bool} trailing 是否保证最后一次触发的执行": "* @param {bool} whether trailing guarantees the execution of the last trigger", "* true:表示保证最后一次调用会触发执行。": "* true: Indicates that the last call is guaranteed to trigger execution.", "* 但任何调用后不可能立即执行,总会delay。": "* But it is impossible to execute immediately after any call, and there will always be a delay.", "* false:表示不保证最后一次调用会触发执行。": "* false: Indicates that the last call is not guaranteed to trigger execution.", "* 但只要间隔大于delay,调用就会立即执行。": "* But as long as the interval is greater than delay, the call will be executed immediately.", "* @param {bool} debounce 节流": "* @param {bool} debounce throttling", "* true:表示:频繁调用(间隔小于delay)时,根本不执行": "* true: means: when called frequently (the interval is less than delay), it will not be executed at all.", "* false:表示:频繁调用(间隔小于delay)时,按规律心跳执行": "* false: means: when called frequently (interval less than delay), execute according to regular heartbeat", "* @return {(Function|Array.)} 实际调用函数。": "* @return {(Function|Array.)} actually calls the function.", "* 当输入的fn为array时,返回值也为array。": "* When the input fn is an array, the return value is also an array.", "* 每项是Function。": "* Each item is Function.", "* 按一定频率执行,最后一次调用总归会执行": "* Executed at a certain frequency, the last call will always be executed", "* 直到不频繁调用了才会执行,最后一次调用总归会执行": "* It will not be executed until it is called infrequently. The last call will always be executed.", "polar支持": "polar support", "FIXME 这里不兼容 ECharts 2.x,2.x 貌似参数是整个数据?": "FIXME This is not compatible with ECharts 2.x. It seems that the parameter in 2.x is the entire data?", "所控制的series indices,默认所有有value的series.": "The series indices controlled are all series with value by default.", "值域边框颜色": "Range border color", "值域边框线宽,单位px,默认为0(无边框)": "Value field border line width, unit px, default is 0 (no border)", "值域内边距,单位px,默认各方向内边距为5,": "The inner margin of the value range, in px. The default inner margin in each direction is 5.", "接受数组分别设定上右下左边距,同css": "Accept arrays to set the top, right, bottom and left margins respectively, same as css", "小数精度,默认为0,无小数点": "Decimal precision, default is 0, no decimal point", "颜色(deprecated,兼容ec2,顺序同pieces,不同于inRange/outOfRange)": "Color (deprecated, compatible with ec2, the order is the same as pieces, different from inRange/outOfRange)", "文本,如['高', '低'],兼容ec2,text[0]对应高值,text[1]对应低值": "Text, such as ['high', 'low'], compatible with ec2, text[0] corresponds to high value, text[1] corresponds to low value", "值域文字颜色": "Range text color", "只考虑了list,还没有考虑map等。": "Only the list is considered, not the map, etc.", "这里可能应该这么判断:data.dimensions中有超出其所属coordSystem的量。": "This may be the judgment here: there is an amount in data.dimensions that exceeds the coordSystem to which it belongs.", "FIXME category模式也使用pieceList,但在visualMapping中不是使用pieceList。": "FIXME category mode also uses pieceList, but pieceList is not used in visualMapping.", "是否改一致。": "Whether to change it to be consistent.", "时间轴背景颜色": "Timeline background color", "时间轴边框颜色": "Timeline border color", "时间轴边框线宽,单位px,默认为0(无边框)": "Timeline border line width, unit px, default is 0 (no border)", "文本标签": "text label", "其余属性默认使用全局文本样式,详见TEXTSTYLE": "The remaining attributes use the global text style by default, see TEXTSTYLE for details.", "一级层叠": "One level cascading", "二级层叠": "Second level stacking", "模式是时间类型,支持 value, category": "The mode is a time type and supports value, category", "反向播放": "Reverse playback", "播放时间间隔,单位ms": "Playback time interval, unit ms", "暂没有实现用户传入": "User input is not implemented yet", "最小高度改为0": "Minimum height changed to 0", "默认自适应": "Default adaptive", "柱间距离,默认为柱形宽度的30%,可设固定值": "The distance between columns, the default is 30% of the column width, and can be set to a fixed value", "类目间柱形距离,默认为类目间距的20%,可设固定值": "Column distance between categories, the default is 20% of the category spacing, and can be set to a fixed value", "color: '各异',": "color: 'different',", "柱条边线": "Column edge", "柱条边线线宽,单位px,默认为1": "Column edge line width, unit px, default is 1", "阳线 positive": "Yang line positive", "阴线 negative '#c23531', '#314656'": "negative line negative '#c23531', '#314656'", "ec2中使用的是lineStyle.color 和 lineStyle.color0": "LineStyle.color and lineStyle.color0 are used in ec2", "symbol: null, // 图形类型": "symbol: null, // graphic type", "图形大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2": "Graphic size, half-width (radius) parameter, when the graphic is a direction or diamond, the total width is symbolSize * 2", "symbolRotate: null, // 图形旋转控制": "symbolRotate: null, // Graphic rotation control", "默认取数据最小最大值": "By default, the minimum and maximum value of the data is taken", "formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调": "formatter: label text formatter, same as Tooltip.formatter, does not support asynchronous callback", "textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE": "textStyle: null // Use global text style by default, see TEXTSTYLE for details", "color: 各异,": "color: different,", "默认全局居中": "Default global center", "最小值": "minimum value", "最大值": "maximum value", "分割段数,默认为10": "Number of segments, default is 10", "坐标轴线": "Coordinate axis", "默认显示,属性show控制显示与否": "Displayed by default, the attribute show controls whether to display or not.", "属性lineStyle控制线条样式": "The attribute lineStyle controls the line style", "分隔线": "divider", "属性length控制线长": "The length attribute controls the line length", "属性lineStyle(详见lineStyle)控制线条样式": "The attribute lineStyle (see lineStyle for details) controls the line style", "坐标轴小标记": "Axis small mark", "属性show控制显示与否,默认不显示": "The attribute show controls whether to display or not. It is not displayed by default.", "每份split细分多少段": "How many segments are divided into each split?", "x, y,单位px": "x, y, unit px", "* 从邻接矩阵生成": "* Generated from adjacency matrix", "* @param {Array.} nodes 节点信息": "* @param {Array.} nodes node information", "* @param {Array} matrix 邻接矩阵": "* @param {Array} matrix adjacency matrix", "* @param {boolean} directed 是否是有向图": "* @param {boolean} directed whether it is a directed graph", "多个散点图系列在同一个地区的时候": "When multiple scatter plot series are in the same region", "考虑时间轴": "Consider the timeline", "拐点图形类型": "Inflection point graph type", "拐点图形大小": "Inflection point graphic size", "拐点图形旋转控制": "Inflection point graphic rotation control", "是否显示 symbol, 只有在 tooltip hover 的时候显示": "Whether to display the symbol, it will only be displayed when the tooltip hovers", "标志图形默认只有主轴显示(随主轴标签间隔隐藏策略)": "By default, only the main axis is displayed in the logo graphic (with the main axis label interval hidden strategy)", "是否连接断点": "Whether to connect breakpoints", "数据过滤,'average', 'max', 'min', 'sum'": "Data filtering, 'average', 'max', 'min', 'sum'", "各省的 map 暂时都用中文": "The maps of each province are temporarily in Chinese.", "height // 自适应": "height // adaptive", "数值合并方式,默认加和,可选为:": "Numeric merging method, default sum, optional:", "地图数值计算结果小数精度": "Decimal precision of map numerical calculation results", "显示图例颜色标识(系列标识的小圆点),图例开启时有效": "Displays the legend color identification (the small dot of the series identification), which is valid when the legend is turned on.", "选择模式,默认关闭,可选single,multiple": "Select mode, off by default, single, multiple optional", "是否开启缩放及漫游模式": "Whether to enable zoom and roaming modes", "也是选中样式": "Also selected style", "FIXME 公用?": "FIXME Public?", "FIXME 尚无用": "FIXME is not available yet", "position: 默认自适应,水平布局为'top',垂直布局为'right',可选为": "position: The default is adaptive, the horizontal layout is 'top', the vertical layout is 'right', optional", "如果没有设置axis data, 应自动算出,或者提示。": "If axis data is not set, it should be calculated automatically or prompted.", "公用方法?": "Public method?", "默认顺时针": "Default clockwise", "最小角度改为0": "Minimum angle changed to 0", "选中是扇区偏移量": "Selected is the sector offset", "南丁格尔玫瑰图模式,'radius'(半径) | 'area'(面积)": "Nightingale rose pattern, 'radius' | 'area'", "distance: 当position为inner时有效,为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数": "distance: valid when position is inner, it is the proportional coefficient between the distance from the label position to the center of the circle and the circle radius (the donut diagram is the sum of the inner and outer radii)", "引导线两段中的第一段长度": "The length of the first of the two segments of the guide line", "引导线两段中的第二段长度": "The length of the second of the two segments of the guide line", "压": "press", "弹": "bomb", "右侧": "right side", "下": "Down", "上": "superior", "左侧": "left side", "右下,左下": "lower right, lower left", "右上,左上": "upper right, upper left", "FIXME 兼容 2.0 但是 roseType 是 area 的时候才是这样?": "FIXME is compatible with 2.0 but is this the case when roseType is area?", "color: 各异": "color: various", "sereis.mergeOption 的 getInitData是否放在merge后,从而能直接获取merege后的结果而非手动判断。": "Whether the getInitData of sereis.mergeOption is placed after merge, so that the result after merge can be obtained directly instead of manual judgment.", "加一层containerGroup是为了clip,但是现在clip功能并没有实现。": "Adding a layer of containerGroup is for clipping, but the clip function is not implemented now.", "不用click以及silent的原因是,animate时视图设置silent true来避免click生效,": "The reason why click and silent are not used is that the view sets silent true when animate to prevent click from taking effect.", "但是animate中,按下鼠标,animate结束后(silent设回为false)松开鼠标,": "But in animate, if you press the mouse and release the mouse after animate ends (silent is set back to false),", "还是会触发click,期望是不触发。": "The click will still be triggered, but the expectation is that it will not be triggered.", "现在没有clip功能,暂时取ec高宽。": "There is no clip function now, so take the ec height and width for now.", "这里是兼容ec2的写法(没指定xAxisIndex和yAxisIndex时把scatter和双数值轴折柱纳入dataZoom控制),": "Here is the writing method compatible with ec2 (when xAxisIndex and yAxisIndex are not specified, scatter and double value axis folding columns are included in dataZoom control),", "但是实际是否需要Grid.js#getScaleByOption来判断(考虑time,log等axis type)?": "But is Grid.js#getScaleByOption actually needed to judge (consider axis types such as time, log, etc.)?", "需要series的xAxisIndex和yAxisIndex都首先自动设置上。": "The xAxisIndex and yAxisIndex of the required series are automatically set first.", "例如series.type === scatter时。": "For example, when series.type === scatter.", "布局方式,默认为水平布局,可选为:": "Layout mode, the default is horizontal layout, optional:", "水平对齐": "Align horizontally", "默认为 'auto', 根据 x 的位置判断是左对齐还是右对齐": "The default is 'auto', which determines whether to align left or right based on the position of x", "图例边框颜色": "Legend border color", "图例边框线宽,单位px,默认为0(无边框)": "Legend border line width, unit px, default is 0 (no border)", "图例内边距,单位px,默认各方向内边距为5,": "Legend padding, unit px, default padding in all directions is 5.", "各个item之间的间隔,单位px,默认为10,": "The interval between each item, in px, the default is 10,", "横向布局时为水平间隔,纵向布局时为纵向间隔": "Horizontal spacing is used for horizontal layout, and vertical spacing is used for vertical layout.", "图例图形宽度": "Legend graphic width", "图例图形高度": "Legend graphic height", "图例文字颜色": "Legend text color", "选择模式,默认开启图例开关": "Select mode, the legend switch is turned on by default", "配置默认选中状态,可配合LEGEND.SELECTED事件做动态数据载入": "Configure the default selected state, which can be used with the LEGEND.SELECTED event for dynamic data loading", "图例内容(详见legend.data,数组中每一项代表一个item": "Legend content (see legend.data for details, each item in the array represents an item", "超链接跳转": "Hyperlink jump", "仅支持self | blank": "Only supports self | blank", "¦ {number}(x坐标,单位px)": "¦ {number} (x coordinate, unit px)", "¦ {number}(y坐标,单位px)": "¦ {number} (y coordinate, unit px)", "默认根据 x 的位置判断是左对齐还是右对齐": "By default, it is judged whether to align left or right based on the position of x", "标题边框颜色": "Title border color", "标题边框线宽,单位px,默认为0(无边框)": "Title border line width, unit px, default is 0 (no border)", "标题内边距,单位px,默认各方向内边距为5,": "Title padding, unit px, default padding in all directions is 5.", "主副标题纵向间隔,单位px,默认为10,": "Vertical spacing between main and subtitles, unit px, default is 10,", "主标题文字颜色": "Main title text color", "副标题文字颜色": "Subtitle text color", "tooltip主体内容": "tooltip body content", "触发类型,默认数据触发,见下图,可选为:'item' ¦ 'axis'": "Trigger type, default data trigger, see the figure below, optional: 'item' ¦ 'axis'", "触发条件,支持 'click' | 'mousemove'": "Trigger condition, supports 'click' | 'mousemove'", "是否永远显示 content": "Whether to always display content", "位置 {Array} | {Function}": "Location {Array} | {Function}", "内容格式器:{string}(Template) ¦ {Function}": "Content formatter: {string} (Template) ¦ {Function}", "隐藏延迟,单位ms": "Hidden delay, unit ms", "动画变换时间,单位s": "Animation transformation time, unit s", "提示背景颜色,默认为透明度为0.7的黑色": "Prompt background color, default is black with transparency of 0.7", "提示边框颜色": "Prompt border color", "提示边框圆角,单位px,默认为4": "Prompt border rounded corners, unit px, default is 4", "提示边框线宽,单位px,默认为0(无边框)": "Prompt border line width, unit px, default is 0 (no border)", "提示内边距,单位px,默认各方向内边距为5,": "Prompt padding in px. The default padding in each direction is 5.", "坐标轴指示器,坐标轴触发有效": "Axis indicator, axis trigger is valid", "默认为直线": "Default is straight line", "可选为:'line' | 'shadow' | 'cross'": "Optional: 'line' | 'shadow' | 'cross'", "type 为 line 的时候有效,指定 tooltip line 所在的轴,可选": "Valid when type is line, specify the axis where the tooltip line is located, optional", "可选 'x' | 'y' | 'angle' | 'radius' | 'auto'": "Optional 'x' | 'y' | 'angle' | 'radius' | 'auto'", "默认 'auto',会选择类型为 cateogry 的轴,对于双数值轴,笛卡尔坐标系会默认选择 x 轴": "The default is 'auto', which will select the axis of type cateogry. For double value axes, the Cartesian coordinate system will select the x-axis by default.", "极坐标系会默认选择 angle 轴": "The polar coordinate system will select the angle axis by default.", "直线指示器样式设置": "Line indicator style settings", "阴影指示器样式设置": "Shadow indicator style settings", "反向坐标轴": "reverse axis", "坐标轴名字,默认为空": "Axis name, default is empty", "坐标轴名字位置,支持'start' | 'middle' | 'end'": "Axis name position, supports 'start' | 'middle' | 'end'", "坐标轴文字样式,默认取全局样式": "Axis text style, default to global style", "文字与轴线距离": "Distance between text and axis", "是否能触发鼠标事件": "Whether mouse events can be triggered", "属性show控制显示与否,默认显示": "The attribute show controls whether to display or not. It is displayed by default.", "控制小标记是否在grid里": "Control whether the small mark is in the grid", "坐标轴文本标签,详见axis.axisLabel": "Axis text label, see axis.axisLabel for details", "控制文本标签是否在grid里": "Control whether the text label is in the grid", "分隔区域": "separate areas", "默认不显示,属性show控制显示与否": "Not displayed by default, the attribute show controls whether to display it or not.", "属性areaStyle(详见areaStyle)控制区域样式": "The attribute areaStyle (see areaStyle for details) controls the area style.", "类目起始和结束两端空白策略": "Blank strategy at the beginning and end of the category", "数值起始和结束两端空白策略": "Numeric starting and ending blank strategy", "最小值, 设置成 'dataMin' 则从数据中计算最小值": "Minimum value, set to 'dataMin' to calculate the minimum value from the data", "最大值,设置成 'dataMax' 则从数据中计算最大值": "Maximum value, set to 'dataMax' to calculate the maximum value from the data", "脱离0值比例,放大聚焦到最终_min,_max区间": "Break away from the 0 value ratio, zoom in and focus to the final _min, _max interval", "分割段数,默认为5": "Number of segments, default is 5", "不同角的axis和label,不只是horizontal和vertical.": "Axis and label at different angles, not just horizontal and vertical.", "依赖 GridModel, AxisModel 做预处理": "Rely on GridModel, AxisModel for preprocessing", "Grid 是在有直角坐标系的时候必须要存在的": "Grid must exist when there is a rectangular coordinate system", "所以这里也要被 Cartesian2D 依赖": "So this is also dependent on Cartesian2D", "自适应": "Adaptive", "Fix for 南海诸岛": "Fix for South China Sea Islands", "全国": "Nationwide", "'北京': [-10, 0],": "'Beijing': [-10, 0],", "tick等排布信息。": "tick and other arrangement information.", "根据axis order 更新 dimensions顺序。": "Update dimensions order according to axis order.", "依赖 PolarModel 做预处理": "Rely on PolarModel for preprocessing", "* 是否是有向图": "* Whether it is a directed graph", "* 图边": "* Picture edge", "* 节点1,如果是有向图则为源节点": "* Node 1, if it is a directed graph, it is the source node", "* 节点2,如果是有向图则为目标节点": "* Node 2, if it is a directed graph, it is the target node", "* Model 的初始化函数": "* Model initialization function", "* 从新的 Option merge": "* From new Option merge", "如果 timeline options 或者 media 中设置了某个属性,而baseOption中没有设置,则进行警告。": "If a property is set in timeline options or media but not in baseOption, a warning will be issued.", "如果没有reset功能则不clone。": "If there is no reset function, it will not be cloned.", "是否mediaDefault应该强制用户设置,否则可能修改不能回归。": "Whether mediaDefault should be forced to be set by users, otherwise it may be modified and cannot be returned.", "全图默认背景": "Full image default background", "浅色": "light color", "深色": "Dark", "默认需要 Grid 配置项": "Grid configuration items are required by default", "主题,主题": "theme, theme", "主题,默认标志图形类型列表": "Theme, list of default logo graphic types", "过渡动画是否开启": "Whether transition animation is on", "动画元素阀值,产生的图形原素超过2000不出动画": "Animation element threshold, the generated graphic elements exceeding 2000 will not animate.", "过渡动画参数:进入": "Transition animation parameters: enter", "过渡动画参数:更新": "Transition animation parameters: updated", "* 数值处理模块": "* Numerical processing module", "FIXME 判断图形默认是填充还是描边,使用 onlyStroke ?": "FIXME To determine whether a graphic is filled or stroked by default, use onlyStroke?", "|//)(?:\\\\S+(?::\\\\S*)?@)?(?:(?:(?:[1-9]\\\\d?|1\\\\d\\\\d|2[01]\\\\d|22[0-3])(?:\\\\.(?:1?\\\\d{1,2}|2[0-4]\\\\d|25[0-5])){2}(?:\\\\.(?:[0-9]\\\\d?|1\\\\d\\\\d|2[0-4]\\\\d|25[0-4]))|(?:(?:[a-z\\\\u00a1-\\\\uffff0-9]+-?)*[a-z\\\\u00a1-\\\\uffff0-9]+)(?:\\\\.(?:[a-z\\\\u00a1-\\\\uffff0-9]+-?)*[a-z\\\\u00a1-\\\\uffff0-9]+)*(?:\\\\.(?:[a-z\\\\u00a1-\\\\uffff]{2,})))|localhost)(?::\\\\d{2,5})?(?:(/|\\\\?|#)[^\\\\s]*)?$\",\"i\"),hex:/^#?([a-f0-9]{6}|[a-f0-9]{3})$/i},Ga={integer:function(e){return Ga.number(e)&&parseInt(e,10)===e},float:function(e){return Ga.number(e)&&!Ga.integer(e)},array:function(e){return Array.isArray(e)},regexp:function(e){if(e instanceof RegExp)return!0;try{return!!new RegExp(e)}catch(e){return!1}},date:function(e){return\"function\"==typeof e.getTime&&\"function\"==typeof e.getMonth&&\"function\"==typeof e.getYear},number:function(e){return!isNaN(e)&&\"number\"==typeof e},object:function(e){return\"object\"===(void 0===e?\"undefined\":Fa()(e))&&!Ga.array(e)},method:function(e){return\"function\"==typeof e},email:function(e){return\"string\"==typeof e&&!!e.match(Ka.email)&&e.length<255},url:function(e){return\"string\"==typeof e&&!!e.match(Ka.url)},hex:function(e){return\"string\"==typeof e&&!!e.match(Ka.hex)}};var Ua=function(e,t,i,n,r){if(e.required&&void 0===t)qa(e,t,i,n,r);else{var s=e.type;[\"integer\",\"float\",\"array\",\"regexp\",\"object\",\"method\",\"email\",\"number\",\"date\",\"url\",\"hex\"].indexOf(s)>-1?Ga[s](t)||n.push(Ba(r.messages.types[s],e.fullField,e.type)):s&&(void 0===t?\"undefined\":Fa()(t))!==e.type&&n.push(Ba(r.messages.types[s],e.fullField,e.type))}};var Xa=\"enum\";var Ja={required:qa,whitespace:Ya,type:Ua,range:function(e,t,i,n,r){var s=\"number\"==typeof e.len,a=\"number\"==typeof e.min,o=\"number\"==typeof e.max,l=t,u=null,c=\"number\"==typeof t,h=\"string\"==typeof t,d=Array.isArray(t);if(c?u=\"number\":h?u=\"string\":d&&(u=\"array\"),!u)return!1;d&&(l=t.length),h&&(l=t.replace(/[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/g,\"_\").length),s?l!==e.len&&n.push(Ba(r.messages[u].len,e.fullField,e.len)):a&&!o&&le.max?n.push(Ba(r.messages[u].max,e.fullField,e.max)):a&&o&&(le.max)&&n.push(Ba(r.messages[u].range,e.fullField,e.min,e.max))},enum:function(e,t,i,n,r){e[Xa]=Array.isArray(e[Xa])?e[Xa]:[],-1===e[Xa].indexOf(t)&&n.push(Ba(r.messages[Xa],e.fullField,e[Xa].join(\", \")))},pattern:function(e,t,i,n,r){e.pattern&&(e.pattern instanceof RegExp?(e.pattern.lastIndex=0,e.pattern.test(t)||n.push(Ba(r.messages.pattern.mismatch,e.fullField,t,e.pattern))):\"string\"==typeof e.pattern&&(new RegExp(e.pattern).test(t)||n.push(Ba(r.messages.pattern.mismatch,e.fullField,t,e.pattern))))}};var Za=\"enum\";var Qa=function(e,t,i,n,r){var s=e.type,a=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t,s)&&!e.required)return i();Ja.required(e,t,n,a,r,s),za(t,s)||Ja.type(e,t,n,a,r)}i(a)},eo={string:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t,\"string\")&&!e.required)return i();Ja.required(e,t,n,s,r,\"string\"),za(t,\"string\")||(Ja.type(e,t,n,s,r),Ja.range(e,t,n,s,r),Ja.pattern(e,t,n,s,r),!0===e.whitespace&&Ja.whitespace(e,t,n,s,r))}i(s)},method:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();Ja.required(e,t,n,s,r),void 0!==t&&Ja.type(e,t,n,s,r)}i(s)},number:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();Ja.required(e,t,n,s,r),void 0!==t&&(Ja.type(e,t,n,s,r),Ja.range(e,t,n,s,r))}i(s)},boolean:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();Ja.required(e,t,n,s,r),void 0!==t&&Ja.type(e,t,n,s,r)}i(s)},regexp:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();Ja.required(e,t,n,s,r),za(t)||Ja.type(e,t,n,s,r)}i(s)},integer:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();Ja.required(e,t,n,s,r),void 0!==t&&(Ja.type(e,t,n,s,r),Ja.range(e,t,n,s,r))}i(s)},float:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();Ja.required(e,t,n,s,r),void 0!==t&&(Ja.type(e,t,n,s,r),Ja.range(e,t,n,s,r))}i(s)},array:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t,\"array\")&&!e.required)return i();Ja.required(e,t,n,s,r,\"array\"),za(t,\"array\")||(Ja.type(e,t,n,s,r),Ja.range(e,t,n,s,r))}i(s)},object:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();Ja.required(e,t,n,s,r),void 0!==t&&Ja.type(e,t,n,s,r)}i(s)},enum:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();Ja.required(e,t,n,s,r),t&&Ja[Za](e,t,n,s,r)}i(s)},pattern:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t,\"string\")&&!e.required)return i();Ja.required(e,t,n,s,r),za(t,\"string\")||Ja.pattern(e,t,n,s,r)}i(s)},date:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();if(Ja.required(e,t,n,s,r),!za(t)){var a=void 0;a=\"number\"==typeof t?new Date(t):t,Ja.type(e,a,n,s,r),a&&Ja.range(e,a.getTime(),n,s,r)}}i(s)},url:Qa,hex:Qa,email:Qa,required:function(e,t,i,n,r){var s=[],a=Array.isArray(t)?\"array\":void 0===t?\"undefined\":Fa()(t);Ja.required(e,t,n,s,r,a),i(s)}};function to(){return{default:\"Validation error on field %s\",required:\"%s is required\",enum:\"%s must be one of %s\",whitespace:\"%s cannot be empty\",date:{format:\"%s date %s is invalid for format %s\",parse:\"%s date could not be parsed, %s is invalid \",invalid:\"%s date %s is invalid\"},types:{string:\"%s is not a %s\",method:\"%s is not a %s (function)\",array:\"%s is not an %s\",object:\"%s is not an %s\",number:\"%s is not a %s\",date:\"%s is not a %s\",boolean:\"%s is not a %s\",integer:\"%s is not an %s\",float:\"%s is not a %s\",regexp:\"%s is not a valid %s\",email:\"%s is not a valid %s\",url:\"%s is not a valid %s\",hex:\"%s is not a valid %s\"},string:{len:\"%s must be exactly %s characters\",min:\"%s must be at least %s characters\",max:\"%s cannot be longer than %s characters\",range:\"%s must be between %s and %s characters\"},number:{len:\"%s must equal %s\",min:\"%s cannot be less than %s\",max:\"%s cannot be greater than %s\",range:\"%s must be between %s and %s\"},array:{len:\"%s must be exactly %s in length\",min:\"%s cannot be less than %s in length\",max:\"%s cannot be greater than %s in length\",range:\"%s must be between %s and %s in length\"},pattern:{mismatch:\"%s value %s does not match pattern %s\"},clone:function(){var e=JSON.parse(JSON.stringify(this));return e.clone=this.clone,e}}}var io=to();function no(e){this.rules=null,this._messages=io,this.define(e)}no.prototype={messages:function(e){return e&&(this._messages=ja(to(),e)),this._messages},define:function(e){if(!e)throw new Error(\"Cannot configure a schema with no rules\");if(\"object\"!==(void 0===e?\"undefined\":Fa()(e))||Array.isArray(e))throw new Error(\"Rules must be an object\");this.rules={};var t=void 0,i=void 0;for(t in e)e.hasOwnProperty(t)&&(i=e[t],this.rules[t]=Array.isArray(i)?i:[i])},validate:function(e){var t=this,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments[2],r=e,s=i,a=n;if(\"function\"==typeof s&&(a=s,s={}),this.rules&&0!==Object.keys(this.rules).length){if(s.messages){var o=this.messages();o===io&&(o=to()),ja(o,s.messages),s.messages=o}else s.messages=this.messages();var l=void 0,u=void 0,c={};(s.keys||Object.keys(this.rules)).forEach(function(i){l=t.rules[i],u=r[i],l.forEach(function(n){var s=n;\"function\"==typeof s.transform&&(r===e&&(r=Ia()({},r)),u=r[i]=s.transform(u)),(s=\"function\"==typeof s?{validator:s}:Ia()({},s)).validator=t.getValidationMethod(s),s.field=i,s.fullField=s.fullField||i,s.type=t.getType(s),s.validator&&(c[i]=c[i]||[],c[i].push({rule:s,value:u,source:r,field:i}))})});var h={};Ra(c,s,function(e,t){var i=e.rule,n=!(\"object\"!==i.type&&\"array\"!==i.type||\"object\"!==Fa()(i.fields)&&\"object\"!==Fa()(i.defaultField));function r(e,t){return Ia()({},t,{fullField:i.fullField+\".\"+e})}function a(){var a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];if(Array.isArray(a)||(a=[a]),a.length&&Va(\"async-validator:\",a),a.length&&i.message&&(a=[].concat(i.message)),a=a.map(Wa(i)),s.first&&a.length)return h[i.field]=1,t(a);if(n){if(i.required&&!e.value)return a=i.message?[].concat(i.message).map(Wa(i)):s.error?[s.error(i,Ba(s.messages.required,i.field))]:[],t(a);var o={};if(i.defaultField)for(var l in e.value)e.value.hasOwnProperty(l)&&(o[l]=i.defaultField);for(var u in o=Ia()({},o,e.rule.fields))if(o.hasOwnProperty(u)){var c=Array.isArray(o[u])?o[u]:[o[u]];o[u]=c.map(r.bind(null,u))}var d=new no(o);d.messages(s.messages),e.rule.options&&(e.rule.options.messages=s.messages,e.rule.options.error=s.error),d.validate(e.value,e.rule.options||s,function(e){t(e&&e.length?a.concat(e):e)})}else t(a)}n=n&&(i.required||!i.required&&e.value),i.field=e.field;var o=i.validator(i,e.value,a,e.source,s);o&&o.then&&o.then(function(){return a()},function(e){return a(e)})},function(e){!function(e){var t,i=void 0,n=void 0,r=[],s={};for(i=0;i0&&void 0!==arguments[0]?arguments[0]:\"update\";this.$slots.default&&this.isAutoWidth&&this.$el.firstElementChild&&(\"update\"===e?this.computedWidth=this.getLabelWidth():\"remove\"===e&&this.elForm.deregisterLabelWidth(this.computedWidth))}},watch:{computedWidth:function(e,t){this.updateAll&&(this.elForm.registerLabelWidth(e,t),this.elFormItem.updateComputedLabelWidth(e))}},data:function(){return{computedWidth:0}},mounted:function(){this.updateLabelWidth(\"update\")},updated:function(){this.updateLabelWidth(\"update\")},beforeDestroy:function(){this.updateLabelWidth(\"remove\")}},void 0,void 0,!1,null,null,null);so.options.__file=\"packages/form/src/label-wrap.vue\";var ao=so.exports,oo=r({name:\"ElFormItem\",componentName:\"ElFormItem\",mixins:[l],provide:function(){return{elFormItem:this}},inject:[\"elForm\"],props:{label:String,labelWidth:String,prop:String,required:{type:Boolean,default:void 0},rules:[Object,Array],error:String,validateStatus:String,for:String,inlineMessage:{type:[String,Boolean],default:\"\"},showMessage:{type:Boolean,default:!0},size:String},components:{LabelWrap:ao},watch:{error:{immediate:!0,handler:function(e){this.validateMessage=e,this.validateState=e?\"error\":\"\"}},validateStatus:function(e){this.validateState=e}},computed:{labelFor:function(){return this.for||this.prop},labelStyle:function(){var e={};if(\"top\"===this.form.labelPosition)return e;var t=this.labelWidth||this.form.labelWidth;return t&&(e.width=t),e},contentStyle:function(){var e={},t=this.label;if(\"top\"===this.form.labelPosition||this.form.inline)return e;if(!t&&!this.labelWidth&&this.isNested)return e;var i=this.labelWidth||this.form.labelWidth;return\"auto\"===i?\"auto\"===this.labelWidth?e.marginLeft=this.computedLabelWidth:\"auto\"===this.form.labelWidth&&(e.marginLeft=this.elForm.autoLabelWidth):e.marginLeft=i,e},form:function(){for(var e=this.$parent,t=e.$options.componentName;\"ElForm\"!==t;)\"ElFormItem\"===t&&(this.isNested=!0),t=(e=e.$parent).$options.componentName;return e},fieldValue:function(){var e=this.form.model;if(e&&this.prop){var t=this.prop;return-1!==t.indexOf(\":\")&&(t=t.replace(/:/,\".\")),S(e,t,!0).v}},isRequired:function(){var e=this.getRules(),t=!1;return e&&e.length&&e.every(function(e){return!e.required||(t=!0,!1)}),t},_formSize:function(){return this.elForm.size},elFormItemSize:function(){return this.size||this._formSize},sizeClass:function(){return this.elFormItemSize||(this.$ELEMENT||{}).size}},data:function(){return{validateState:\"\",validateMessage:\"\",validateDisabled:!1,validator:{},isNested:!1,computedLabelWidth:\"\"}},methods:{validate:function(e){var t=this,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:_;this.validateDisabled=!1;var n=this.getFilteredRule(e);if((!n||0===n.length)&&void 0===this.required)return i(),!0;this.validateState=\"validating\";var r={};n&&n.length>0&&n.forEach(function(e){delete e.trigger}),r[this.prop]=n;var s=new ro(r),a={};a[this.prop]=this.fieldValue,s.validate(a,{firstFields:!0},function(e,n){t.validateState=e?\"error\":\"success\",t.validateMessage=e?e[0].message:\"\",i(t.validateMessage,n),t.elForm&&t.elForm.$emit(\"validate\",t.prop,!e,t.validateMessage||null)})},clearValidate:function(){this.validateState=\"\",this.validateMessage=\"\",this.validateDisabled=!1},resetField:function(){var e=this;this.validateState=\"\",this.validateMessage=\"\";var t=this.form.model,i=this.fieldValue,n=this.prop;-1!==n.indexOf(\":\")&&(n=n.replace(/:/,\".\"));var r=S(t,n,!0);this.validateDisabled=!0,Array.isArray(i)?r.o[r.k]=[].concat(this.initialValue):r.o[r.k]=this.initialValue,this.$nextTick(function(){e.validateDisabled=!1}),this.broadcast(\"ElTimeSelect\",\"fieldReset\",this.initialValue)},getRules:function(){var e=this.form.rules,t=this.rules,i=void 0!==this.required?{required:!!this.required}:[],n=S(e,this.prop||\"\");return e=e?n.o[this.prop||\"\"]||n.v:[],[].concat(t||e||[]).concat(i)},getFilteredRule:function(e){return this.getRules().filter(function(t){return!t.trigger||\"\"===e||(Array.isArray(t.trigger)?t.trigger.indexOf(e)>-1:t.trigger===e)}).map(function(e){return Z({},e)})},onFieldBlur:function(){this.validate(\"blur\")},onFieldChange:function(){this.validateDisabled?this.validateDisabled=!1:this.validate(\"change\")},updateComputedLabelWidth:function(e){this.computedLabelWidth=e?e+\"px\":\"\"},addValidateEvents:function(){(this.getRules().length||void 0!==this.required)&&(this.$on(\"el.form.blur\",this.onFieldBlur),this.$on(\"el.form.change\",this.onFieldChange))},removeValidateEvents:function(){this.$off()}},mounted:function(){if(this.prop){this.dispatch(\"ElForm\",\"el.form.addField\",[this]);var e=this.fieldValue;Array.isArray(e)&&(e=[].concat(e)),Object.defineProperty(this,\"initialValue\",{value:e}),this.addValidateEvents()}},beforeDestroy:function(){this.dispatch(\"ElForm\",\"el.form.removeField\",[this])}},Pa,[],!1,null,null,null);oo.options.__file=\"packages/form/src/form-item.vue\";var lo=oo.exports;lo.install=function(e){e.component(lo.name,lo)};var uo=lo,co=function(){var e=this.$createElement;return(this._self._c||e)(\"div\",{staticClass:\"el-tabs__active-bar\",class:\"is-\"+this.rootTabs.tabPosition,style:this.barStyle})};co._withStripped=!0;var ho=r({name:\"TabBar\",props:{tabs:Array},inject:[\"rootTabs\"],computed:{barStyle:{get:function(){var e=this,t={},i=0,n=0,r=-1!==[\"top\",\"bottom\"].indexOf(this.rootTabs.tabPosition)?\"width\":\"height\",s=\"width\"===r?\"x\":\"y\",a=function(e){return e.toLowerCase().replace(/( |^)[a-z]/g,function(e){return e.toUpperCase()})};this.tabs.every(function(t,s){var o=T(e.$parent.$refs.tabs||[],function(e){return e.id.replace(\"tab-\",\"\")===t.paneName});if(!o)return!1;if(t.active){n=o[\"client\"+a(r)];var l=window.getComputedStyle(o);return\"width\"===r&&e.tabs.length>1&&(n-=parseFloat(l.paddingLeft)+parseFloat(l.paddingRight)),\"width\"===r&&(i+=parseFloat(l.paddingLeft)),!1}return i+=o[\"client\"+a(r)],!0});var o=\"translate\"+a(s)+\"(\"+i+\"px)\";return t[r]=n+\"px\",t.transform=o,t.msTransform=o,t.webkitTransform=o,t}}}},co,[],!1,null,null,null);ho.options.__file=\"packages/tabs/src/tab-bar.vue\";var po=ho.exports;function fo(){}var mo=function(e){return e.toLowerCase().replace(/( |^)[a-z]/g,function(e){return e.toUpperCase()})},vo=r({name:\"TabNav\",components:{TabBar:po},inject:[\"rootTabs\"],props:{panes:Array,currentName:String,editable:Boolean,onTabClick:{type:Function,default:fo},onTabRemove:{type:Function,default:fo},type:String,stretch:Boolean},data:function(){return{scrollable:!1,navOffset:0,isFocus:!1,focusable:!0}},computed:{navStyle:function(){return{transform:\"translate\"+(-1!==[\"top\",\"bottom\"].indexOf(this.rootTabs.tabPosition)?\"X\":\"Y\")+\"(-\"+this.navOffset+\"px)\"}},sizeName:function(){return-1!==[\"top\",\"bottom\"].indexOf(this.rootTabs.tabPosition)?\"width\":\"height\"}},methods:{scrollPrev:function(){var e=this.$refs.navScroll[\"offset\"+mo(this.sizeName)],t=this.navOffset;if(t){var i=t>e?t-e:0;this.navOffset=i}},scrollNext:function(){var e=this.$refs.nav[\"offset\"+mo(this.sizeName)],t=this.$refs.navScroll[\"offset\"+mo(this.sizeName)],i=this.navOffset;if(!(e-i<=t)){var n=e-i>2*t?i+t:e-t;this.navOffset=n}},scrollToActiveTab:function(){if(this.scrollable){var e=this.$refs.nav,t=this.$el.querySelector(\".is-active\");if(t){var i=this.$refs.navScroll,n=-1!==[\"top\",\"bottom\"].indexOf(this.rootTabs.tabPosition),r=t.getBoundingClientRect(),s=i.getBoundingClientRect(),a=n?e.offsetWidth-s.width:e.offsetHeight-s.height,o=this.navOffset,l=o;n?(r.lefts.right&&(l=o+r.right-s.right)):(r.tops.bottom&&(l=o+(r.bottom-s.bottom))),l=Math.max(l,0),this.navOffset=Math.min(l,a)}}},update:function(){if(this.$refs.nav){var e=this.sizeName,t=this.$refs.nav[\"offset\"+mo(e)],i=this.$refs.navScroll[\"offset\"+mo(e)],n=this.navOffset;if(i0&&(this.navOffset=0)}},changeTab:function(e){var t=e.keyCode,i=void 0,n=void 0,r=void 0;-1!==[37,38,39,40].indexOf(t)&&(r=e.currentTarget.querySelectorAll(\"[role=tab]\"),n=Array.prototype.indexOf.call(r,e.target),r[i=37===t||38===t?0===n?r.length-1:n-1:n0&&void 0!==arguments[0]&&arguments[0];if(this.$slots.default){var i=this.$slots.default.filter(function(e){return e.tag&&e.componentOptions&&\"ElTabPane\"===e.componentOptions.Ctor.options.name}).map(function(e){return e.componentInstance}),n=!(i.length===this.panes.length&&i.every(function(t,i){return t===e.panes[i]}));(t||n)&&(this.panes=i)}else 0!==this.panes.length&&(this.panes=[])},handleTabClick:function(e,t,i){e.disabled||(this.setCurrentName(t),this.$emit(\"tab-click\",e,i))},handleTabRemove:function(e,t){e.disabled||(t.stopPropagation(),this.$emit(\"edit\",e.name,\"remove\"),this.$emit(\"tab-remove\",e.name))},handleTabAdd:function(){this.$emit(\"edit\",null,\"add\"),this.$emit(\"tab-add\")},setCurrentName:function(e){var t=this,i=function(){t.currentName=e,t.$emit(\"input\",e)};if(this.currentName!==e&&this.beforeLeave){var n=this.beforeLeave(e,this.currentName);n&&n.then?n.then(function(){i(),t.$refs.nav&&t.$refs.nav.removeFocus()},function(){}):!1!==n&&i()}else i()}},render:function(e){var t,i=this.type,n=this.handleTabClick,r=this.handleTabRemove,s=this.handleTabAdd,a=this.currentName,o=this.panes,l=this.editable,u=this.addable,c=this.tabPosition,h=this.stretch,d=l||u?e(\"span\",{class:\"el-tabs__new-tab\",on:{click:s,keydown:function(e){13===e.keyCode&&s()}},attrs:{tabindex:\"0\"}},[e(\"i\",{class:\"el-icon-plus\"})]):null,p=e(\"div\",{class:[\"el-tabs__header\",\"is-\"+c]},[d,e(\"tab-nav\",{props:{currentName:a,onTabClick:n,onTabRemove:r,editable:l,type:i,panes:o,stretch:h},ref:\"nav\"})]),f=e(\"div\",{class:\"el-tabs__content\"},[this.$slots.default]);return e(\"div\",{class:(t={\"el-tabs\":!0,\"el-tabs--card\":\"card\"===i},t[\"el-tabs--\"+c]=!0,t[\"el-tabs--border-card\"]=\"border-card\"===i,t)},[\"bottom\"!==c?[p,f]:[f,p]])},created:function(){this.currentName||this.setCurrentName(\"0\"),this.$on(\"tab-nav-update\",this.calcPaneInstances.bind(null,!0))},mounted:function(){this.calcPaneInstances()},updated:function(){this.calcPaneInstances()}},void 0,void 0,!1,null,null,null);go.options.__file=\"packages/tabs/src/tabs.vue\";var bo=go.exports;bo.install=function(e){e.component(bo.name,bo)};var yo=bo,wo=function(){var e=this,t=e.$createElement,i=e._self._c||t;return!e.lazy||e.loaded||e.active?i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.active,expression:\"active\"}],staticClass:\"el-tab-pane\",attrs:{role:\"tabpanel\",\"aria-hidden\":!e.active,id:\"pane-\"+e.paneName,\"aria-labelledby\":\"tab-\"+e.paneName}},[e._t(\"default\")],2):e._e()};wo._withStripped=!0;var _o=r({name:\"ElTabPane\",componentName:\"ElTabPane\",props:{label:String,labelContent:Function,name:String,closable:Boolean,disabled:Boolean,lazy:Boolean},data:function(){return{index:null,loaded:!1}},computed:{isClosable:function(){return this.closable||this.$parent.closable},active:function(){var e=this.$parent.currentName===(this.name||this.index);return e&&(this.loaded=!0),e},paneName:function(){return this.name||this.index}},updated:function(){this.$parent.$emit(\"tab-nav-update\")}},wo,[],!1,null,null,null);_o.options.__file=\"packages/tabs/src/tab-pane.vue\";var xo=_o.exports;xo.install=function(e){e.component(xo.name,xo)};var Co=xo,ko=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-tree\",class:{\"el-tree--highlight-current\":e.highlightCurrent,\"is-dragging\":!!e.dragState.draggingNode,\"is-drop-not-allow\":!e.dragState.allowDrop,\"is-drop-inner\":\"inner\"===e.dragState.dropType},attrs:{role:\"tree\"}},[e._l(e.root.childNodes,function(t){return i(\"el-tree-node\",{key:e.getNodeKey(t),attrs:{node:t,props:e.props,\"render-after-expand\":e.renderAfterExpand,\"show-checkbox\":e.showCheckbox,\"render-content\":e.renderContent},on:{\"node-expand\":e.handleNodeExpand}})}),e.isEmpty?i(\"div\",{staticClass:\"el-tree__empty-block\"},[i(\"span\",{staticClass:\"el-tree__empty-text\"},[e._v(e._s(e.emptyText))])]):e._e(),i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.dragState.showDropIndicator,expression:\"dragState.showDropIndicator\"}],ref:\"dropIndicator\",staticClass:\"el-tree__drop-indicator\"})],2)};ko._withStripped=!0;var So=\"$treeNodeId\",Do=function(e,t){t&&!t[So]&&Object.defineProperty(t,So,{value:e.id,enumerable:!1,configurable:!1,writable:!1})},$o=function(e,t){return e?t[e]:t[So]},Eo=function(){function e(e,t){for(var i=0;i0&&n.lazy&&n.defaultExpandAll&&this.expand(),Array.isArray(this.data)||Do(this,this.data),this.data){var a=n.defaultExpandedKeys,o=n.key;o&&a&&-1!==a.indexOf(this.key)&&this.expand(null,n.autoExpandParent),o&&void 0!==n.currentNodeKey&&this.key===n.currentNodeKey&&(n.currentNode=this,n.currentNode.isCurrent=!0),n.lazy&&n._initDefaultCheckedNode(this),this.updateLeafState()}}return e.prototype.setData=function(e){Array.isArray(e)||Do(this,e),this.data=e,this.childNodes=[];for(var t=void 0,i=0,n=(t=0===this.level&&this.data instanceof Array?this.data:No(this,\"children\")||[]).length;i1&&void 0!==arguments[1])||arguments[1];return function i(n){for(var r=n.childNodes||[],s=!1,a=0,o=r.length;a-1&&t.splice(i,1);var n=this.childNodes.indexOf(e);n>-1&&(this.store&&this.store.deregisterNode(e),e.parent=null,this.childNodes.splice(n,1)),this.updateLeafState()},e.prototype.removeChildByData=function(e){for(var t=null,i=0;i0;)n.expanded=!0,n=n.parent;i.expanded=!0,e&&e()};this.shouldLoadData()?this.loadData(function(e){e instanceof Array&&(i.checked?i.setChecked(!0,!0):i.store.checkStrictly||Mo(i),n())}):n()},e.prototype.doCreateChildren=function(e){var t=this,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};e.forEach(function(e){t.insertChild(Z({data:e},i),void 0,!0)})},e.prototype.collapse=function(){this.expanded=!1},e.prototype.shouldLoadData=function(){return!0===this.store.lazy&&this.store.load&&!this.loaded},e.prototype.updateLeafState=function(){if(!0!==this.store.lazy||!0===this.loaded||void 0===this.isLeafByUser){var e=this.childNodes;!this.store.lazy||!0===this.store.lazy&&!0===this.loaded?this.isLeaf=!e||0===e.length:this.isLeaf=!1}else this.isLeaf=this.isLeafByUser},e.prototype.setChecked=function(e,t,i,n){var r=this;if(this.indeterminate=\"half\"===e,this.checked=!0===e,!this.store.checkStrictly){if(!this.shouldLoadData()||this.store.checkDescendants){var s=To(this.childNodes),a=s.all,o=s.allWithoutDisable;this.isLeaf||a||!o||(this.checked=!1,e=!1);var l=function(){if(t){for(var i=r.childNodes,s=0,a=i.length;s0&&void 0!==arguments[0]&&arguments[0];if(0===this.level)return this.data;var t=this.data;if(!t)return null;var i=this.store.props,n=\"children\";return i&&(n=i.children||\"children\"),void 0===t[n]&&(t[n]=null),e&&!t[n]&&(t[n]=[]),t[n]},e.prototype.updateChildren=function(){var e=this,t=this.getChildren()||[],i=this.childNodes.map(function(e){return e.data}),n={},r=[];t.forEach(function(e,t){var s=e[So];!!s&&E(i,function(e){return e[So]===s})>=0?n[s]={index:t,data:e}:r.push({index:t,data:e})}),this.store.lazy||i.forEach(function(t){n[t[So]]||e.removeChildByData(t)}),r.forEach(function(t){var i=t.index,n=t.data;e.insertChild({data:n},i)}),this.updateLeafState()},e.prototype.loadData=function(e){var t=this,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!0!==this.store.lazy||!this.store.load||this.loaded||this.loading&&!Object.keys(i).length)e&&e.call(this);else{this.loading=!0;this.store.load(this,function(n){t.loaded=!0,t.loading=!1,t.childNodes=[],t.doCreateChildren(n,i),t.updateLeafState(),e&&e.call(t,n)})}},Eo(e,[{key:\"label\",get:function(){return No(this,\"label\")}},{key:\"key\",get:function(){var e=this.store.key;return this.data?this.data[e]:null}},{key:\"disabled\",get:function(){return No(this,\"disabled\")}},{key:\"nextSibling\",get:function(){var e=this.parent;if(e){var t=e.childNodes.indexOf(this);if(t>-1)return e.childNodes[t+1]}return null}},{key:\"previousSibling\",get:function(){var e=this.parent;if(e){var t=e.childNodes.indexOf(this);if(t>-1)return t>0?e.childNodes[t-1]:null}return null}}]),e}(),Io=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e};var Ao=function(){function e(t){var i=this;for(var n in function(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")}(this,e),this.currentNode=null,this.currentNodeKey=null,t)t.hasOwnProperty(n)&&(this[n]=t[n]);(this.nodesMap={},this.root=new Oo({data:this.data,store:this}),this.lazy&&this.load)?(0,this.load)(this.root,function(e){i.root.doCreateChildren(e),i._initDefaultCheckedNodes()}):this._initDefaultCheckedNodes()}return e.prototype.filter=function(e){var t=this.filterNodeMethod,i=this.lazy;!function n(r){var s=r.root?r.root.childNodes:r.childNodes;if(s.forEach(function(i){i.visible=t.call(i,e,i.data,i),n(i)}),!r.visible&&s.length){var a;a=!s.some(function(e){return e.visible}),r.root?r.root.visible=!1===a:r.visible=!1===a}e&&(!r.visible||r.isLeaf||i||r.expand())}(this)},e.prototype.setData=function(e){e!==this.root.data?(this.root.setData(e),this._initDefaultCheckedNodes()):this.root.updateChildren()},e.prototype.getNode=function(e){if(e instanceof Oo)return e;var t=\"object\"!==(void 0===e?\"undefined\":Io(e))?e:$o(this.key,e);return this.nodesMap[t]||null},e.prototype.insertBefore=function(e,t){var i=this.getNode(t);i.parent.insertBefore({data:e},i)},e.prototype.insertAfter=function(e,t){var i=this.getNode(t);i.parent.insertAfter({data:e},i)},e.prototype.remove=function(e){var t=this.getNode(e);t&&t.parent&&(t===this.currentNode&&(this.currentNode=null),t.parent.removeChild(t))},e.prototype.append=function(e,t){var i=t?this.getNode(t):this.root;i&&i.insertChild({data:e})},e.prototype._initDefaultCheckedNodes=function(){var e=this,t=this.defaultCheckedKeys||[],i=this.nodesMap;t.forEach(function(t){var n=i[t];n&&n.setChecked(!0,!e.checkStrictly)})},e.prototype._initDefaultCheckedNode=function(e){-1!==(this.defaultCheckedKeys||[]).indexOf(e.key)&&e.setChecked(!0,!this.checkStrictly)},e.prototype.setDefaultCheckedKey=function(e){e!==this.defaultCheckedKeys&&(this.defaultCheckedKeys=e,this._initDefaultCheckedNodes())},e.prototype.registerNode=function(e){this.key&&e&&e.data&&(void 0!==e.key&&(this.nodesMap[e.key]=e))},e.prototype.deregisterNode=function(e){var t=this;this.key&&e&&e.data&&(e.childNodes.forEach(function(e){t.deregisterNode(e)}),delete this.nodesMap[e.key])},e.prototype.getCheckedNodes=function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=[];return function n(r){(r.root?r.root.childNodes:r.childNodes).forEach(function(r){(r.checked||t&&r.indeterminate)&&(!e||e&&r.isLeaf)&&i.push(r.data),n(r)})}(this),i},e.prototype.getCheckedKeys=function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]&&arguments[0];return this.getCheckedNodes(t).map(function(t){return(t||{})[e.key]})},e.prototype.getHalfCheckedNodes=function(){var e=[];return function t(i){(i.root?i.root.childNodes:i.childNodes).forEach(function(i){i.indeterminate&&e.push(i.data),t(i)})}(this),e},e.prototype.getHalfCheckedKeys=function(){var e=this;return this.getHalfCheckedNodes().map(function(t){return(t||{})[e.key]})},e.prototype._getAllNodes=function(){var e=[],t=this.nodesMap;for(var i in t)t.hasOwnProperty(i)&&e.push(t[i]);return e},e.prototype.updateChildren=function(e,t){var i=this.nodesMap[e];if(i){for(var n=i.childNodes,r=n.length-1;r>=0;r--){var s=n[r];this.remove(s.data)}for(var a=0,o=t.length;a1&&void 0!==arguments[1]&&arguments[1],i=arguments[2],n=this._getAllNodes().sort(function(e,t){return t.level-e.level}),r=Object.create(null),s=Object.keys(i);n.forEach(function(e){return e.setChecked(!1,!1)});for(var a=0,o=n.length;a-1){for(var c=l.parent;c&&c.level>0;)r[c.data[e]]=!0,c=c.parent;l.isLeaf||this.checkStrictly?l.setChecked(!0,!1):(l.setChecked(!0,!0),t&&function(){l.setChecked(!1,!1);!function e(t){t.childNodes.forEach(function(t){t.isLeaf||t.setChecked(!1,!1),e(t)})}(l)}())}else l.checked&&!r[u]&&l.setChecked(!1,!1)}},e.prototype.setCheckedNodes=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=this.key,n={};e.forEach(function(e){n[(e||{})[i]]=!0}),this._setCheckedKeys(i,t,n)},e.prototype.setCheckedKeys=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];this.defaultCheckedKeys=e;var i=this.key,n={};e.forEach(function(e){n[e]=!0}),this._setCheckedKeys(i,t,n)},e.prototype.setDefaultExpandedKeys=function(e){var t=this;e=e||[],this.defaultExpandedKeys=e,e.forEach(function(e){var i=t.getNode(e);i&&i.expand(null,t.autoExpandParent)})},e.prototype.setChecked=function(e,t,i){var n=this.getNode(e);n&&n.setChecked(!!t,i)},e.prototype.getCurrentNode=function(){return this.currentNode},e.prototype.setCurrentNode=function(e){var t=this.currentNode;t&&(t.isCurrent=!1),this.currentNode=e,this.currentNode.isCurrent=!0},e.prototype.setUserCurrentNode=function(e){var t=e[this.key],i=this.nodesMap[t];this.setCurrentNode(i)},e.prototype.setCurrentNodeKey=function(e){if(null==e)return this.currentNode&&(this.currentNode.isCurrent=!1),void(this.currentNode=null);var t=this.getNode(e);t&&this.setCurrentNode(t)},e}(),Fo=function(){var e=this,t=this,i=t.$createElement,n=t._self._c||i;return n(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:t.node.visible,expression:\"node.visible\"}],ref:\"node\",staticClass:\"el-tree-node\",class:{\"is-expanded\":t.expanded,\"is-current\":t.node.isCurrent,\"is-hidden\":!t.node.visible,\"is-focusable\":!t.node.disabled,\"is-checked\":!t.node.disabled&&t.node.checked},attrs:{role:\"treeitem\",tabindex:\"-1\",\"aria-expanded\":t.expanded,\"aria-disabled\":t.node.disabled,\"aria-checked\":t.node.checked,draggable:t.tree.draggable},on:{click:function(e){return e.stopPropagation(),t.handleClick(e)},contextmenu:function(t){return e.handleContextMenu(t)},dragstart:function(e){return e.stopPropagation(),t.handleDragStart(e)},dragover:function(e){return e.stopPropagation(),t.handleDragOver(e)},dragend:function(e){return e.stopPropagation(),t.handleDragEnd(e)},drop:function(e){return e.stopPropagation(),t.handleDrop(e)}}},[n(\"div\",{staticClass:\"el-tree-node__content\",style:{\"padding-left\":(t.node.level-1)*t.tree.indent+\"px\"}},[n(\"span\",{class:[{\"is-leaf\":t.node.isLeaf,expanded:!t.node.isLeaf&&t.expanded},\"el-tree-node__expand-icon\",t.tree.iconClass?t.tree.iconClass:\"el-icon-caret-right\"],on:{click:function(e){return e.stopPropagation(),t.handleExpandIconClick(e)}}}),t.showCheckbox?n(\"el-checkbox\",{attrs:{indeterminate:t.node.indeterminate,disabled:!!t.node.disabled},on:{change:t.handleCheckChange},nativeOn:{click:function(e){e.stopPropagation()}},model:{value:t.node.checked,callback:function(e){t.$set(t.node,\"checked\",e)},expression:\"node.checked\"}}):t._e(),t.node.loading?n(\"span\",{staticClass:\"el-tree-node__loading-icon el-icon-loading\"}):t._e(),n(\"node-content\",{attrs:{node:t.node}})],1),n(\"el-collapse-transition\",[!t.renderAfterExpand||t.childNodeRendered?n(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:t.expanded,expression:\"expanded\"}],staticClass:\"el-tree-node__children\",attrs:{role:\"group\",\"aria-expanded\":t.expanded}},t._l(t.node.childNodes,function(e){return n(\"el-tree-node\",{key:t.getNodeKey(e),attrs:{\"render-content\":t.renderContent,\"render-after-expand\":t.renderAfterExpand,\"show-checkbox\":t.showCheckbox,node:e},on:{\"node-expand\":t.handleChildNodeExpand}})}),1):t._e()])],1)};Fo._withStripped=!0;var Lo=r({name:\"ElTreeNode\",componentName:\"ElTreeNode\",mixins:[l],props:{node:{default:function(){return{}}},props:{},renderContent:Function,renderAfterExpand:{type:Boolean,default:!0},showCheckbox:{type:Boolean,default:!1}},components:{ElCollapseTransition:ii,ElCheckbox:Vi,NodeContent:{props:{node:{required:!0}},render:function(e){var t=this.$parent,i=t.tree,n=this.node,r=n.data,s=n.store;return t.renderContent?t.renderContent.call(t._renderProxy,e,{_self:i.$vnode.context,node:n,data:r,store:s}):i.$scopedSlots.default?i.$scopedSlots.default({node:n,data:r}):e(\"span\",{class:\"el-tree-node__label\"},[n.label])}}},data:function(){return{tree:null,expanded:!1,childNodeRendered:!1,oldChecked:null,oldIndeterminate:null}},watch:{\"node.indeterminate\":function(e){this.handleSelectChange(this.node.checked,e)},\"node.checked\":function(e){this.handleSelectChange(e,this.node.indeterminate)},\"node.expanded\":function(e){var t=this;this.$nextTick(function(){return t.expanded=e}),e&&(this.childNodeRendered=!0)}},methods:{getNodeKey:function(e){return $o(this.tree.nodeKey,e.data)},handleSelectChange:function(e,t){this.oldChecked!==e&&this.oldIndeterminate!==t&&this.tree.$emit(\"check-change\",this.node.data,e,t),this.oldChecked=e,this.indeterminate=t},handleClick:function(){var e=this.tree.store;e.setCurrentNode(this.node),this.tree.$emit(\"current-change\",e.currentNode?e.currentNode.data:null,e.currentNode),this.tree.currentNode=this,this.tree.expandOnClickNode&&this.handleExpandIconClick(),this.tree.checkOnClickNode&&!this.node.disabled&&this.handleCheckChange(null,{target:{checked:!this.node.checked}}),this.tree.$emit(\"node-click\",this.node.data,this.node,this)},handleContextMenu:function(e){this.tree._events[\"node-contextmenu\"]&&this.tree._events[\"node-contextmenu\"].length>0&&(e.stopPropagation(),e.preventDefault()),this.tree.$emit(\"node-contextmenu\",e,this.node.data,this.node,this)},handleExpandIconClick:function(){this.node.isLeaf||(this.expanded?(this.tree.$emit(\"node-collapse\",this.node.data,this.node,this),this.node.collapse()):(this.node.expand(),this.$emit(\"node-expand\",this.node.data,this.node,this)))},handleCheckChange:function(e,t){var i=this;this.node.setChecked(t.target.checked,!this.tree.checkStrictly),this.$nextTick(function(){var e=i.tree.store;i.tree.$emit(\"check\",i.node.data,{checkedNodes:e.getCheckedNodes(),checkedKeys:e.getCheckedKeys(),halfCheckedNodes:e.getHalfCheckedNodes(),halfCheckedKeys:e.getHalfCheckedKeys()})})},handleChildNodeExpand:function(e,t,i){this.broadcast(\"ElTreeNode\",\"tree-node-expand\",t),this.tree.$emit(\"node-expand\",e,t,i)},handleDragStart:function(e){this.tree.draggable&&this.tree.$emit(\"tree-node-drag-start\",e,this)},handleDragOver:function(e){this.tree.draggable&&(this.tree.$emit(\"tree-node-drag-over\",e,this),e.preventDefault())},handleDrop:function(e){e.preventDefault()},handleDragEnd:function(e){this.tree.draggable&&this.tree.$emit(\"tree-node-drag-end\",e,this)}},created:function(){var e=this,t=this.$parent;t.isTree?this.tree=t:this.tree=t.tree;var i=this.tree;i||console.warn(\"Can not find node's tree.\");var n=(i.props||{}).children||\"children\";this.$watch(\"node.data.\"+n,function(){e.node.updateChildren()}),this.node.expanded&&(this.expanded=!0,this.childNodeRendered=!0),this.tree.accordion&&this.$on(\"tree-node-expand\",function(t){e.node!==t&&e.node.collapse()})}},Fo,[],!1,null,null,null);Lo.options.__file=\"packages/tree/src/tree-node.vue\";var Vo=r({name:\"ElTree\",mixins:[l],components:{ElTreeNode:Lo.exports},data:function(){return{store:null,root:null,currentNode:null,treeItems:null,checkboxItems:[],dragState:{showDropIndicator:!1,draggingNode:null,dropNode:null,allowDrop:!0}}},props:{data:{type:Array},emptyText:{type:String,default:function(){return W(\"el.tree.emptyText\")}},renderAfterExpand:{type:Boolean,default:!0},nodeKey:String,checkStrictly:Boolean,defaultExpandAll:Boolean,expandOnClickNode:{type:Boolean,default:!0},checkOnClickNode:Boolean,checkDescendants:{type:Boolean,default:!1},autoExpandParent:{type:Boolean,default:!0},defaultCheckedKeys:Array,defaultExpandedKeys:Array,currentNodeKey:[String,Number],renderContent:Function,showCheckbox:{type:Boolean,default:!1},draggable:{type:Boolean,default:!1},allowDrag:Function,allowDrop:Function,props:{default:function(){return{children:\"children\",label:\"label\",disabled:\"disabled\"}}},lazy:{type:Boolean,default:!1},highlightCurrent:Boolean,load:Function,filterNodeMethod:Function,accordion:Boolean,indent:{type:Number,default:18},iconClass:String},computed:{children:{set:function(e){this.data=e},get:function(){return this.data}},treeItemArray:function(){return Array.prototype.slice.call(this.treeItems)},isEmpty:function(){var e=this.root.childNodes;return!e||0===e.length||e.every(function(e){return!e.visible})}},watch:{defaultCheckedKeys:function(e){this.store.setDefaultCheckedKey(e)},defaultExpandedKeys:function(e){this.store.defaultExpandedKeys=e,this.store.setDefaultExpandedKeys(e)},data:function(e){this.store.setData(e)},checkboxItems:function(e){Array.prototype.forEach.call(e,function(e){e.setAttribute(\"tabindex\",-1)})},checkStrictly:function(e){this.store.checkStrictly=e}},methods:{filter:function(e){if(!this.filterNodeMethod)throw new Error(\"[Tree] filterNodeMethod is required when filter\");this.store.filter(e)},getNodeKey:function(e){return $o(this.nodeKey,e.data)},getNodePath:function(e){if(!this.nodeKey)throw new Error(\"[Tree] nodeKey is required in getNodePath\");var t=this.store.getNode(e);if(!t)return[];for(var i=[t.data],n=t.parent;n&&n!==this.root;)i.push(n.data),n=n.parent;return i.reverse()},getCheckedNodes:function(e,t){return this.store.getCheckedNodes(e,t)},getCheckedKeys:function(e){return this.store.getCheckedKeys(e)},getCurrentNode:function(){var e=this.store.getCurrentNode();return e?e.data:null},getCurrentKey:function(){if(!this.nodeKey)throw new Error(\"[Tree] nodeKey is required in getCurrentKey\");var e=this.getCurrentNode();return e?e[this.nodeKey]:null},setCheckedNodes:function(e,t){if(!this.nodeKey)throw new Error(\"[Tree] nodeKey is required in setCheckedNodes\");this.store.setCheckedNodes(e,t)},setCheckedKeys:function(e,t){if(!this.nodeKey)throw new Error(\"[Tree] nodeKey is required in setCheckedKeys\");this.store.setCheckedKeys(e,t)},setChecked:function(e,t,i){this.store.setChecked(e,t,i)},getHalfCheckedNodes:function(){return this.store.getHalfCheckedNodes()},getHalfCheckedKeys:function(){return this.store.getHalfCheckedKeys()},setCurrentNode:function(e){if(!this.nodeKey)throw new Error(\"[Tree] nodeKey is required in setCurrentNode\");this.store.setUserCurrentNode(e)},setCurrentKey:function(e){if(!this.nodeKey)throw new Error(\"[Tree] nodeKey is required in setCurrentKey\");this.store.setCurrentNodeKey(e)},getNode:function(e){return this.store.getNode(e)},remove:function(e){this.store.remove(e)},append:function(e,t){this.store.append(e,t)},insertBefore:function(e,t){this.store.insertBefore(e,t)},insertAfter:function(e,t){this.store.insertAfter(e,t)},handleNodeExpand:function(e,t,i){this.broadcast(\"ElTreeNode\",\"tree-node-expand\",t),this.$emit(\"node-expand\",e,t,i)},updateKeyChildren:function(e,t){if(!this.nodeKey)throw new Error(\"[Tree] nodeKey is required in updateKeyChild\");this.store.updateChildren(e,t)},initTabIndex:function(){this.treeItems=this.$el.querySelectorAll(\".is-focusable[role=treeitem]\"),this.checkboxItems=this.$el.querySelectorAll(\"input[type=checkbox]\");var e=this.$el.querySelectorAll(\".is-checked[role=treeitem]\");e.length?e[0].setAttribute(\"tabindex\",0):this.treeItems[0]&&this.treeItems[0].setAttribute(\"tabindex\",0)},handleKeydown:function(e){var t=e.target;if(-1!==t.className.indexOf(\"el-tree-node\")){var i=e.keyCode;this.treeItems=this.$el.querySelectorAll(\".is-focusable[role=treeitem]\");var n=this.treeItemArray.indexOf(t),r=void 0;[38,40].indexOf(i)>-1&&(e.preventDefault(),r=38===i?0!==n?n-1:0:n-1&&(e.preventDefault(),t.click());var s=t.querySelector('[type=\"checkbox\"]');[13,32].indexOf(i)>-1&&s&&(e.preventDefault(),s.click())}}},created:function(){var e=this;this.isTree=!0,this.store=new Ao({key:this.nodeKey,data:this.data,lazy:this.lazy,props:this.props,load:this.load,currentNodeKey:this.currentNodeKey,checkStrictly:this.checkStrictly,checkDescendants:this.checkDescendants,defaultCheckedKeys:this.defaultCheckedKeys,defaultExpandedKeys:this.defaultExpandedKeys,autoExpandParent:this.autoExpandParent,defaultExpandAll:this.defaultExpandAll,filterNodeMethod:this.filterNodeMethod}),this.root=this.store.root;var t=this.dragState;this.$on(\"tree-node-drag-start\",function(i,n){if(\"function\"==typeof e.allowDrag&&!e.allowDrag(n.node))return i.preventDefault(),!1;i.dataTransfer.effectAllowed=\"move\";try{i.dataTransfer.setData(\"text/plain\",\"\")}catch(e){}t.draggingNode=n,e.$emit(\"node-drag-start\",n.node,i)}),this.$on(\"tree-node-drag-over\",function(i,n){var r=function(e,t){for(var i=e;i&&\"BODY\"!==i.tagName;){if(i.__vue__&&i.__vue__.$options.name===t)return i.__vue__;i=i.parentNode}return null}(i.target,\"ElTreeNode\"),s=t.dropNode;s&&s!==r&&me(s.$el,\"is-drop-inner\");var a=t.draggingNode;if(a&&r){var o=!0,l=!0,u=!0,c=!0;\"function\"==typeof e.allowDrop&&(o=e.allowDrop(a.node,r.node,\"prev\"),c=l=e.allowDrop(a.node,r.node,\"inner\"),u=e.allowDrop(a.node,r.node,\"next\")),i.dataTransfer.dropEffect=l?\"move\":\"none\",(o||l||u)&&s!==r&&(s&&e.$emit(\"node-drag-leave\",a.node,s.node,i),e.$emit(\"node-drag-enter\",a.node,r.node,i)),(o||l||u)&&(t.dropNode=r),r.node.nextSibling===a.node&&(u=!1),r.node.previousSibling===a.node&&(o=!1),r.node.contains(a.node,!1)&&(l=!1),(a.node===r.node||a.node.contains(r.node))&&(o=!1,l=!1,u=!1);var h=r.$el.getBoundingClientRect(),d=e.$el.getBoundingClientRect(),p=void 0,f=o?l?.25:u?.45:1:-1,m=u?l?.75:o?.55:0:1,v=-9999,g=i.clientY-h.top;p=gh.height*m?\"after\":l?\"inner\":\"none\";var b=r.$el.querySelector(\".el-tree-node__expand-icon\").getBoundingClientRect(),y=e.$refs.dropIndicator;\"before\"===p?v=b.top-d.top:\"after\"===p&&(v=b.bottom-d.top),y.style.top=v+\"px\",y.style.left=b.right-d.left+\"px\",\"inner\"===p?fe(r.$el,\"is-drop-inner\"):me(r.$el,\"is-drop-inner\"),t.showDropIndicator=\"before\"===p||\"after\"===p,t.allowDrop=t.showDropIndicator||c,t.dropType=p,e.$emit(\"node-drag-over\",a.node,r.node,i)}}),this.$on(\"tree-node-drag-end\",function(i){var n=t.draggingNode,r=t.dropType,s=t.dropNode;if(i.preventDefault(),i.dataTransfer.dropEffect=\"move\",n&&s){var a={data:n.node.data};\"none\"!==r&&n.node.remove(),\"before\"===r?s.node.parent.insertBefore(a,s.node):\"after\"===r?s.node.parent.insertAfter(a,s.node):\"inner\"===r&&s.node.insertChild(a),\"none\"!==r&&e.store.registerNode(a),me(s.$el,\"is-drop-inner\"),e.$emit(\"node-drag-end\",n.node,s.node,r,i),\"none\"!==r&&e.$emit(\"node-drop\",n.node,s.node,r,i)}n&&!s&&e.$emit(\"node-drag-end\",n.node,null,r,i),t.showDropIndicator=!1,t.draggingNode=null,t.dropNode=null,t.allowDrop=!0})},mounted:function(){this.initTabIndex(),this.$el.addEventListener(\"keydown\",this.handleKeydown)},updated:function(){this.treeItems=this.$el.querySelectorAll(\"[role=treeitem]\"),this.checkboxItems=this.$el.querySelectorAll(\"input[type=checkbox]\")}},ko,[],!1,null,null,null);Vo.options.__file=\"packages/tree/src/tree.vue\";var Bo=Vo.exports;Bo.install=function(e){e.component(Bo.name,Bo)};var zo=Bo,Ho=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition\",{attrs:{name:\"el-alert-fade\"}},[i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.visible,expression:\"visible\"}],staticClass:\"el-alert\",class:[e.typeClass,e.center?\"is-center\":\"\",\"is-\"+e.effect],attrs:{role:\"alert\"}},[e.showIcon?i(\"i\",{staticClass:\"el-alert__icon\",class:[e.iconClass,e.isBigIcon]}):e._e(),i(\"div\",{staticClass:\"el-alert__content\"},[e.title||e.$slots.title?i(\"span\",{staticClass:\"el-alert__title\",class:[e.isBoldTitle]},[e._t(\"title\",[e._v(e._s(e.title))])],2):e._e(),e.$slots.default&&!e.description?i(\"p\",{staticClass:\"el-alert__description\"},[e._t(\"default\")],2):e._e(),e.description&&!e.$slots.default?i(\"p\",{staticClass:\"el-alert__description\"},[e._v(e._s(e.description))]):e._e(),i(\"i\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.closable,expression:\"closable\"}],staticClass:\"el-alert__closebtn\",class:{\"is-customed\":\"\"!==e.closeText,\"el-icon-close\":\"\"===e.closeText},on:{click:function(t){e.close()}}},[e._v(e._s(e.closeText))])])])])};Ho._withStripped=!0;var Ro={success:\"el-icon-success\",warning:\"el-icon-warning\",error:\"el-icon-error\"},Wo=r({name:\"ElAlert\",props:{title:{type:String,default:\"\"},description:{type:String,default:\"\"},type:{type:String,default:\"info\"},closable:{type:Boolean,default:!0},closeText:{type:String,default:\"\"},showIcon:Boolean,center:Boolean,effect:{type:String,default:\"light\",validator:function(e){return-1!==[\"light\",\"dark\"].indexOf(e)}}},data:function(){return{visible:!0}},methods:{close:function(){this.visible=!1,this.$emit(\"close\")}},computed:{typeClass:function(){return\"el-alert--\"+this.type},iconClass:function(){return Ro[this.type]||\"el-icon-info\"},isBigIcon:function(){return this.description||this.$slots.default?\"is-big\":\"\"},isBoldTitle:function(){return this.description||this.$slots.default?\"is-bold\":\"\"}}},Ho,[],!1,null,null,null);Wo.options.__file=\"packages/alert/src/main.vue\";var jo=Wo.exports;jo.install=function(e){e.component(jo.name,jo)};var qo=jo,Yo=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition\",{attrs:{name:\"el-notification-fade\"}},[i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.visible,expression:\"visible\"}],class:[\"el-notification\",e.customClass,e.horizontalClass],style:e.positionStyle,attrs:{role:\"alert\"},on:{mouseenter:function(t){e.clearTimer()},mouseleave:function(t){e.startTimer()},click:e.click}},[e.type||e.iconClass?i(\"i\",{staticClass:\"el-notification__icon\",class:[e.typeClass,e.iconClass]}):e._e(),i(\"div\",{staticClass:\"el-notification__group\",class:{\"is-with-icon\":e.typeClass||e.iconClass}},[i(\"h2\",{staticClass:\"el-notification__title\",domProps:{textContent:e._s(e.title)}}),i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.message,expression:\"message\"}],staticClass:\"el-notification__content\"},[e._t(\"default\",[e.dangerouslyUseHTMLString?i(\"p\",{domProps:{innerHTML:e._s(e.message)}}):i(\"p\",[e._v(e._s(e.message))])])],2),e.showClose?i(\"div\",{staticClass:\"el-notification__closeBtn el-icon-close\",on:{click:function(t){return t.stopPropagation(),e.close(t)}}}):e._e()])])])};Yo._withStripped=!0;var Ko={success:\"success\",info:\"info\",warning:\"warning\",error:\"error\"},Go=r({data:function(){return{visible:!1,title:\"\",message:\"\",duration:4500,type:\"\",showClose:!0,customClass:\"\",iconClass:\"\",onClose:null,onClick:null,closed:!1,verticalOffset:0,timer:null,dangerouslyUseHTMLString:!1,position:\"top-right\"}},computed:{typeClass:function(){return this.type&&Ko[this.type]?\"el-icon-\"+Ko[this.type]:\"\"},horizontalClass:function(){return this.position.indexOf(\"right\")>-1?\"right\":\"left\"},verticalProperty:function(){return/^top-/.test(this.position)?\"top\":\"bottom\"},positionStyle:function(){var e;return(e={})[this.verticalProperty]=this.verticalOffset+\"px\",e}},watch:{closed:function(e){e&&(this.visible=!1,this.$el.addEventListener(\"transitionend\",this.destroyElement))}},methods:{destroyElement:function(){this.$el.removeEventListener(\"transitionend\",this.destroyElement),this.$destroy(!0),this.$el.parentNode.removeChild(this.$el)},click:function(){\"function\"==typeof this.onClick&&this.onClick()},close:function(){this.closed=!0,\"function\"==typeof this.onClose&&this.onClose()},clearTimer:function(){clearTimeout(this.timer)},startTimer:function(){var e=this;this.duration>0&&(this.timer=setTimeout(function(){e.closed||e.close()},this.duration))},keydown:function(e){46===e.keyCode||8===e.keyCode?this.clearTimer():27===e.keyCode?this.closed||this.close():this.startTimer()}},mounted:function(){var e=this;this.duration>0&&(this.timer=setTimeout(function(){e.closed||e.close()},this.duration)),document.addEventListener(\"keydown\",this.keydown)},beforeDestroy:function(){document.removeEventListener(\"keydown\",this.keydown)}},Yo,[],!1,null,null,null);Go.options.__file=\"packages/notification/src/main.vue\";var Uo=Go.exports,Xo=h.a.extend(Uo),Jo=void 0,Zo=[],Qo=1,el=function e(t){if(!h.a.prototype.$isServer){var i=(t=Z({},t)).onClose,n=\"notification_\"+Qo++,r=t.position||\"top-right\";t.onClose=function(){e.close(n,i)},Jo=new Xo({data:t}),ua(t.message)&&(Jo.$slots.default=[t.message],t.message=\"REPLACED_BY_VNODE\"),Jo.id=n,Jo.$mount(),document.body.appendChild(Jo.$el),Jo.visible=!0,Jo.dom=Jo.$el,Jo.dom.style.zIndex=Se.nextZIndex();var s=t.offset||0;return Zo.filter(function(e){return e.position===r}).forEach(function(e){s+=e.$el.offsetHeight+16}),s+=16,Jo.verticalOffset=s,Zo.push(Jo),Jo}};[\"success\",\"warning\",\"info\",\"error\"].forEach(function(e){el[e]=function(t){return(\"string\"==typeof t||ua(t))&&(t={message:t}),t.type=e,el(t)}}),el.close=function(e,t){var i=-1,n=Zo.length,r=Zo.filter(function(t,n){return t.id===e&&(i=n,!0)})[0];if(r&&(\"function\"==typeof t&&t(r),Zo.splice(i,1),!(n<=1)))for(var s=r.position,a=r.dom.offsetHeight,o=i;o=0;e--)Zo[e].close()};var tl=el,il=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-slider\",class:{\"is-vertical\":e.vertical,\"el-slider--with-input\":e.showInput},attrs:{role:\"slider\",\"aria-valuemin\":e.min,\"aria-valuemax\":e.max,\"aria-orientation\":e.vertical?\"vertical\":\"horizontal\",\"aria-disabled\":e.sliderDisabled}},[e.showInput&&!e.range?i(\"el-input-number\",{ref:\"input\",staticClass:\"el-slider__input\",attrs:{step:e.step,disabled:e.sliderDisabled,controls:e.showInputControls,min:e.min,max:e.max,debounce:e.debounce,size:e.inputSize},on:{change:e.emitChange},model:{value:e.firstValue,callback:function(t){e.firstValue=t},expression:\"firstValue\"}}):e._e(),i(\"div\",{ref:\"slider\",staticClass:\"el-slider__runway\",class:{\"show-input\":e.showInput,disabled:e.sliderDisabled},style:e.runwayStyle,on:{click:e.onSliderClick}},[i(\"div\",{staticClass:\"el-slider__bar\",style:e.barStyle}),i(\"slider-button\",{ref:\"button1\",attrs:{vertical:e.vertical,\"tooltip-class\":e.tooltipClass},model:{value:e.firstValue,callback:function(t){e.firstValue=t},expression:\"firstValue\"}}),e.range?i(\"slider-button\",{ref:\"button2\",attrs:{vertical:e.vertical,\"tooltip-class\":e.tooltipClass},model:{value:e.secondValue,callback:function(t){e.secondValue=t},expression:\"secondValue\"}}):e._e(),e._l(e.stops,function(t,n){return e.showStops?i(\"div\",{key:n,staticClass:\"el-slider__stop\",style:e.getStopStyle(t)}):e._e()}),e.markList.length>0?[i(\"div\",e._l(e.markList,function(t,n){return i(\"div\",{key:n,staticClass:\"el-slider__stop el-slider__marks-stop\",style:e.getStopStyle(t.position)})}),0),i(\"div\",{staticClass:\"el-slider__marks\"},e._l(e.markList,function(t,n){return i(\"slider-marker\",{key:n,style:e.getStopStyle(t.position),attrs:{mark:t.mark}})}),1)]:e._e()],2)],1)};il._withStripped=!0;var nl=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{ref:\"button\",staticClass:\"el-slider__button-wrapper\",class:{hover:e.hovering,dragging:e.dragging},style:e.wrapperStyle,attrs:{tabindex:\"0\"},on:{mouseenter:e.handleMouseEnter,mouseleave:e.handleMouseLeave,mousedown:e.onButtonDown,touchstart:e.onButtonDown,focus:e.handleMouseEnter,blur:e.handleMouseLeave,keydown:[function(t){return\"button\"in t||!e._k(t.keyCode,\"left\",37,t.key,[\"Left\",\"ArrowLeft\"])?\"button\"in t&&0!==t.button?null:e.onLeftKeyDown(t):null},function(t){return\"button\"in t||!e._k(t.keyCode,\"right\",39,t.key,[\"Right\",\"ArrowRight\"])?\"button\"in t&&2!==t.button?null:e.onRightKeyDown(t):null},function(t){return\"button\"in t||!e._k(t.keyCode,\"down\",40,t.key,[\"Down\",\"ArrowDown\"])?(t.preventDefault(),e.onLeftKeyDown(t)):null},function(t){return\"button\"in t||!e._k(t.keyCode,\"up\",38,t.key,[\"Up\",\"ArrowUp\"])?(t.preventDefault(),e.onRightKeyDown(t)):null}]}},[i(\"el-tooltip\",{ref:\"tooltip\",attrs:{placement:\"top\",\"popper-class\":e.tooltipClass,disabled:!e.showTooltip}},[i(\"span\",{attrs:{slot:\"content\"},slot:\"content\"},[e._v(e._s(e.formatValue))]),i(\"div\",{staticClass:\"el-slider__button\",class:{hover:e.hovering,dragging:e.dragging}})])],1)};nl._withStripped=!0;var rl=r({name:\"ElSliderButton\",components:{ElTooltip:ui},props:{value:{type:Number,default:0},vertical:{type:Boolean,default:!1},tooltipClass:String},data:function(){return{hovering:!1,dragging:!1,isClick:!1,startX:0,currentX:0,startY:0,currentY:0,startPosition:0,newPosition:null,oldValue:this.value}},computed:{disabled:function(){return this.$parent.sliderDisabled},max:function(){return this.$parent.max},min:function(){return this.$parent.min},step:function(){return this.$parent.step},showTooltip:function(){return this.$parent.showTooltip},precision:function(){return this.$parent.precision},currentPosition:function(){return(this.value-this.min)/(this.max-this.min)*100+\"%\"},enableFormat:function(){return this.$parent.formatTooltip instanceof Function},formatValue:function(){return this.enableFormat&&this.$parent.formatTooltip(this.value)||this.value},wrapperStyle:function(){return this.vertical?{bottom:this.currentPosition}:{left:this.currentPosition}}},watch:{dragging:function(e){this.$parent.dragging=e}},methods:{displayTooltip:function(){this.$refs.tooltip&&(this.$refs.tooltip.showPopper=!0)},hideTooltip:function(){this.$refs.tooltip&&(this.$refs.tooltip.showPopper=!1)},handleMouseEnter:function(){this.hovering=!0,this.displayTooltip()},handleMouseLeave:function(){this.hovering=!1,this.hideTooltip()},onButtonDown:function(e){this.disabled||(e.preventDefault(),this.onDragStart(e),window.addEventListener(\"mousemove\",this.onDragging),window.addEventListener(\"touchmove\",this.onDragging),window.addEventListener(\"mouseup\",this.onDragEnd),window.addEventListener(\"touchend\",this.onDragEnd),window.addEventListener(\"contextmenu\",this.onDragEnd))},onLeftKeyDown:function(){this.disabled||(this.newPosition=parseFloat(this.currentPosition)-this.step/(this.max-this.min)*100,this.setPosition(this.newPosition),this.$parent.emitChange())},onRightKeyDown:function(){this.disabled||(this.newPosition=parseFloat(this.currentPosition)+this.step/(this.max-this.min)*100,this.setPosition(this.newPosition),this.$parent.emitChange())},onDragStart:function(e){this.dragging=!0,this.isClick=!0,\"touchstart\"===e.type&&(e.clientY=e.touches[0].clientY,e.clientX=e.touches[0].clientX),this.vertical?this.startY=e.clientY:this.startX=e.clientX,this.startPosition=parseFloat(this.currentPosition),this.newPosition=this.startPosition},onDragging:function(e){if(this.dragging){this.isClick=!1,this.displayTooltip(),this.$parent.resetSize();var t=0;\"touchmove\"===e.type&&(e.clientY=e.touches[0].clientY,e.clientX=e.touches[0].clientX),this.vertical?(this.currentY=e.clientY,t=(this.startY-this.currentY)/this.$parent.sliderSize*100):(this.currentX=e.clientX,t=(this.currentX-this.startX)/this.$parent.sliderSize*100),this.newPosition=this.startPosition+t,this.setPosition(this.newPosition)}},onDragEnd:function(){var e=this;this.dragging&&(setTimeout(function(){e.dragging=!1,e.hideTooltip(),e.isClick||(e.setPosition(e.newPosition),e.$parent.emitChange())},0),window.removeEventListener(\"mousemove\",this.onDragging),window.removeEventListener(\"touchmove\",this.onDragging),window.removeEventListener(\"mouseup\",this.onDragEnd),window.removeEventListener(\"touchend\",this.onDragEnd),window.removeEventListener(\"contextmenu\",this.onDragEnd))},setPosition:function(e){var t=this;if(null!==e&&!isNaN(e)){e<0?e=0:e>100&&(e=100);var i=100/((this.max-this.min)/this.step),n=Math.round(e/i)*i*(this.max-this.min)*.01+this.min;n=parseFloat(n.toFixed(this.precision)),this.$emit(\"input\",n),this.$nextTick(function(){t.displayTooltip(),t.$refs.tooltip&&t.$refs.tooltip.updatePopper()}),this.dragging||this.value===this.oldValue||(this.oldValue=this.value)}}}},nl,[],!1,null,null,null);rl.options.__file=\"packages/slider/src/button.vue\";var sl=rl.exports,al={name:\"ElMarker\",props:{mark:{type:[String,Object]}},render:function(){var e=arguments[0],t=\"string\"==typeof this.mark?this.mark:this.mark.label;return e(\"div\",{class:\"el-slider__marks-text\",style:this.mark.style||{}},[t])}},ol=r({name:\"ElSlider\",mixins:[l],inject:{elForm:{default:\"\"}},props:{min:{type:Number,default:0},max:{type:Number,default:100},step:{type:Number,default:1},value:{type:[Number,Array],default:0},showInput:{type:Boolean,default:!1},showInputControls:{type:Boolean,default:!0},inputSize:{type:String,default:\"small\"},showStops:{type:Boolean,default:!1},showTooltip:{type:Boolean,default:!0},formatTooltip:Function,disabled:{type:Boolean,default:!1},range:{type:Boolean,default:!1},vertical:{type:Boolean,default:!1},height:{type:String},debounce:{type:Number,default:300},label:{type:String},tooltipClass:String,marks:Object},components:{ElInputNumber:_i,SliderButton:sl,SliderMarker:al},data:function(){return{firstValue:null,secondValue:null,oldValue:null,dragging:!1,sliderSize:1}},watch:{value:function(e,t){this.dragging||Array.isArray(e)&&Array.isArray(t)&&e.every(function(e,i){return e===t[i]})||this.setValues()},dragging:function(e){e||this.setValues()},firstValue:function(e){this.range?this.$emit(\"input\",[this.minValue,this.maxValue]):this.$emit(\"input\",e)},secondValue:function(){this.range&&this.$emit(\"input\",[this.minValue,this.maxValue])},min:function(){this.setValues()},max:function(){this.setValues()}},methods:{valueChanged:function(){var e=this;return this.range?![this.minValue,this.maxValue].every(function(t,i){return t===e.oldValue[i]}):this.value!==this.oldValue},setValues:function(){if(this.min>this.max)console.error(\"[Element Error][Slider]min should not be greater than max.\");else{var e=this.value;this.range&&Array.isArray(e)?e[1]this.max?this.$emit(\"input\",[this.max,this.max]):e[0]this.max?this.$emit(\"input\",[e[0],this.max]):(this.firstValue=e[0],this.secondValue=e[1],this.valueChanged()&&(this.dispatch(\"ElFormItem\",\"el.form.change\",[this.minValue,this.maxValue]),this.oldValue=e.slice())):this.range||\"number\"!=typeof e||isNaN(e)||(ethis.max?this.$emit(\"input\",this.max):(this.firstValue=e,this.valueChanged()&&(this.dispatch(\"ElFormItem\",\"el.form.change\",e),this.oldValue=e)))}},setPosition:function(e){var t=this.min+e*(this.max-this.min)/100;if(this.range){var i=void 0;i=Math.abs(this.minValue-t)this.secondValue?\"button1\":\"button2\",this.$refs[i].setPosition(e)}else this.$refs.button1.setPosition(e)},onSliderClick:function(e){if(!this.sliderDisabled&&!this.dragging){if(this.resetSize(),this.vertical){var t=this.$refs.slider.getBoundingClientRect().bottom;this.setPosition((t-e.clientY)/this.sliderSize*100)}else{var i=this.$refs.slider.getBoundingClientRect().left;this.setPosition((e.clientX-i)/this.sliderSize*100)}this.emitChange()}},resetSize:function(){this.$refs.slider&&(this.sliderSize=this.$refs.slider[\"client\"+(this.vertical?\"Height\":\"Width\")])},emitChange:function(){var e=this;this.$nextTick(function(){e.$emit(\"change\",e.range?[e.minValue,e.maxValue]:e.value)})},getStopStyle:function(e){return this.vertical?{bottom:e+\"%\"}:{left:e+\"%\"}}},computed:{stops:function(){var e=this;if(!this.showStops||this.min>this.max)return[];if(0===this.step)return[];for(var t=(this.max-this.min)/this.step,i=100*this.step/(this.max-this.min),n=[],r=1;r100*(e.maxValue-e.min)/(e.max-e.min)}):n.filter(function(t){return t>100*(e.firstValue-e.min)/(e.max-e.min)})},markList:function(){var e=this;return this.marks?Object.keys(this.marks).map(parseFloat).sort(function(e,t){return e-t}).filter(function(t){return t<=e.max&&t>=e.min}).map(function(t){return{point:t,position:100*(t-e.min)/(e.max-e.min),mark:e.marks[t]}}):[]},minValue:function(){return Math.min(this.firstValue,this.secondValue)},maxValue:function(){return Math.max(this.firstValue,this.secondValue)},barSize:function(){return this.range?100*(this.maxValue-this.minValue)/(this.max-this.min)+\"%\":100*(this.firstValue-this.min)/(this.max-this.min)+\"%\"},barStart:function(){return this.range?100*(this.minValue-this.min)/(this.max-this.min)+\"%\":\"0%\"},precision:function(){var e=[this.min,this.max,this.step].map(function(e){var t=(\"\"+e).split(\".\")[1];return t?t.length:0});return Math.max.apply(null,e)},runwayStyle:function(){return this.vertical?{height:this.height}:{}},barStyle:function(){return this.vertical?{height:this.barSize,bottom:this.barStart}:{width:this.barSize,left:this.barStart}},sliderDisabled:function(){return this.disabled||(this.elForm||{}).disabled}},mounted:function(){var e=void 0;this.range?(Array.isArray(this.value)?(this.firstValue=Math.max(this.min,this.value[0]),this.secondValue=Math.min(this.max,this.value[1])):(this.firstValue=this.min,this.secondValue=this.max),this.oldValue=[this.firstValue,this.secondValue],e=this.firstValue+\"-\"+this.secondValue):(\"number\"!=typeof this.value||isNaN(this.value)?this.firstValue=this.min:this.firstValue=Math.min(this.max,Math.max(this.min,this.value)),this.oldValue=this.firstValue,e=this.firstValue),this.$el.setAttribute(\"aria-valuetext\",e),this.$el.setAttribute(\"aria-label\",this.label?this.label:\"slider between \"+this.min+\" and \"+this.max),this.resetSize(),window.addEventListener(\"resize\",this.resetSize)},beforeDestroy:function(){window.removeEventListener(\"resize\",this.resetSize)}},il,[],!1,null,null,null);ol.options.__file=\"packages/slider/src/main.vue\";var ll=ol.exports;ll.install=function(e){e.component(ll.name,ll)};var ul=ll,cl=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition\",{attrs:{name:\"el-loading-fade\"},on:{\"after-leave\":e.handleAfterLeave}},[i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.visible,expression:\"visible\"}],staticClass:\"el-loading-mask\",class:[e.customClass,{\"is-fullscreen\":e.fullscreen}],style:{backgroundColor:e.background||\"\"}},[i(\"div\",{staticClass:\"el-loading-spinner\"},[e.spinner?i(\"i\",{class:e.spinner}):i(\"svg\",{staticClass:\"circular\",attrs:{viewBox:\"25 25 50 50\"}},[i(\"circle\",{staticClass:\"path\",attrs:{cx:\"50\",cy:\"50\",r:\"20\",fill:\"none\"}})]),e.text?i(\"p\",{staticClass:\"el-loading-text\"},[e._v(e._s(e.text))]):e._e()])])])};cl._withStripped=!0;var hl=r({data:function(){return{text:null,spinner:null,background:null,fullscreen:!0,visible:!1,customClass:\"\"}},methods:{handleAfterLeave:function(){this.$emit(\"after-leave\")},setText:function(e){this.text=e}}},cl,[],!1,null,null,null);hl.options.__file=\"packages/loading/src/loading.vue\";var dl=hl.exports,pl=function(e,t){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:300,n=arguments.length>3&&void 0!==arguments[3]&&arguments[3];if(!e||!t)throw new Error(\"instance & callback is required\");var r=!1,s=function(){r||(r=!0,t&&t.apply(null,arguments))};n?e.$once(\"after-leave\",s):e.$on(\"after-leave\",s),setTimeout(function(){s()},i+100)},fl=h.a.extend(dl),ml={install:function(e){if(!e.prototype.$isServer){var t=function(t,n){n.value?e.nextTick(function(){n.modifiers.fullscreen?(t.originalPosition=ve(document.body,\"position\"),t.originalOverflow=ve(document.body,\"overflow\"),t.maskStyle.zIndex=Se.nextZIndex(),fe(t.mask,\"is-fullscreen\"),i(document.body,t,n)):(me(t.mask,\"is-fullscreen\"),n.modifiers.body?(t.originalPosition=ve(document.body,\"position\"),[\"top\",\"left\"].forEach(function(e){var i=\"top\"===e?\"scrollTop\":\"scrollLeft\";t.maskStyle[e]=t.getBoundingClientRect()[e]+document.body[i]+document.documentElement[i]-parseInt(ve(document.body,\"margin-\"+e),10)+\"px\"}),[\"height\",\"width\"].forEach(function(e){t.maskStyle[e]=t.getBoundingClientRect()[e]+\"px\"}),i(document.body,t,n)):(t.originalPosition=ve(t,\"position\"),i(t,t,n)))}):(pl(t.instance,function(e){if(t.instance.hiding){t.domVisible=!1;var i=n.modifiers.fullscreen||n.modifiers.body?document.body:t;me(i,\"el-loading-parent--relative\"),me(i,\"el-loading-parent--hidden\"),t.instance.hiding=!1}},300,!0),t.instance.visible=!1,t.instance.hiding=!0)},i=function(t,i,n){i.domVisible||\"none\"===ve(i,\"display\")||\"hidden\"===ve(i,\"visibility\")?i.domVisible&&!0===i.instance.hiding&&(i.instance.visible=!0,i.instance.hiding=!1):(Object.keys(i.maskStyle).forEach(function(e){i.mask.style[e]=i.maskStyle[e]}),\"absolute\"!==i.originalPosition&&\"fixed\"!==i.originalPosition&&fe(t,\"el-loading-parent--relative\"),n.modifiers.fullscreen&&n.modifiers.lock&&fe(t,\"el-loading-parent--hidden\"),i.domVisible=!0,t.appendChild(i.mask),e.nextTick(function(){i.instance.hiding?i.instance.$emit(\"after-leave\"):i.instance.visible=!0}),i.domInserted=!0)};e.directive(\"loading\",{bind:function(e,i,n){var r=e.getAttribute(\"element-loading-text\"),s=e.getAttribute(\"element-loading-spinner\"),a=e.getAttribute(\"element-loading-background\"),o=e.getAttribute(\"element-loading-custom-class\"),l=n.context,u=new fl({el:document.createElement(\"div\"),data:{text:l&&l[r]||r,spinner:l&&l[s]||s,background:l&&l[a]||a,customClass:l&&l[o]||o,fullscreen:!!i.modifiers.fullscreen}});e.instance=u,e.mask=u.$el,e.maskStyle={},i.value&&t(e,i)},update:function(e,i){e.instance.setText(e.getAttribute(\"element-loading-text\")),i.oldValue!==i.value&&t(e,i)},unbind:function(e,i){e.domInserted&&(e.mask&&e.mask.parentNode&&e.mask.parentNode.removeChild(e.mask),t(e,{value:!1,modifiers:i.modifiers})),e.instance&&e.instance.$destroy()}})}}},vl=ml,gl=h.a.extend(dl),bl={text:null,fullscreen:!0,body:!1,lock:!1,customClass:\"\"},yl=void 0;gl.prototype.originalPosition=\"\",gl.prototype.originalOverflow=\"\",gl.prototype.close=function(){var e=this;this.fullscreen&&(yl=void 0),pl(this,function(t){var i=e.fullscreen||e.body?document.body:e.target;me(i,\"el-loading-parent--relative\"),me(i,\"el-loading-parent--hidden\"),e.$el&&e.$el.parentNode&&e.$el.parentNode.removeChild(e.$el),e.$destroy()},300),this.visible=!1};var wl=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!h.a.prototype.$isServer){if(\"string\"==typeof(e=Z({},bl,e)).target&&(e.target=document.querySelector(e.target)),e.target=e.target||document.body,e.target!==document.body?e.fullscreen=!1:e.body=!0,e.fullscreen&&yl)return yl;var t=e.body?document.body:e.target,i=new gl({el:document.createElement(\"div\"),data:e});return function(e,t,i){var n={};e.fullscreen?(i.originalPosition=ve(document.body,\"position\"),i.originalOverflow=ve(document.body,\"overflow\"),n.zIndex=Se.nextZIndex()):e.body?(i.originalPosition=ve(document.body,\"position\"),[\"top\",\"left\"].forEach(function(t){var i=\"top\"===t?\"scrollTop\":\"scrollLeft\";n[t]=e.target.getBoundingClientRect()[t]+document.body[i]+document.documentElement[i]+\"px\"}),[\"height\",\"width\"].forEach(function(t){n[t]=e.target.getBoundingClientRect()[t]+\"px\"})):i.originalPosition=ve(t,\"position\"),Object.keys(n).forEach(function(e){i.$el.style[e]=n[e]})}(e,t,i),\"absolute\"!==i.originalPosition&&\"fixed\"!==i.originalPosition&&fe(t,\"el-loading-parent--relative\"),e.fullscreen&&e.lock&&fe(t,\"el-loading-parent--hidden\"),t.appendChild(i.$el),h.a.nextTick(function(){i.visible=!0}),e.fullscreen&&(yl=i),i}},_l={install:function(e){e.use(vl),e.prototype.$loading=wl},directive:vl,service:wl},xl=function(){var e=this.$createElement;return(this._self._c||e)(\"i\",{class:\"el-icon-\"+this.name})};xl._withStripped=!0;var Cl=r({name:\"ElIcon\",props:{name:String}},xl,[],!1,null,null,null);Cl.options.__file=\"packages/icon/src/icon.vue\";var kl=Cl.exports;kl.install=function(e){e.component(kl.name,kl)};var Sl=kl,Dl={name:\"ElRow\",componentName:\"ElRow\",props:{tag:{type:String,default:\"div\"},gutter:Number,type:String,justify:{type:String,default:\"start\"},align:{type:String,default:\"top\"}},computed:{style:function(){var e={};return this.gutter&&(e.marginLeft=\"-\"+this.gutter/2+\"px\",e.marginRight=e.marginLeft),e}},render:function(e){return e(this.tag,{class:[\"el-row\",\"start\"!==this.justify?\"is-justify-\"+this.justify:\"\",\"top\"!==this.align?\"is-align-\"+this.align:\"\",{\"el-row--flex\":\"flex\"===this.type}],style:this.style},this.$slots.default)},install:function(e){e.component(Dl.name,Dl)}},$l=Dl,El=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e},Tl={name:\"ElCol\",props:{span:{type:Number,default:24},tag:{type:String,default:\"div\"},offset:Number,pull:Number,push:Number,xs:[Number,Object],sm:[Number,Object],md:[Number,Object],lg:[Number,Object],xl:[Number,Object]},computed:{gutter:function(){for(var e=this.$parent;e&&\"ElRow\"!==e.$options.componentName;)e=e.$parent;return e?e.gutter:0}},render:function(e){var t=this,i=[],n={};return this.gutter&&(n.paddingLeft=this.gutter/2+\"px\",n.paddingRight=n.paddingLeft),[\"span\",\"offset\",\"pull\",\"push\"].forEach(function(e){(t[e]||0===t[e])&&i.push(\"span\"!==e?\"el-col-\"+e+\"-\"+t[e]:\"el-col-\"+t[e])}),[\"xs\",\"sm\",\"md\",\"lg\",\"xl\"].forEach(function(e){if(\"number\"==typeof t[e])i.push(\"el-col-\"+e+\"-\"+t[e]);else if(\"object\"===El(t[e])){var n=t[e];Object.keys(n).forEach(function(t){i.push(\"span\"!==t?\"el-col-\"+e+\"-\"+t+\"-\"+n[t]:\"el-col-\"+e+\"-\"+n[t])})}}),e(this.tag,{class:[\"el-col\",i],style:n},this.$slots.default)},install:function(e){e.component(Tl.name,Tl)}},Ml=Tl,Nl=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition-group\",{class:[\"el-upload-list\",\"el-upload-list--\"+e.listType,{\"is-disabled\":e.disabled}],attrs:{tag:\"ul\",name:\"el-list\"}},e._l(e.files,function(t){return i(\"li\",{key:t.uid,class:[\"el-upload-list__item\",\"is-\"+t.status,e.focusing?\"focusing\":\"\"],attrs:{tabindex:\"0\"},on:{keydown:function(i){if(!(\"button\"in i)&&e._k(i.keyCode,\"delete\",[8,46],i.key,[\"Backspace\",\"Delete\",\"Del\"]))return null;!e.disabled&&e.$emit(\"remove\",t)},focus:function(t){e.focusing=!0},blur:function(t){e.focusing=!1},click:function(t){e.focusing=!1}}},[e._t(\"default\",[\"uploading\"!==t.status&&[\"picture-card\",\"picture\"].indexOf(e.listType)>-1?i(\"img\",{staticClass:\"el-upload-list__item-thumbnail\",attrs:{src:t.url,alt:\"\"}}):e._e(),i(\"a\",{staticClass:\"el-upload-list__item-name\",on:{click:function(i){e.handleClick(t)}}},[i(\"i\",{staticClass:\"el-icon-document\"}),e._v(e._s(t.name)+\"\\n \")]),i(\"label\",{staticClass:\"el-upload-list__item-status-label\"},[i(\"i\",{class:{\"el-icon-upload-success\":!0,\"el-icon-circle-check\":\"text\"===e.listType,\"el-icon-check\":[\"picture-card\",\"picture\"].indexOf(e.listType)>-1}})]),e.disabled?e._e():i(\"i\",{staticClass:\"el-icon-close\",on:{click:function(i){e.$emit(\"remove\",t)}}}),e.disabled?e._e():i(\"i\",{staticClass:\"el-icon-close-tip\"},[e._v(e._s(e.t(\"el.upload.deleteTip\")))]),\"uploading\"===t.status?i(\"el-progress\",{attrs:{type:\"picture-card\"===e.listType?\"circle\":\"line\",\"stroke-width\":\"picture-card\"===e.listType?6:2,percentage:e.parsePercentage(t.percentage)}}):e._e(),\"picture-card\"===e.listType?i(\"span\",{staticClass:\"el-upload-list__item-actions\"},[e.handlePreview&&\"picture-card\"===e.listType?i(\"span\",{staticClass:\"el-upload-list__item-preview\",on:{click:function(i){e.handlePreview(t)}}},[i(\"i\",{staticClass:\"el-icon-zoom-in\"})]):e._e(),e.disabled?e._e():i(\"span\",{staticClass:\"el-upload-list__item-delete\",on:{click:function(i){e.$emit(\"remove\",t)}}},[i(\"i\",{staticClass:\"el-icon-delete\"})])]):e._e()],{file:t})],2)}),0)};Nl._withStripped=!0;var Pl=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-progress\",class:[\"el-progress--\"+e.type,e.status?\"is-\"+e.status:\"\",{\"el-progress--without-text\":!e.showText,\"el-progress--text-inside\":e.textInside}],attrs:{role:\"progressbar\",\"aria-valuenow\":e.percentage,\"aria-valuemin\":\"0\",\"aria-valuemax\":\"100\"}},[\"line\"===e.type?i(\"div\",{staticClass:\"el-progress-bar\"},[i(\"div\",{staticClass:\"el-progress-bar__outer\",style:{height:e.strokeWidth+\"px\"}},[i(\"div\",{staticClass:\"el-progress-bar__inner\",style:e.barStyle},[e.showText&&e.textInside?i(\"div\",{staticClass:\"el-progress-bar__innerText\"},[e._v(e._s(e.content))]):e._e()])])]):i(\"div\",{staticClass:\"el-progress-circle\",style:{height:e.width+\"px\",width:e.width+\"px\"}},[i(\"svg\",{attrs:{viewBox:\"0 0 100 100\"}},[i(\"path\",{staticClass:\"el-progress-circle__track\",style:e.trailPathStyle,attrs:{d:e.trackPath,stroke:\"#e5e9f2\",\"stroke-width\":e.relativeStrokeWidth,fill:\"none\"}}),i(\"path\",{staticClass:\"el-progress-circle__path\",style:e.circlePathStyle,attrs:{d:e.trackPath,stroke:e.stroke,fill:\"none\",\"stroke-linecap\":e.strokeLinecap,\"stroke-width\":e.percentage?e.relativeStrokeWidth:0}})])]),e.showText&&!e.textInside?i(\"div\",{staticClass:\"el-progress__text\",style:{fontSize:e.progressTextSize+\"px\"}},[e.status?i(\"i\",{class:e.iconClass}):[e._v(e._s(e.content))]],2):e._e()])};Pl._withStripped=!0;var Ol=r({name:\"ElProgress\",props:{type:{type:String,default:\"line\",validator:function(e){return[\"line\",\"circle\",\"dashboard\"].indexOf(e)>-1}},percentage:{type:Number,default:0,required:!0,validator:function(e){return e>=0&&e<=100}},status:{type:String,validator:function(e){return[\"success\",\"exception\",\"warning\"].indexOf(e)>-1}},strokeWidth:{type:Number,default:6},strokeLinecap:{type:String,default:\"round\"},textInside:{type:Boolean,default:!1},width:{type:Number,default:126},showText:{type:Boolean,default:!0},color:{type:[String,Array,Function],default:\"\"},format:Function},computed:{barStyle:function(){var e={};return e.width=this.percentage+\"%\",e.backgroundColor=this.getCurrentColor(this.percentage),e},relativeStrokeWidth:function(){return(this.strokeWidth/this.width*100).toFixed(1)},radius:function(){return\"circle\"===this.type||\"dashboard\"===this.type?parseInt(50-parseFloat(this.relativeStrokeWidth)/2,10):0},trackPath:function(){var e=this.radius,t=\"dashboard\"===this.type;return\"\\n M 50 50\\n m 0 \"+(t?\"\":\"-\")+e+\"\\n a \"+e+\" \"+e+\" 0 1 1 0 \"+(t?\"-\":\"\")+2*e+\"\\n a \"+e+\" \"+e+\" 0 1 1 0 \"+(t?\"\":\"-\")+2*e+\"\\n \"},perimeter:function(){return 2*Math.PI*this.radius},rate:function(){return\"dashboard\"===this.type?.75:1},strokeDashoffset:function(){return-1*this.perimeter*(1-this.rate)/2+\"px\"},trailPathStyle:function(){return{strokeDasharray:this.perimeter*this.rate+\"px, \"+this.perimeter+\"px\",strokeDashoffset:this.strokeDashoffset}},circlePathStyle:function(){return{strokeDasharray:this.perimeter*this.rate*(this.percentage/100)+\"px, \"+this.perimeter+\"px\",strokeDashoffset:this.strokeDashoffset,transition:\"stroke-dasharray 0.6s ease 0s, stroke 0.6s ease\"}},stroke:function(){var e=void 0;if(this.color)e=this.getCurrentColor(this.percentage);else switch(this.status){case\"success\":e=\"#13ce66\";break;case\"exception\":e=\"#ff4949\";break;case\"warning\":e=\"#e6a23c\";break;default:e=\"#20a0ff\"}return e},iconClass:function(){return\"warning\"===this.status?\"el-icon-warning\":\"line\"===this.type?\"success\"===this.status?\"el-icon-circle-check\":\"el-icon-circle-close\":\"success\"===this.status?\"el-icon-check\":\"el-icon-close\"},progressTextSize:function(){return\"line\"===this.type?12+.4*this.strokeWidth:.111111*this.width+2},content:function(){return\"function\"==typeof this.format?this.format(this.percentage)||\"\":this.percentage+\"%\"}},methods:{getCurrentColor:function(e){return\"function\"==typeof this.color?this.color(e):\"string\"==typeof this.color?this.color:this.getLevelColor(e)},getLevelColor:function(e){for(var t=this.getColorArray().sort(function(e,t){return e.percentage-t.percentage}),i=0;ie)return t[i].color;return t[t.length-1].color},getColorArray:function(){var e=this.color,t=100/e.length;return e.map(function(e,i){return\"string\"==typeof e?{color:e,progress:(i+1)*t}:e})}}},Pl,[],!1,null,null,null);Ol.options.__file=\"packages/progress/src/progress.vue\";var Il=Ol.exports;Il.install=function(e){e.component(Il.name,Il)};var Al=Il,Fl=r({name:\"ElUploadList\",mixins:[q],data:function(){return{focusing:!1}},components:{ElProgress:Al},props:{files:{type:Array,default:function(){return[]}},disabled:{type:Boolean,default:!1},handlePreview:Function,listType:String},methods:{parsePercentage:function(e){return parseInt(e,10)},handleClick:function(e){this.handlePreview&&this.handlePreview(e)}}},Nl,[],!1,null,null,null);Fl.options.__file=\"packages/upload/src/upload-list.vue\";var Ll=Fl.exports,Vl=i(6),Bl=i.n(Vl);var zl=function(){var e=this,t=e.$createElement;return(e._self._c||t)(\"div\",{staticClass:\"el-upload-dragger\",class:{\"is-dragover\":e.dragover},on:{drop:function(t){return t.preventDefault(),e.onDrop(t)},dragover:function(t){return t.preventDefault(),e.onDragover(t)},dragleave:function(t){t.preventDefault(),e.dragover=!1}}},[e._t(\"default\")],2)};zl._withStripped=!0;var Hl=r({name:\"ElUploadDrag\",props:{disabled:Boolean},inject:{uploader:{default:\"\"}},data:function(){return{dragover:!1}},methods:{onDragover:function(){this.disabled||(this.dragover=!0)},onDrop:function(e){if(!this.disabled&&this.uploader){var t=this.uploader.accept;this.dragover=!1,t?this.$emit(\"file\",[].slice.call(e.dataTransfer.files).filter(function(e){var i=e.type,n=e.name,r=n.indexOf(\".\")>-1?\".\"+n.split(\".\").pop():\"\",s=i.replace(/\\/.*$/,\"\");return t.split(\",\").map(function(e){return e.trim()}).filter(function(e){return e}).some(function(e){return/\\..+$/.test(e)?r===e:/\\/\\*$/.test(e)?s===e.replace(/\\/\\*$/,\"\"):!!/^[^\\/]+\\/[^\\/]+$/.test(e)&&i===e})})):this.$emit(\"file\",e.dataTransfer.files)}}}},zl,[],!1,null,null,null);Hl.options.__file=\"packages/upload/src/upload-dragger.vue\";var Rl=r({inject:[\"uploader\"],components:{UploadDragger:Hl.exports},props:{type:String,action:{type:String,required:!0},name:{type:String,default:\"file\"},data:Object,headers:Object,withCredentials:Boolean,multiple:Boolean,accept:String,onStart:Function,onProgress:Function,onSuccess:Function,onError:Function,beforeUpload:Function,drag:Boolean,onPreview:{type:Function,default:function(){}},onRemove:{type:Function,default:function(){}},fileList:Array,autoUpload:Boolean,listType:String,httpRequest:{type:Function,default:function(e){if(\"undefined\"!=typeof XMLHttpRequest){var t=new XMLHttpRequest,i=e.action;t.upload&&(t.upload.onprogress=function(t){t.total>0&&(t.percent=t.loaded/t.total*100),e.onProgress(t)});var n=new FormData;e.data&&Object.keys(e.data).forEach(function(t){n.append(t,e.data[t])}),n.append(e.filename,e.file,e.file.name),t.onerror=function(t){e.onError(t)},t.onload=function(){if(t.status<200||t.status>=300)return e.onError(function(e,t,i){var n=void 0;n=i.response?\"\"+(i.response.error||i.response):i.responseText?\"\"+i.responseText:\"fail to post \"+e+\" \"+i.status;var r=new Error(n);return r.status=i.status,r.method=\"post\",r.url=e,r}(i,0,t));e.onSuccess(function(e){var t=e.responseText||e.response;if(!t)return t;try{return JSON.parse(t)}catch(e){return t}}(t))},t.open(\"post\",i,!0),e.withCredentials&&\"withCredentials\"in t&&(t.withCredentials=!0);var r=e.headers||{};for(var s in r)r.hasOwnProperty(s)&&null!==r[s]&&t.setRequestHeader(s,r[s]);return t.send(n),t}}},disabled:Boolean,limit:Number,onExceed:Function},data:function(){return{mouseover:!1,reqs:{}}},methods:{isImage:function(e){return-1!==e.indexOf(\"image\")},handleChange:function(e){var t=e.target.files;t&&this.uploadFiles(t)},uploadFiles:function(e){var t=this;if(this.limit&&this.fileList.length+e.length>this.limit)this.onExceed&&this.onExceed(e,this.fileList);else{var i=Array.prototype.slice.call(e);this.multiple||(i=i.slice(0,1)),0!==i.length&&i.forEach(function(e){t.onStart(e),t.autoUpload&&t.upload(e)})}},upload:function(e){var t=this;if(this.$refs.input.value=null,!this.beforeUpload)return this.post(e);var i=this.beforeUpload(e);i&&i.then?i.then(function(i){var n=Object.prototype.toString.call(i);if(\"[object File]\"===n||\"[object Blob]\"===n){for(var r in\"[object Blob]\"===n&&(i=new File([i],e.name,{type:e.type})),e)e.hasOwnProperty(r)&&(i[r]=e[r]);t.post(i)}else t.post(e)},function(){t.onRemove(null,e)}):!1!==i?this.post(e):this.onRemove(null,e)},abort:function(e){var t=this.reqs;if(e){var i=e;e.uid&&(i=e.uid),t[i]&&t[i].abort()}else Object.keys(t).forEach(function(e){t[e]&&t[e].abort(),delete t[e]})},post:function(e){var t=this,i=e.uid,n={headers:this.headers,withCredentials:this.withCredentials,file:e,data:this.data,filename:this.name,action:this.action,onProgress:function(i){t.onProgress(i,e)},onSuccess:function(n){t.onSuccess(n,e),delete t.reqs[i]},onError:function(n){t.onError(n,e),delete t.reqs[i]}},r=this.httpRequest(n);this.reqs[i]=r,r&&r.then&&r.then(n.onSuccess,n.onError)},handleClick:function(){this.disabled||(this.$refs.input.value=null,this.$refs.input.click())},handleKeydown:function(e){e.target===e.currentTarget&&(13!==e.keyCode&&32!==e.keyCode||this.handleClick())}},render:function(e){var t=this.handleClick,i=this.drag,n=this.name,r=this.handleChange,s=this.multiple,a=this.accept,o=this.listType,l=this.uploadFiles,u=this.disabled,c={class:{\"el-upload\":!0},on:{click:t,keydown:this.handleKeydown}};return c.class[\"el-upload--\"+o]=!0,e(\"div\",Bl()([c,{attrs:{tabindex:\"0\"}}]),[i?e(\"upload-dragger\",{attrs:{disabled:u},on:{file:l}},[this.$slots.default]):this.$slots.default,e(\"input\",{class:\"el-upload__input\",attrs:{type:\"file\",name:n,multiple:s,accept:a},ref:\"input\",on:{change:r}})])}},void 0,void 0,!1,null,null,null);Rl.options.__file=\"packages/upload/src/upload.vue\";var Wl=Rl.exports;function jl(){}var ql=r({name:\"ElUpload\",mixins:[K],components:{ElProgress:Al,UploadList:Ll,Upload:Wl},provide:function(){return{uploader:this}},inject:{elForm:{default:\"\"}},props:{action:{type:String,required:!0},headers:{type:Object,default:function(){return{}}},data:Object,multiple:Boolean,name:{type:String,default:\"file\"},drag:Boolean,dragger:Boolean,withCredentials:Boolean,showFileList:{type:Boolean,default:!0},accept:String,type:{type:String,default:\"select\"},beforeUpload:Function,beforeRemove:Function,onRemove:{type:Function,default:jl},onChange:{type:Function,default:jl},onPreview:{type:Function},onSuccess:{type:Function,default:jl},onProgress:{type:Function,default:jl},onError:{type:Function,default:jl},fileList:{type:Array,default:function(){return[]}},autoUpload:{type:Boolean,default:!0},listType:{type:String,default:\"text\"},httpRequest:Function,disabled:Boolean,limit:Number,onExceed:{type:Function,default:jl}},data:function(){return{uploadFiles:[],dragOver:!1,draging:!1,tempIndex:1}},computed:{uploadDisabled:function(){return this.disabled||(this.elForm||{}).disabled}},watch:{listType:function(e){\"picture-card\"!==e&&\"picture\"!==e||(this.uploadFiles=this.uploadFiles.map(function(e){if(!e.url&&e.raw)try{e.url=URL.createObjectURL(e.raw)}catch(e){console.error(\"[Element Error][Upload]\",e)}return e}))},fileList:{immediate:!0,handler:function(e){var t=this;this.uploadFiles=e.map(function(e){return e.uid=e.uid||Date.now()+t.tempIndex++,e.status=e.status||\"success\",e})}}},methods:{handleStart:function(e){e.uid=Date.now()+this.tempIndex++;var t={status:\"ready\",name:e.name,size:e.size,percentage:0,uid:e.uid,raw:e};if(\"picture-card\"===this.listType||\"picture\"===this.listType)try{t.url=URL.createObjectURL(e)}catch(e){return void console.error(\"[Element Error][Upload]\",e)}this.uploadFiles.push(t),this.onChange(t,this.uploadFiles)},handleProgress:function(e,t){var i=this.getFile(t);this.onProgress(e,i,this.uploadFiles),i.status=\"uploading\",i.percentage=e.percent||0},handleSuccess:function(e,t){var i=this.getFile(t);i&&(i.status=\"success\",i.response=e,this.onSuccess(e,i,this.uploadFiles),this.onChange(i,this.uploadFiles))},handleError:function(e,t){var i=this.getFile(t),n=this.uploadFiles;i.status=\"fail\",n.splice(n.indexOf(i),1),this.onError(e,i,this.uploadFiles),this.onChange(i,this.uploadFiles)},handleRemove:function(e,t){var i=this;t&&(e=this.getFile(t));var n=function(){i.abort(e);var t=i.uploadFiles;t.splice(t.indexOf(e),1),i.onRemove(e,t)};if(this.beforeRemove){if(\"function\"==typeof this.beforeRemove){var r=this.beforeRemove(e,this.uploadFiles);r&&r.then?r.then(function(){n()},jl):!1!==r&&n()}}else n()},getFile:function(e){var t=this.uploadFiles,i=void 0;return t.every(function(t){return!(i=e.uid===t.uid?t:null)}),i},abort:function(e){this.$refs[\"upload-inner\"].abort(e)},clearFiles:function(){this.uploadFiles=[]},submit:function(){var e=this;this.uploadFiles.filter(function(e){return\"ready\"===e.status}).forEach(function(t){e.$refs[\"upload-inner\"].upload(t.raw)})},getMigratingConfig:function(){return{props:{\"default-file-list\":\"default-file-list is renamed to file-list.\",\"show-upload-list\":\"show-upload-list is renamed to show-file-list.\",\"thumbnail-mode\":\"thumbnail-mode has been deprecated, you can implement the same effect according to this case: http://element.eleme.io/#/zh-CN/component/upload#yong-hu-tou-xiang-shang-chuan\"}}}},beforeDestroy:function(){this.uploadFiles.forEach(function(e){e.url&&0===e.url.indexOf(\"blob:\")&&URL.revokeObjectURL(e.url)})},render:function(e){var t=this,i=void 0;this.showFileList&&(i=e(Ll,{attrs:{disabled:this.uploadDisabled,listType:this.listType,files:this.uploadFiles,handlePreview:this.onPreview},on:{remove:this.handleRemove}},[function(e){if(t.$scopedSlots.file)return t.$scopedSlots.file({file:e.file})}]));var n=e(\"upload\",{props:{type:this.type,drag:this.drag,action:this.action,multiple:this.multiple,\"before-upload\":this.beforeUpload,\"with-credentials\":this.withCredentials,headers:this.headers,name:this.name,data:this.data,accept:this.accept,fileList:this.uploadFiles,autoUpload:this.autoUpload,listType:this.listType,disabled:this.uploadDisabled,limit:this.limit,\"on-exceed\":this.onExceed,\"on-start\":this.handleStart,\"on-progress\":this.handleProgress,\"on-success\":this.handleSuccess,\"on-error\":this.handleError,\"on-preview\":this.onPreview,\"on-remove\":this.handleRemove,\"http-request\":this.httpRequest},ref:\"upload-inner\"},[this.$slots.trigger||this.$slots.default]);return e(\"div\",[\"picture-card\"===this.listType?i:\"\",this.$slots.trigger?[n,this.$slots.default]:n,this.$slots.tip,\"picture-card\"!==this.listType?i:\"\"])}},void 0,void 0,!1,null,null,null);ql.options.__file=\"packages/upload/src/index.vue\";var Yl=ql.exports;Yl.install=function(e){e.component(Yl.name,Yl)};var Kl=Yl,Gl=function(){var e=this.$createElement,t=this._self._c||e;return t(\"span\",{staticClass:\"el-spinner\"},[t(\"svg\",{staticClass:\"el-spinner-inner\",style:{width:this.radius/2+\"px\",height:this.radius/2+\"px\"},attrs:{viewBox:\"0 0 50 50\"}},[t(\"circle\",{staticClass:\"path\",attrs:{cx:\"25\",cy:\"25\",r:\"20\",fill:\"none\",stroke:this.strokeColor,\"stroke-width\":this.strokeWidth}})])])};Gl._withStripped=!0;var Ul=r({name:\"ElSpinner\",props:{type:String,radius:{type:Number,default:100},strokeWidth:{type:Number,default:5},strokeColor:{type:String,default:\"#efefef\"}}},Gl,[],!1,null,null,null);Ul.options.__file=\"packages/spinner/src/spinner.vue\";var Xl=Ul.exports;Xl.install=function(e){e.component(Xl.name,Xl)};var Jl=Xl,Zl=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition\",{attrs:{name:\"el-message-fade\"},on:{\"after-leave\":e.handleAfterLeave}},[i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.visible,expression:\"visible\"}],class:[\"el-message\",e.type&&!e.iconClass?\"el-message--\"+e.type:\"\",e.center?\"is-center\":\"\",e.showClose?\"is-closable\":\"\",e.customClass],style:e.positionStyle,attrs:{role:\"alert\"},on:{mouseenter:e.clearTimer,mouseleave:e.startTimer}},[e.iconClass?i(\"i\",{class:e.iconClass}):i(\"i\",{class:e.typeClass}),e._t(\"default\",[e.dangerouslyUseHTMLString?i(\"p\",{staticClass:\"el-message__content\",domProps:{innerHTML:e._s(e.message)}}):i(\"p\",{staticClass:\"el-message__content\"},[e._v(e._s(e.message))])]),e.showClose?i(\"i\",{staticClass:\"el-message__closeBtn el-icon-close\",on:{click:e.close}}):e._e()],2)])};Zl._withStripped=!0;var Ql={success:\"success\",info:\"info\",warning:\"warning\",error:\"error\"},eu=r({data:function(){return{visible:!1,message:\"\",duration:3e3,type:\"info\",iconClass:\"\",customClass:\"\",onClose:null,showClose:!1,closed:!1,verticalOffset:20,timer:null,dangerouslyUseHTMLString:!1,center:!1}},computed:{typeClass:function(){return this.type&&!this.iconClass?\"el-message__icon el-icon-\"+Ql[this.type]:\"\"},positionStyle:function(){return{top:this.verticalOffset+\"px\"}}},watch:{closed:function(e){e&&(this.visible=!1)}},methods:{handleAfterLeave:function(){this.$destroy(!0),this.$el.parentNode.removeChild(this.$el)},close:function(){this.closed=!0,\"function\"==typeof this.onClose&&this.onClose(this)},clearTimer:function(){clearTimeout(this.timer)},startTimer:function(){var e=this;this.duration>0&&(this.timer=setTimeout(function(){e.closed||e.close()},this.duration))},keydown:function(e){27===e.keyCode&&(this.closed||this.close())}},mounted:function(){this.startTimer(),document.addEventListener(\"keydown\",this.keydown)},beforeDestroy:function(){document.removeEventListener(\"keydown\",this.keydown)}},Zl,[],!1,null,null,null);eu.options.__file=\"packages/message/src/main.vue\";var tu=eu.exports,iu=h.a.extend(tu),nu=void 0,ru=[],su=1,au=function e(t){if(!h.a.prototype.$isServer){\"string\"==typeof(t=t||{})&&(t={message:t});var i=t.onClose,n=\"message_\"+su++;t.onClose=function(){e.close(n,i)},(nu=new iu({data:t})).id=n,ua(nu.message)&&(nu.$slots.default=[nu.message],nu.message=null),nu.$mount(),document.body.appendChild(nu.$el);var r=t.offset||20;return ru.forEach(function(e){r+=e.$el.offsetHeight+16}),nu.verticalOffset=r,nu.visible=!0,nu.$el.style.zIndex=Se.nextZIndex(),ru.push(nu),nu}};[\"success\",\"warning\",\"info\",\"error\"].forEach(function(e){au[e]=function(t){return\"string\"==typeof t&&(t={message:t}),t.type=e,au(t)}}),au.close=function(e,t){for(var i=ru.length,n=-1,r=void 0,s=0;sru.length-1))for(var a=n;a=0;e--)ru[e].close()};var ou=au,lu=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-badge\"},[e._t(\"default\"),i(\"transition\",{attrs:{name:\"el-zoom-in-center\"}},[i(\"sup\",{directives:[{name:\"show\",rawName:\"v-show\",value:!e.hidden&&(e.content||0===e.content||e.isDot),expression:\"!hidden && (content || content === 0 || isDot)\"}],staticClass:\"el-badge__content\",class:[\"el-badge__content--\"+e.type,{\"is-fixed\":e.$slots.default,\"is-dot\":e.isDot}],domProps:{textContent:e._s(e.content)}})])],2)};lu._withStripped=!0;var uu=r({name:\"ElBadge\",props:{value:[String,Number],max:Number,isDot:Boolean,hidden:Boolean,type:{type:String,validator:function(e){return[\"primary\",\"success\",\"warning\",\"info\",\"danger\"].indexOf(e)>-1}}},computed:{content:function(){if(!this.isDot){var e=this.value,t=this.max;return\"number\"==typeof e&&\"number\"==typeof t&&t0&&e-1this.value,i=this.allowHalf&&this.pointerAtLeftHalf&&e-.5<=this.currentValue&&e>this.currentValue;return t||i},getIconStyle:function(e){var t=this.rateDisabled?this.disabledVoidColor:this.voidColor;return{color:e<=this.currentValue?this.activeColor:t}},selectValue:function(e){this.rateDisabled||(this.allowHalf&&this.pointerAtLeftHalf?(this.$emit(\"input\",this.currentValue),this.$emit(\"change\",this.currentValue)):(this.$emit(\"input\",e),this.$emit(\"change\",e)))},handleKey:function(e){if(!this.rateDisabled){var t=this.currentValue,i=e.keyCode;38===i||39===i?(this.allowHalf?t+=.5:t+=1,e.stopPropagation(),e.preventDefault()):37!==i&&40!==i||(this.allowHalf?t-=.5:t-=1,e.stopPropagation(),e.preventDefault()),t=(t=t<0?0:t)>this.max?this.max:t,this.$emit(\"input\",t),this.$emit(\"change\",t)}},setCurrentValue:function(e,t){if(!this.rateDisabled){if(this.allowHalf){var i=t.target;pe(i,\"el-rate__item\")&&(i=i.querySelector(\".el-rate__icon\")),pe(i,\"el-rate__decimal\")&&(i=i.parentNode),this.pointerAtLeftHalf=2*t.offsetX<=i.clientWidth,this.currentValue=this.pointerAtLeftHalf?e-.5:e}else this.currentValue=e;this.hoverIndex=e}},resetCurrentValue:function(){this.rateDisabled||(this.allowHalf&&(this.pointerAtLeftHalf=this.value!==Math.floor(this.value)),this.currentValue=this.value,this.hoverIndex=-1)}},created:function(){this.value||this.$emit(\"input\",0)}},vu,[],!1,null,null,null);gu.options.__file=\"packages/rate/src/main.vue\";var bu=gu.exports;bu.install=function(e){e.component(bu.name,bu)};var yu=bu,wu=function(){var e=this.$createElement;return(this._self._c||e)(\"div\",{staticClass:\"el-steps\",class:[!this.simple&&\"el-steps--\"+this.direction,this.simple&&\"el-steps--simple\"]},[this._t(\"default\")],2)};wu._withStripped=!0;var _u=r({name:\"ElSteps\",mixins:[K],props:{space:[Number,String],active:Number,direction:{type:String,default:\"horizontal\"},alignCenter:Boolean,simple:Boolean,finishStatus:{type:String,default:\"finish\"},processStatus:{type:String,default:\"process\"}},data:function(){return{steps:[],stepOffset:0}},methods:{getMigratingConfig:function(){return{props:{center:\"center is removed.\"}}}},watch:{active:function(e,t){this.$emit(\"change\",e,t)},steps:function(e){e.forEach(function(e,t){e.index=t})}}},wu,[],!1,null,null,null);_u.options.__file=\"packages/steps/src/steps.vue\";var xu=_u.exports;xu.install=function(e){e.component(xu.name,xu)};var Cu=xu,ku=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-step\",class:[!e.isSimple&&\"is-\"+e.$parent.direction,e.isSimple&&\"is-simple\",e.isLast&&!e.space&&!e.isCenter&&\"is-flex\",e.isCenter&&!e.isVertical&&!e.isSimple&&\"is-center\"],style:e.style},[i(\"div\",{staticClass:\"el-step__head\",class:\"is-\"+e.currentStatus},[i(\"div\",{staticClass:\"el-step__line\",style:e.isLast?\"\":{marginRight:e.$parent.stepOffset+\"px\"}},[i(\"i\",{staticClass:\"el-step__line-inner\",style:e.lineStyle})]),i(\"div\",{staticClass:\"el-step__icon\",class:\"is-\"+(e.icon?\"icon\":\"text\")},[\"success\"!==e.currentStatus&&\"error\"!==e.currentStatus?e._t(\"icon\",[e.icon?i(\"i\",{staticClass:\"el-step__icon-inner\",class:[e.icon]}):e._e(),e.icon||e.isSimple?e._e():i(\"div\",{staticClass:\"el-step__icon-inner\"},[e._v(e._s(e.index+1))])]):i(\"i\",{staticClass:\"el-step__icon-inner is-status\",class:[\"el-icon-\"+(\"success\"===e.currentStatus?\"check\":\"close\")]})],2)]),i(\"div\",{staticClass:\"el-step__main\"},[i(\"div\",{ref:\"title\",staticClass:\"el-step__title\",class:[\"is-\"+e.currentStatus]},[e._t(\"title\",[e._v(e._s(e.title))])],2),e.isSimple?i(\"div\",{staticClass:\"el-step__arrow\"}):i(\"div\",{staticClass:\"el-step__description\",class:[\"is-\"+e.currentStatus]},[e._t(\"description\",[e._v(e._s(e.description))])],2)])])};ku._withStripped=!0;var Su=r({name:\"ElStep\",props:{title:String,icon:String,description:String,status:String},data:function(){return{index:-1,lineStyle:{},internalStatus:\"\"}},beforeCreate:function(){this.$parent.steps.push(this)},beforeDestroy:function(){var e=this.$parent.steps,t=e.indexOf(this);t>=0&&e.splice(t,1)},computed:{currentStatus:function(){return this.status||this.internalStatus},prevStatus:function(){var e=this.$parent.steps[this.index-1];return e?e.currentStatus:\"wait\"},isCenter:function(){return this.$parent.alignCenter},isVertical:function(){return\"vertical\"===this.$parent.direction},isSimple:function(){return this.$parent.simple},isLast:function(){var e=this.$parent;return e.steps[e.steps.length-1]===this},stepsCount:function(){return this.$parent.steps.length},space:function(){var e=this.isSimple,t=this.$parent.space;return e?\"\":t},style:function(){var e={},t=this.$parent.steps.length,i=\"number\"==typeof this.space?this.space+\"px\":this.space?this.space:100/(t-(this.isCenter?0:1))+\"%\";return e.flexBasis=i,this.isVertical?e:(this.isLast?e.maxWidth=100/this.stepsCount+\"%\":e.marginRight=-this.$parent.stepOffset+\"px\",e)}},methods:{updateStatus:function(e){var t=this.$parent.$children[this.index-1];e>this.index?this.internalStatus=this.$parent.finishStatus:e===this.index&&\"error\"!==this.prevStatus?this.internalStatus=this.$parent.processStatus:this.internalStatus=\"wait\",t&&t.calcProgress(this.internalStatus)},calcProgress:function(e){var t=100,i={};i.transitionDelay=150*this.index+\"ms\",e===this.$parent.processStatus?(this.currentStatus,t=0):\"wait\"===e&&(t=0,i.transitionDelay=-150*this.index+\"ms\"),i.borderWidth=t&&!this.isSimple?\"1px\":0,\"vertical\"===this.$parent.direction?i.height=t+\"%\":i.width=t+\"%\",this.lineStyle=i}},mounted:function(){var e=this,t=this.$watch(\"index\",function(i){e.$watch(\"$parent.active\",e.updateStatus,{immediate:!0}),e.$watch(\"$parent.processStatus\",function(){var t=e.$parent.active;e.updateStatus(t)},{immediate:!0}),t()})}},ku,[],!1,null,null,null);Su.options.__file=\"packages/steps/src/step.vue\";var Du=Su.exports;Du.install=function(e){e.component(Du.name,Du)};var $u=Du,Eu=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{class:e.carouselClasses,on:{mouseenter:function(t){return t.stopPropagation(),e.handleMouseEnter(t)},mouseleave:function(t){return t.stopPropagation(),e.handleMouseLeave(t)}}},[i(\"div\",{staticClass:\"el-carousel__container\",style:{height:e.height}},[e.arrowDisplay?i(\"transition\",{attrs:{name:\"carousel-arrow-left\"}},[i(\"button\",{directives:[{name:\"show\",rawName:\"v-show\",value:(\"always\"===e.arrow||e.hover)&&(e.loop||e.activeIndex>0),expression:\"(arrow === 'always' || hover) && (loop || activeIndex > 0)\"}],staticClass:\"el-carousel__arrow el-carousel__arrow--left\",attrs:{type:\"button\"},on:{mouseenter:function(t){e.handleButtonEnter(\"left\")},mouseleave:e.handleButtonLeave,click:function(t){t.stopPropagation(),e.throttledArrowClick(e.activeIndex-1)}}},[i(\"i\",{staticClass:\"el-icon-arrow-left\"})])]):e._e(),e.arrowDisplay?i(\"transition\",{attrs:{name:\"carousel-arrow-right\"}},[i(\"button\",{directives:[{name:\"show\",rawName:\"v-show\",value:(\"always\"===e.arrow||e.hover)&&(e.loop||e.activeIndex0})},carouselClasses:function(){var e=[\"el-carousel\",\"el-carousel--\"+this.direction];return\"card\"===this.type&&e.push(\"el-carousel--card\"),e},indicatorsClasses:function(){var e=[\"el-carousel__indicators\",\"el-carousel__indicators--\"+this.direction];return this.hasLabel&&e.push(\"el-carousel__indicators--labels\"),\"outside\"!==this.indicatorPosition&&\"card\"!==this.type||e.push(\"el-carousel__indicators--outside\"),e}},watch:{items:function(e){e.length>0&&this.setActiveItem(this.initialIndex)},activeIndex:function(e,t){this.resetItemPosition(t),t>-1&&this.$emit(\"change\",e,t)},autoplay:function(e){e?this.startTimer():this.pauseTimer()},loop:function(){this.setActiveItem(this.activeIndex)}},methods:{handleMouseEnter:function(){this.hover=!0,this.pauseTimer()},handleMouseLeave:function(){this.hover=!1,this.startTimer()},itemInStage:function(e,t){var i=this.items.length;return t===i-1&&e.inStage&&this.items[0].active||e.inStage&&this.items[t+1]&&this.items[t+1].active?\"left\":!!(0===t&&e.inStage&&this.items[i-1].active||e.inStage&&this.items[t-1]&&this.items[t-1].active)&&\"right\"},handleButtonEnter:function(e){var t=this;\"vertical\"!==this.direction&&this.items.forEach(function(i,n){e===t.itemInStage(i,n)&&(i.hover=!0)})},handleButtonLeave:function(){\"vertical\"!==this.direction&&this.items.forEach(function(e){e.hover=!1})},updateItems:function(){this.items=this.$children.filter(function(e){return\"ElCarouselItem\"===e.$options.name})},resetItemPosition:function(e){var t=this;this.items.forEach(function(i,n){i.translateItem(n,t.activeIndex,e)})},playSlides:function(){this.activeIndex0&&(e=this.items.indexOf(t[0]))}if(e=Number(e),isNaN(e)||e!==Math.floor(e))console.warn(\"[Element Warn][Carousel]index must be an integer.\");else{var i=this.items.length,n=this.activeIndex;this.activeIndex=e<0?this.loop?i-1:0:e>=i?this.loop?0:i-1:e,n===this.activeIndex&&this.resetItemPosition(n)}},prev:function(){this.setActiveItem(this.activeIndex-1)},next:function(){this.setActiveItem(this.activeIndex+1)},handleIndicatorClick:function(e){this.activeIndex=e},handleIndicatorHover:function(e){\"hover\"===this.trigger&&e!==this.activeIndex&&(this.activeIndex=e)}},created:function(){var e=this;this.throttledArrowClick=Mu()(300,!0,function(t){e.setActiveItem(t)}),this.throttledIndicatorHover=Mu()(300,function(t){e.handleIndicatorHover(t)})},mounted:function(){var e=this;this.updateItems(),this.$nextTick(function(){Ye(e.$el,e.resetItemPosition),e.initialIndex=0&&(e.activeIndex=e.initialIndex),e.startTimer()})},beforeDestroy:function(){this.$el&&Ke(this.$el,this.resetItemPosition),this.pauseTimer()}},Eu,[],!1,null,null,null);Nu.options.__file=\"packages/carousel/src/main.vue\";var Pu=Nu.exports;Pu.install=function(e){e.component(Pu.name,Pu)};var Ou=Pu,Iu=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.ready,expression:\"ready\"}],staticClass:\"el-carousel__item\",class:{\"is-active\":e.active,\"el-carousel__item--card\":\"card\"===e.$parent.type,\"is-in-stage\":e.inStage,\"is-hover\":e.hover,\"is-animating\":e.animating},style:e.itemStyle,on:{click:e.handleItemClick}},[\"card\"===e.$parent.type?i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:!e.active,expression:\"!active\"}],staticClass:\"el-carousel__mask\"}):e._e(),e._t(\"default\")],2)};Iu._withStripped=!0;var Au=r({name:\"ElCarouselItem\",props:{name:String,label:{type:[String,Number],default:\"\"}},data:function(){return{hover:!1,translate:0,scale:1,active:!1,ready:!1,inStage:!1,animating:!1}},methods:{processIndex:function(e,t,i){return 0===t&&e===i-1?-1:t===i-1&&0===e?i:e=i/2?i+1:e>t+1&&e-t>=i/2?-2:e},calcCardTranslate:function(e,t){var i=this.$parent.$el.offsetWidth;return this.inStage?i*(1.17*(e-t)+1)/4:e2&&this.$parent.loop&&(e=this.processIndex(e,t,s)),\"card\"===n)\"vertical\"===r&&console.warn(\"[Element Warn][Carousel]vertical direction is not supported in card mode\"),this.inStage=Math.round(Math.abs(e-t))<=1,this.active=e===t,this.translate=this.calcCardTranslate(e,t),this.scale=this.active?1:.83;else{this.active=e===t;var a=\"vertical\"===r;this.translate=this.calcTranslate(e,t,a)}this.ready=!0},handleItemClick:function(){var e=this.$parent;if(e&&\"card\"===e.type){var t=e.items.indexOf(this);e.setActiveItem(t)}}},computed:{parentDirection:function(){return this.$parent.direction},itemStyle:function(){return function(e){if(\"object\"!==(void 0===e?\"undefined\":y(e)))return e;var t=[\"ms-\",\"webkit-\"];return[\"transform\",\"transition\",\"animation\"].forEach(function(i){var n=e[i];i&&n&&t.forEach(function(t){e[t+i]=n})}),e}({transform:(\"vertical\"===this.parentDirection?\"translateY\":\"translateX\")+\"(\"+this.translate+\"px) scale(\"+this.scale+\")\"})}},created:function(){this.$parent&&this.$parent.updateItems()},destroyed:function(){this.$parent&&this.$parent.updateItems()}},Iu,[],!1,null,null,null);Au.options.__file=\"packages/carousel/src/item.vue\";var Fu=Au.exports;Fu.install=function(e){e.component(Fu.name,Fu)};var Lu=Fu,Vu=function(){var e=this.$createElement;return(this._self._c||e)(\"div\",{staticClass:\"el-collapse\",attrs:{role:\"tablist\",\"aria-multiselectable\":\"true\"}},[this._t(\"default\")],2)};Vu._withStripped=!0;var Bu=r({name:\"ElCollapse\",componentName:\"ElCollapse\",props:{accordion:Boolean,value:{type:[Array,String,Number],default:function(){return[]}}},data:function(){return{activeNames:[].concat(this.value)}},provide:function(){return{collapse:this}},watch:{value:function(e){this.activeNames=[].concat(e)}},methods:{setActiveNames:function(e){e=[].concat(e);var t=this.accordion?e[0]:e;this.activeNames=e,this.$emit(\"input\",t),this.$emit(\"change\",t)},handleItemClick:function(e){if(this.accordion)this.setActiveNames(!this.activeNames[0]&&0!==this.activeNames[0]||this.activeNames[0]!==e.name?e.name:\"\");else{var t=this.activeNames.slice(0),i=t.indexOf(e.name);i>-1?t.splice(i,1):t.push(e.name),this.setActiveNames(t)}}},created:function(){this.$on(\"item-click\",this.handleItemClick)}},Vu,[],!1,null,null,null);Bu.options.__file=\"packages/collapse/src/collapse.vue\";var zu=Bu.exports;zu.install=function(e){e.component(zu.name,zu)};var Hu=zu,Ru=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-collapse-item\",class:{\"is-active\":e.isActive,\"is-disabled\":e.disabled}},[i(\"div\",{attrs:{role:\"tab\",\"aria-expanded\":e.isActive,\"aria-controls\":\"el-collapse-content-\"+e.id,\"aria-describedby\":\"el-collapse-content-\"+e.id}},[i(\"div\",{staticClass:\"el-collapse-item__header\",class:{focusing:e.focusing,\"is-active\":e.isActive},attrs:{role:\"button\",id:\"el-collapse-head-\"+e.id,tabindex:e.disabled?void 0:0},on:{click:e.handleHeaderClick,keyup:function(t){return\"button\"in t||!e._k(t.keyCode,\"space\",32,t.key,[\" \",\"Spacebar\"])||!e._k(t.keyCode,\"enter\",13,t.key,\"Enter\")?(t.stopPropagation(),e.handleEnterClick(t)):null},focus:e.handleFocus,blur:function(t){e.focusing=!1}}},[e._t(\"title\",[e._v(e._s(e.title))]),i(\"i\",{staticClass:\"el-collapse-item__arrow el-icon-arrow-right\",class:{\"is-active\":e.isActive}})],2)]),i(\"el-collapse-transition\",[i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.isActive,expression:\"isActive\"}],staticClass:\"el-collapse-item__wrap\",attrs:{role:\"tabpanel\",\"aria-hidden\":!e.isActive,\"aria-labelledby\":\"el-collapse-head-\"+e.id,id:\"el-collapse-content-\"+e.id}},[i(\"div\",{staticClass:\"el-collapse-item__content\"},[e._t(\"default\")],2)])])],1)};Ru._withStripped=!0;var Wu=r({name:\"ElCollapseItem\",componentName:\"ElCollapseItem\",mixins:[l],components:{ElCollapseTransition:ii},data:function(){return{contentWrapStyle:{height:\"auto\",display:\"block\"},contentHeight:0,focusing:!1,isClick:!1,id:D()}},inject:[\"collapse\"],props:{title:String,name:{type:[String,Number],default:function(){return this._uid}},disabled:Boolean},computed:{isActive:function(){return this.collapse.activeNames.indexOf(this.name)>-1}},methods:{handleFocus:function(){var e=this;setTimeout(function(){e.isClick?e.isClick=!1:e.focusing=!0},50)},handleHeaderClick:function(){this.disabled||(this.dispatch(\"ElCollapse\",\"item-click\",this),this.focusing=!1,this.isClick=!0)},handleEnterClick:function(){this.dispatch(\"ElCollapse\",\"item-click\",this)}}},Ru,[],!1,null,null,null);Wu.options.__file=\"packages/collapse/src/collapse-item.vue\";var ju=Wu.exports;ju.install=function(e){e.component(ju.name,ju)};var qu=ju,Yu=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{directives:[{name:\"clickoutside\",rawName:\"v-clickoutside\",value:function(){return e.toggleDropDownVisible(!1)},expression:\"() => toggleDropDownVisible(false)\"}],ref:\"reference\",class:[\"el-cascader\",e.realSize&&\"el-cascader--\"+e.realSize,{\"is-disabled\":e.isDisabled}],on:{mouseenter:function(t){e.inputHover=!0},mouseleave:function(t){e.inputHover=!1},click:function(){return e.toggleDropDownVisible(!e.readonly||void 0)},keydown:e.handleKeyDown}},[i(\"el-input\",{ref:\"input\",class:{\"is-focus\":e.dropDownVisible},attrs:{size:e.realSize,placeholder:e.placeholder,readonly:e.readonly,disabled:e.isDisabled,\"validate-event\":!1},on:{focus:e.handleFocus,blur:e.handleBlur,input:e.handleInput},model:{value:e.multiple?e.presentText:e.inputValue,callback:function(t){e.multiple?e.presentText:e.inputValue=t},expression:\"multiple ? presentText : inputValue\"}},[i(\"template\",{slot:\"suffix\"},[e.clearBtnVisible?i(\"i\",{key:\"clear\",staticClass:\"el-input__icon el-icon-circle-close\",on:{click:function(t){return t.stopPropagation(),e.handleClear(t)}}}):i(\"i\",{key:\"arrow-down\",class:[\"el-input__icon\",\"el-icon-arrow-down\",e.dropDownVisible&&\"is-reverse\"],on:{click:function(t){t.stopPropagation(),e.toggleDropDownVisible()}}})])],2),e.multiple?i(\"div\",{staticClass:\"el-cascader__tags\"},[e._l(e.presentTags,function(t,n){return i(\"el-tag\",{key:t.key,attrs:{type:\"info\",size:e.tagSize,hit:t.hitState,closable:t.closable,\"disable-transitions\":\"\"},on:{close:function(t){e.deleteTag(n)}}},[i(\"span\",[e._v(e._s(t.text))])])}),e.filterable&&!e.isDisabled?i(\"input\",{directives:[{name:\"model\",rawName:\"v-model.trim\",value:e.inputValue,expression:\"inputValue\",modifiers:{trim:!0}}],staticClass:\"el-cascader__search-input\",attrs:{type:\"text\",placeholder:e.presentTags.length?\"\":e.placeholder},domProps:{value:e.inputValue},on:{input:[function(t){t.target.composing||(e.inputValue=t.target.value.trim())},function(t){return e.handleInput(e.inputValue,t)}],click:function(t){t.stopPropagation(),e.toggleDropDownVisible(!0)},keydown:function(t){return\"button\"in t||!e._k(t.keyCode,\"delete\",[8,46],t.key,[\"Backspace\",\"Delete\",\"Del\"])?e.handleDelete(t):null},blur:function(t){e.$forceUpdate()}}}):e._e()],2):e._e(),i(\"transition\",{attrs:{name:\"el-zoom-in-top\"},on:{\"after-leave\":e.handleDropdownLeave}},[i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.dropDownVisible,expression:\"dropDownVisible\"}],ref:\"popper\",class:[\"el-popper\",\"el-cascader__dropdown\",e.popperClass]},[i(\"el-cascader-panel\",{directives:[{name:\"show\",rawName:\"v-show\",value:!e.filtering,expression:\"!filtering\"}],ref:\"panel\",attrs:{options:e.options,props:e.config,border:!1,\"render-label\":e.$scopedSlots.default},on:{\"expand-change\":e.handleExpandChange,close:function(t){e.toggleDropDownVisible(!1)}},model:{value:e.checkedValue,callback:function(t){e.checkedValue=t},expression:\"checkedValue\"}}),e.filterable?i(\"el-scrollbar\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.filtering,expression:\"filtering\"}],ref:\"suggestionPanel\",staticClass:\"el-cascader__suggestion-panel\",attrs:{tag:\"ul\",\"view-class\":\"el-cascader__suggestion-list\"},nativeOn:{keydown:function(t){return e.handleSuggestionKeyDown(t)}}},[e.suggestions.length?e._l(e.suggestions,function(t,n){return i(\"li\",{key:t.uid,class:[\"el-cascader__suggestion-item\",t.checked&&\"is-checked\"],attrs:{tabindex:-1},on:{click:function(t){e.handleSuggestionClick(n)}}},[i(\"span\",[e._v(e._s(t.text))]),t.checked?i(\"i\",{staticClass:\"el-icon-check\"}):e._e()])}):e._t(\"empty\",[i(\"li\",{staticClass:\"el-cascader__empty-text\"},[e._v(e._s(e.t(\"el.cascader.noMatch\")))])])],2):e._e()],1)])],1)};Yu._withStripped=!0;var Ku=function(){var e=this.$createElement,t=this._self._c||e;return t(\"div\",{class:[\"el-cascader-panel\",this.border&&\"is-bordered\"],on:{keydown:this.handleKeyDown}},this._l(this.menus,function(e,i){return t(\"cascader-menu\",{key:i,ref:\"menu\",refInFor:!0,attrs:{index:i,nodes:e}})}),1)};Ku._withStripped=!0;var Gu=function(e){return e.stopPropagation()},Uu=r({inject:[\"panel\"],components:{ElCheckbox:Vi,ElRadio:Si},props:{node:{required:!0},nodeId:String},computed:{config:function(){return this.panel.config},isLeaf:function(){return this.node.isLeaf},isDisabled:function(){return this.node.isDisabled},checkedValue:function(){return this.panel.checkedValue},isChecked:function(){return this.node.isSameNode(this.checkedValue)},inActivePath:function(){return this.isInPath(this.panel.activePath)},inCheckedPath:function(){var e=this;return!!this.config.checkStrictly&&this.panel.checkedNodePaths.some(function(t){return e.isInPath(t)})},value:function(){return this.node.getValueByOption()}},methods:{handleExpand:function(){var e=this,t=this.panel,i=this.node,n=this.isDisabled,r=this.config,s=r.multiple;!r.checkStrictly&&n||i.loading||(r.lazy&&!i.loaded?t.lazyLoad(i,function(){var t=e.isLeaf;if(t||e.handleExpand(),s){var n=!!t&&i.checked;e.handleMultiCheckChange(n)}}):t.handleExpand(i))},handleCheckChange:function(){var e=this.panel,t=this.value,i=this.node;e.handleCheckChange(t),e.handleExpand(i)},handleMultiCheckChange:function(e){this.node.doCheck(e),this.panel.calculateMultiCheckedValue()},isInPath:function(e){var t=this.node;return(e[t.level-1]||{}).uid===t.uid},renderPrefix:function(e){var t=this.isLeaf,i=this.isChecked,n=this.config,r=n.checkStrictly;return n.multiple?this.renderCheckbox(e):r?this.renderRadio(e):t&&i?this.renderCheckIcon(e):null},renderPostfix:function(e){var t=this.node,i=this.isLeaf;return t.loading?this.renderLoadingIcon(e):i?null:this.renderExpandIcon(e)},renderCheckbox:function(e){var t=this.node,i=this.config,n=this.isDisabled,r={on:{change:this.handleMultiCheckChange},nativeOn:{}};return i.checkStrictly&&(r.nativeOn.click=Gu),e(\"el-checkbox\",Bl()([{attrs:{value:t.checked,indeterminate:t.indeterminate,disabled:n}},r]))},renderRadio:function(e){var t=this.checkedValue,i=this.value,n=this.isDisabled;return I(i,t)&&(i=t),e(\"el-radio\",{attrs:{value:t,label:i,disabled:n},on:{change:this.handleCheckChange},nativeOn:{click:Gu}},[e(\"span\")])},renderCheckIcon:function(e){return e(\"i\",{class:\"el-icon-check el-cascader-node__prefix\"})},renderLoadingIcon:function(e){return e(\"i\",{class:\"el-icon-loading el-cascader-node__postfix\"})},renderExpandIcon:function(e){return e(\"i\",{class:\"el-icon-arrow-right el-cascader-node__postfix\"})},renderContent:function(e){var t=this.panel,i=this.node,n=t.renderLabelFn;return e(\"span\",{class:\"el-cascader-node__label\"},[(n?n({node:i,data:i.data}):null)||i.label])}},render:function(e){var t=this,i=this.inActivePath,n=this.inCheckedPath,r=this.isChecked,s=this.isLeaf,a=this.isDisabled,o=this.config,l=this.nodeId,u=o.expandTrigger,c=o.checkStrictly,h=o.multiple,d=!c&&a,p={on:{}};return\"click\"===u?p.on.click=this.handleExpand:(p.on.mouseenter=function(e){t.handleExpand(),t.$emit(\"expand\",e)},p.on.focus=function(e){t.handleExpand(),t.$emit(\"expand\",e)}),!s||a||c||h||(p.on.click=this.handleCheckChange),e(\"li\",Bl()([{attrs:{role:\"menuitem\",id:l,\"aria-expanded\":i,tabindex:d?null:-1},class:{\"el-cascader-node\":!0,\"is-selectable\":c,\"in-active-path\":i,\"in-checked-path\":n,\"is-active\":r,\"is-disabled\":d}},p]),[this.renderPrefix(e),this.renderContent(e),this.renderPostfix(e)])}},void 0,void 0,!1,null,null,null);Uu.options.__file=\"packages/cascader-panel/src/cascader-node.vue\";var Xu=r({name:\"ElCascaderMenu\",mixins:[q],inject:[\"panel\"],components:{ElScrollbar:Ze,CascaderNode:Uu.exports},props:{nodes:{type:Array,required:!0},index:Number},data:function(){return{activeNode:null,hoverTimer:null,id:D()}},computed:{isEmpty:function(){return!this.nodes.length},menuId:function(){return\"cascader-menu-\"+this.id+\"-\"+this.index}},methods:{handleExpand:function(e){this.activeNode=e.target},handleMouseMove:function(e){var t=this.activeNode,i=this.hoverTimer,n=this.$refs.hoverZone;if(t&&n)if(t.contains(e.target)){clearTimeout(i);var r=this.$el.getBoundingClientRect().left,s=e.clientX-r,a=this.$el,o=a.offsetWidth,l=a.offsetHeight,u=t.offsetTop,c=u+t.offsetHeight;n.innerHTML='\\n \\n \\n '}else i||(this.hoverTimer=setTimeout(this.clearHoverZone,this.panel.config.hoverThreshold))},clearHoverZone:function(){var e=this.$refs.hoverZone;e&&(e.innerHTML=\"\")},renderEmptyText:function(e){return e(\"div\",{class:\"el-cascader-menu__empty-text\"},[this.t(\"el.cascader.noData\")])},renderNodeList:function(e){var t=this.menuId,i=this.panel.isHoverMenu,n={on:{}};i&&(n.on.expand=this.handleExpand);var r=this.nodes.map(function(i,r){var s=i.hasChildren;return e(\"cascader-node\",Bl()([{key:i.uid,attrs:{node:i,\"node-id\":t+\"-\"+r,\"aria-haspopup\":s,\"aria-owns\":s?t:null}},n]))});return[].concat(r,[i?e(\"svg\",{ref:\"hoverZone\",class:\"el-cascader-menu__hover-zone\"}):null])}},render:function(e){var t=this.isEmpty,i=this.menuId,n={nativeOn:{}};return this.panel.isHoverMenu&&(n.nativeOn.mousemove=this.handleMouseMove),e(\"el-scrollbar\",Bl()([{attrs:{tag:\"ul\",role:\"menu\",id:i,\"wrap-class\":\"el-cascader-menu__wrap\",\"view-class\":{\"el-cascader-menu__list\":!0,\"is-empty\":t}},class:\"el-cascader-menu\"},n]),[t?this.renderEmptyText(e):this.renderNodeList(e)])}},void 0,void 0,!1,null,null,null);Xu.options.__file=\"packages/cascader-panel/src/cascader-menu.vue\";var Ju=Xu.exports,Zu=function(){function e(e,t){for(var i=0;i1?t-1:0),n=1;n1?n-1:0),s=1;s0},e.prototype.syncCheckState=function(e){var t=this.getValueByOption(),i=this.isSameNode(e,t);this.doCheck(i)},e.prototype.doCheck=function(e){this.checked!==e&&(this.config.checkStrictly?this.checked=e:(this.broadcast(\"check\",e),this.setCheckState(e),this.emit(\"check\")))},Zu(e,[{key:\"isDisabled\",get:function(){var e=this.data,t=this.parent,i=this.config,n=i.disabled,r=i.checkStrictly;return e[n]||!r&&t&&t.isDisabled}},{key:\"isLeaf\",get:function(){var e=this.data,t=this.loaded,i=this.hasChildren,n=this.children,r=this.config,s=r.lazy,a=r.leaf;if(s){var o=Q(e[a])?e[a]:!!t&&!n.length;return this.hasChildren=!o,o}return!i}}]),e}();var tc=function(){function e(t,i){!function(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")}(this,e),this.config=i,this.initNodes(t)}return e.prototype.initNodes=function(e){var t=this;e=M(e),this.nodes=e.map(function(e){return new ec(e,t.config)}),this.flattedNodes=this.getFlattedNodes(!1,!1),this.leafNodes=this.getFlattedNodes(!0,!1)},e.prototype.appendNode=function(e,t){var i=new ec(e,this.config,t);(t?t.children:this.nodes).push(i)},e.prototype.appendNodes=function(e,t){var i=this;(e=M(e)).forEach(function(e){return i.appendNode(e,t)})},e.prototype.getNodes=function(){return this.nodes},e.prototype.getFlattedNodes=function(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=e?this.leafNodes:this.flattedNodes;return t?i:function e(t,i){return t.reduce(function(t,n){return n.isLeaf?t.push(n):(!i&&t.push(n),t=t.concat(e(n.children,i))),t},[])}(this.nodes,e)},e.prototype.getNodeByValue=function(e){if(e){var t=this.getFlattedNodes(!1,!this.config.lazy).filter(function(t){return $(t.path,e)||t.value===e});return t&&t.length?t[0]:null}return null},e}(),ic=Object.assign||function(e){for(var t=1;t0){var l=i.store.getNodeByValue(s);l.data[o]||i.lazyLoad(l,function(){i.handleExpand(l)}),i.loadCount===i.checkedValue.length&&i.$parent.computePresentText()}}t&&t(n)})},calculateMultiCheckedValue:function(){this.checkedValue=this.getCheckedNodes(this.leafOnly).map(function(e){return e.getValueByOption()})},scrollIntoView:function(){this.$isServer||(this.$refs.menu||[]).forEach(function(e){var t=e.$el;t&&ot(t.querySelector(\".el-scrollbar__wrap\"),t.querySelector(\".el-cascader-node.is-active\")||t.querySelector(\".el-cascader-node.in-active-path\"))})},getNodeByValue:function(e){return this.store.getNodeByValue(e)},getFlattedNodes:function(e){var t=!this.config.lazy;return this.store.getFlattedNodes(e,t)},getCheckedNodes:function(e){var t=this.checkedValue;return this.multiple?this.getFlattedNodes(e).filter(function(e){return e.checked}):A(t)?[]:[this.getNodeByValue(t)]},clearCheckedNodes:function(){var e=this.config,t=this.leafOnly,i=e.multiple,n=e.emitPath;i?(this.getCheckedNodes(t).filter(function(e){return!e.isDisabled}).forEach(function(e){return e.doCheck(!1)}),this.calculateMultiCheckedValue()):this.checkedValue=n?[]:null}}},Ku,[],!1,null,null,null);uc.options.__file=\"packages/cascader-panel/src/cascader-panel.vue\";var cc=uc.exports;cc.install=function(e){e.component(cc.name,cc)};var hc=cc,dc=qt.keys,pc={expandTrigger:{newProp:\"expandTrigger\",type:String},changeOnSelect:{newProp:\"checkStrictly\",type:Boolean},hoverThreshold:{newProp:\"hoverThreshold\",type:Number}},fc={props:{placement:{type:String,default:\"bottom-start\"},appendToBody:Oe.props.appendToBody,visibleArrow:{type:Boolean,default:!0},arrowOffset:Oe.props.arrowOffset,offset:Oe.props.offset,boundariesPadding:Oe.props.boundariesPadding,popperOptions:Oe.props.popperOptions},methods:Oe.methods,data:Oe.data,beforeDestroy:Oe.beforeDestroy},mc={medium:36,small:32,mini:28},vc=r({name:\"ElCascader\",directives:{Clickoutside:at},mixins:[fc,l,q,K],inject:{elForm:{default:\"\"},elFormItem:{default:\"\"}},components:{ElInput:ne,ElTag:Re,ElScrollbar:Ze,ElCascaderPanel:hc},props:{value:{},options:Array,props:Object,size:String,placeholder:{type:String,default:function(){return W(\"el.cascader.placeholder\")}},disabled:Boolean,clearable:Boolean,filterable:Boolean,filterMethod:Function,separator:{type:String,default:\" / \"},showAllLevels:{type:Boolean,default:!0},collapseTags:Boolean,debounce:{type:Number,default:300},beforeFilter:{type:Function,default:function(){return function(){}}},popperClass:String},data:function(){return{dropDownVisible:!1,checkedValue:this.value||null,inputHover:!1,inputValue:null,presentText:null,presentTags:[],checkedNodes:[],filtering:!1,suggestions:[],inputInitialHeight:0,pressDeleteCount:0}},computed:{realSize:function(){var e=(this.elFormItem||{}).elFormItemSize;return this.size||e||(this.$ELEMENT||{}).size},tagSize:function(){return[\"small\",\"mini\"].indexOf(this.realSize)>-1?\"mini\":\"small\"},isDisabled:function(){return this.disabled||(this.elForm||{}).disabled},config:function(){var e=this.props||{},t=this.$attrs;return Object.keys(pc).forEach(function(i){var n=pc[i],r=n.newProp,s=n.type,a=t[i]||t[N(i)];Q(i)&&!Q(e[r])&&(s===Boolean&&\"\"===a&&(a=!0),e[r]=a)}),e},multiple:function(){return this.config.multiple},leafOnly:function(){return!this.config.checkStrictly},readonly:function(){return!this.filterable||this.multiple},clearBtnVisible:function(){return!(!this.clearable||this.isDisabled||this.filtering||!this.inputHover)&&(this.multiple?!!this.checkedNodes.filter(function(e){return!e.isDisabled}).length:!!this.presentText)},panel:function(){return this.$refs.panel}},watch:{disabled:function(){this.computePresentContent()},value:function(e){I(e,this.checkedValue)||(this.checkedValue=e,this.computePresentContent())},checkedValue:function(e){var t=this.value,i=this.dropDownVisible,n=this.config,r=n.checkStrictly,s=n.multiple;I(e,t)&&!b(t)||(this.computePresentContent(),s||r||!i||this.toggleDropDownVisible(!1),this.$emit(\"input\",e),this.$emit(\"change\",e),this.dispatch(\"ElFormItem\",\"el.form.change\",[e]))},options:{handler:function(){this.$nextTick(this.computePresentContent)},deep:!0},presentText:function(e){this.inputValue=e},presentTags:function(e,t){this.multiple&&(e.length||t.length)&&this.$nextTick(this.updateStyle)},filtering:function(e){this.$nextTick(this.updatePopper)}},mounted:function(){var e=this,t=this.$refs.input;t&&t.$el&&(this.inputInitialHeight=t.$el.offsetHeight||mc[this.realSize]||40),A(this.value)||this.computePresentContent(),this.filterHandler=et()(this.debounce,function(){var t=e.inputValue;if(t){var i=e.beforeFilter(t);i&&i.then?i.then(e.getSuggestions):!1!==i?e.getSuggestions():e.filtering=!1}else e.filtering=!1}),Ye(this.$el,this.updateStyle)},beforeDestroy:function(){Ke(this.$el,this.updateStyle)},methods:{getMigratingConfig:function(){return{props:{\"expand-trigger\":\"expand-trigger is removed, use `props.expandTrigger` instead.\",\"change-on-select\":\"change-on-select is removed, use `props.checkStrictly` instead.\",\"hover-threshold\":\"hover-threshold is removed, use `props.hoverThreshold` instead\"},events:{\"active-item-change\":\"active-item-change is renamed to expand-change\"}}},toggleDropDownVisible:function(e){var t=this;if(!this.isDisabled){var i=this.dropDownVisible,n=this.$refs.input;(e=Q(e)?e:!i)!==i&&(this.dropDownVisible=e,e&&this.$nextTick(function(){t.updatePopper(),t.panel.scrollIntoView()}),n.$refs.input.setAttribute(\"aria-expanded\",e),this.$emit(\"visible-change\",e))}},handleDropdownLeave:function(){this.filtering=!1,this.inputValue=this.presentText},handleKeyDown:function(e){switch(e.keyCode){case dc.enter:this.toggleDropDownVisible();break;case dc.down:this.toggleDropDownVisible(!0),this.focusFirstNode(),e.preventDefault();break;case dc.esc:case dc.tab:this.toggleDropDownVisible(!1)}},handleFocus:function(e){this.$emit(\"focus\",e)},handleBlur:function(e){this.$emit(\"blur\",e)},handleInput:function(e,t){!this.dropDownVisible&&this.toggleDropDownVisible(!0),t&&t.isComposing||(e?this.filterHandler():this.filtering=!1)},handleClear:function(){this.presentText=\"\",this.panel.clearCheckedNodes()},handleExpandChange:function(e){this.$nextTick(this.updatePopper.bind(this)),this.$emit(\"expand-change\",e),this.$emit(\"active-item-change\",e)},focusFirstNode:function(){var e=this;this.$nextTick(function(){var t=e.filtering,i=e.$refs,n=i.popper,r=i.suggestionPanel,s=null;t&&r?s=r.$el.querySelector(\".el-cascader__suggestion-item\"):s=n.querySelector(\".el-cascader-menu\").querySelector('.el-cascader-node[tabindex=\"-1\"]');s&&(s.focus(),!t&&s.click())})},computePresentContent:function(){var e=this;this.$nextTick(function(){e.config.multiple?(e.computePresentTags(),e.presentText=e.presentTags.length?\" \":null):e.computePresentText()})},computePresentText:function(){var e=this.checkedValue,t=this.config;if(!A(e)){var i=this.panel.getNodeByValue(e);if(i&&(t.checkStrictly||i.isLeaf))return void(this.presentText=i.getText(this.showAllLevels,this.separator))}this.presentText=null},computePresentTags:function(){var e=this.isDisabled,t=this.leafOnly,i=this.showAllLevels,n=this.separator,r=this.collapseTags,s=this.getCheckedNodes(t),a=[],o=function(t){return{node:t,key:t.uid,text:t.getText(i,n),hitState:!1,closable:!e&&!t.isDisabled}};if(s.length){var l=s[0],u=s.slice(1),c=u.length;a.push(o(l)),c&&(r?a.push({key:-1,text:\"+ \"+c,closable:!1}):u.forEach(function(e){return a.push(o(e))}))}this.checkedNodes=s,this.presentTags=a},getSuggestions:function(){var e=this,t=this.filterMethod;g(t)||(t=function(e,t){return e.text.includes(t)});var i=this.panel.getFlattedNodes(this.leafOnly).filter(function(i){return!i.isDisabled&&(i.text=i.getText(e.showAllLevels,e.separator)||\"\",t(i,e.inputValue))});this.multiple?this.presentTags.forEach(function(e){e.hitState=!1}):i.forEach(function(t){t.checked=I(e.checkedValue,t.getValueByOption())}),this.filtering=!0,this.suggestions=i,this.$nextTick(this.updatePopper)},handleSuggestionKeyDown:function(e){var t=e.keyCode,i=e.target;switch(t){case dc.enter:i.click();break;case dc.up:var n=i.previousElementSibling;n&&n.focus();break;case dc.down:var r=i.nextElementSibling;r&&r.focus();break;case dc.esc:case dc.tab:this.toggleDropDownVisible(!1)}},handleDelete:function(){var e=this.inputValue,t=this.pressDeleteCount,i=this.presentTags,n=i.length-1,r=i[n];this.pressDeleteCount=e?0:t+1,r&&this.pressDeleteCount&&(r.hitState?this.deleteTag(n):r.hitState=!0)},handleSuggestionClick:function(e){var t=this.multiple,i=this.suggestions[e];if(t){var n=i.checked;i.doCheck(!n),this.panel.calculateMultiCheckedValue()}else this.checkedValue=i.getValueByOption(),this.toggleDropDownVisible(!1)},deleteTag:function(e){var t=this.checkedValue,i=t[e];this.checkedValue=t.filter(function(t,i){return i!==e}),this.$emit(\"remove-tag\",i)},updateStyle:function(){var e=this.$el,t=this.inputInitialHeight;if(!this.$isServer&&e){var i=this.$refs.suggestionPanel,n=e.querySelector(\".el-input__inner\");if(n){var r=e.querySelector(\".el-cascader__tags\"),s=null;if(i&&(s=i.$el))s.querySelector(\".el-cascader__suggestion-list\").style.minWidth=n.offsetWidth+\"px\";if(r){var a=r.offsetHeight,o=Math.max(a+6,t)+\"px\";n.style.height=o,this.updatePopper()}}}},getCheckedNodes:function(e){return this.panel.getCheckedNodes(e)}}},Yu,[],!1,null,null,null);vc.options.__file=\"packages/cascader/src/cascader.vue\";var gc=vc.exports;gc.install=function(e){e.component(gc.name,gc)};var bc=gc,yc=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{directives:[{name:\"clickoutside\",rawName:\"v-clickoutside\",value:e.hide,expression:\"hide\"}],class:[\"el-color-picker\",e.colorDisabled?\"is-disabled\":\"\",e.colorSize?\"el-color-picker--\"+e.colorSize:\"\"]},[e.colorDisabled?i(\"div\",{staticClass:\"el-color-picker__mask\"}):e._e(),i(\"div\",{staticClass:\"el-color-picker__trigger\",on:{click:e.handleTrigger}},[i(\"span\",{staticClass:\"el-color-picker__color\",class:{\"is-alpha\":e.showAlpha}},[i(\"span\",{staticClass:\"el-color-picker__color-inner\",style:{backgroundColor:e.displayedColor}}),e.value||e.showPanelColor?e._e():i(\"span\",{staticClass:\"el-color-picker__empty el-icon-close\"})]),i(\"span\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.value||e.showPanelColor,expression:\"value || showPanelColor\"}],staticClass:\"el-color-picker__icon el-icon-arrow-down\"})]),i(\"picker-dropdown\",{ref:\"dropdown\",class:[\"el-color-picker__panel\",e.popperClass||\"\"],attrs:{color:e.color,\"show-alpha\":e.showAlpha,predefine:e.predefine},on:{pick:e.confirmValue,clear:e.clearValue},model:{value:e.showPicker,callback:function(t){e.showPicker=t},expression:\"showPicker\"}})],1)};yc._withStripped=!0;var wc=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e};var _c=function(e,t,i){return[e,t*i/((e=(2-t)*i)<1?e:2-e)||0,e/2]},xc=function(e,t){var i;\"string\"==typeof(i=e)&&-1!==i.indexOf(\".\")&&1===parseFloat(i)&&(e=\"100%\");var n=function(e){return\"string\"==typeof e&&-1!==e.indexOf(\"%\")}(e);return e=Math.min(t,Math.max(0,parseFloat(e))),n&&(e=parseInt(e*t,10)/100),Math.abs(e-t)<1e-6?1:e%t/parseFloat(t)},Cc={10:\"A\",11:\"B\",12:\"C\",13:\"D\",14:\"E\",15:\"F\"},kc={A:10,B:11,C:12,D:13,E:14,F:15},Sc=function(e){return 2===e.length?16*(kc[e[0].toUpperCase()]||+e[0])+(kc[e[1].toUpperCase()]||+e[1]):kc[e[1].toUpperCase()]||+e[1]},Dc=function(e,t,i){e=xc(e,255),t=xc(t,255),i=xc(i,255);var n,r=Math.max(e,t,i),s=Math.min(e,t,i),a=void 0,o=r,l=r-s;if(n=0===r?0:l/r,r===s)a=0;else{switch(r){case e:a=(t-i)/l+(t2?parseFloat(e):parseInt(e,10)});if(4===n.length?this._alpha=Math.floor(100*parseFloat(n[3])):3===n.length&&(this._alpha=100),n.length>=3){var r=function(e,t,i){i/=100;var n=t/=100,r=Math.max(i,.01);return t*=(i*=2)<=1?i:2-i,n*=r<=1?r:2-r,{h:e,s:100*(0===i?2*n/(r+n):2*t/(i+t)),v:(i+t)/2*100}}(n[0],n[1],n[2]);i(r.h,r.s,r.v)}}else if(-1!==e.indexOf(\"hsv\")){var s=e.replace(/hsva|hsv|\\(|\\)/gm,\"\").split(/\\s|,/g).filter(function(e){return\"\"!==e}).map(function(e,t){return t>2?parseFloat(e):parseInt(e,10)});4===s.length?this._alpha=Math.floor(100*parseFloat(s[3])):3===s.length&&(this._alpha=100),s.length>=3&&i(s[0],s[1],s[2])}else if(-1!==e.indexOf(\"rgb\")){var a=e.replace(/rgba|rgb|\\(|\\)/gm,\"\").split(/\\s|,/g).filter(function(e){return\"\"!==e}).map(function(e,t){return t>2?parseFloat(e):parseInt(e,10)});if(4===a.length?this._alpha=Math.floor(100*parseFloat(a[3])):3===a.length&&(this._alpha=100),a.length>=3){var o=Dc(a[0],a[1],a[2]);i(o.h,o.s,o.v)}}else if(-1!==e.indexOf(\"#\")){var l=e.replace(\"#\",\"\").trim();if(!/^(?:[0-9a-fA-F]{3}){1,2}$/.test(l))return;var u=void 0,c=void 0,h=void 0;3===l.length?(u=Sc(l[0]+l[0]),c=Sc(l[1]+l[1]),h=Sc(l[2]+l[2])):6!==l.length&&8!==l.length||(u=Sc(l.substring(0,2)),c=Sc(l.substring(2,4)),h=Sc(l.substring(4,6))),8===l.length?this._alpha=Math.floor(Sc(l.substring(6))/255*100):3!==l.length&&6!==l.length||(this._alpha=100);var d=Dc(u,c,h);i(d.h,d.s,d.v)}},e.prototype.compare=function(e){return Math.abs(e._hue-this._hue)<2&&Math.abs(e._saturation-this._saturation)<1&&Math.abs(e._value-this._value)<1&&Math.abs(e._alpha-this._alpha)<1},e.prototype.doOnChange=function(){var e=this._hue,t=this._saturation,i=this._value,n=this._alpha,r=this.format;if(this.enableAlpha)switch(r){case\"hsl\":var s=_c(e,t/100,i/100);this.value=\"hsla(\"+e+\", \"+Math.round(100*s[1])+\"%, \"+Math.round(100*s[2])+\"%, \"+n/100+\")\";break;case\"hsv\":this.value=\"hsva(\"+e+\", \"+Math.round(t)+\"%, \"+Math.round(i)+\"%, \"+n/100+\")\";break;default:var a=$c(e,t,i),o=a.r,l=a.g,u=a.b;this.value=\"rgba(\"+o+\", \"+l+\", \"+u+\", \"+n/100+\")\"}else switch(r){case\"hsl\":var c=_c(e,t/100,i/100);this.value=\"hsl(\"+e+\", \"+Math.round(100*c[1])+\"%, \"+Math.round(100*c[2])+\"%)\";break;case\"hsv\":this.value=\"hsv(\"+e+\", \"+Math.round(t)+\"%, \"+Math.round(i)+\"%)\";break;case\"rgb\":var h=$c(e,t,i),d=h.r,p=h.g,f=h.b;this.value=\"rgb(\"+d+\", \"+p+\", \"+f+\")\";break;default:this.value=function(e){var t=e.r,i=e.g,n=e.b,r=function(e){e=Math.min(Math.round(e),255);var t=Math.floor(e/16),i=e%16;return\"\"+(Cc[t]||t)+(Cc[i]||i)};return isNaN(t)||isNaN(i)||isNaN(n)?\"\":\"#\"+r(t)+r(i)+r(n)}($c(e,t,i))}},e}(),Tc=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition\",{attrs:{name:\"el-zoom-in-top\"},on:{\"after-leave\":e.doDestroy}},[i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.showPopper,expression:\"showPopper\"}],staticClass:\"el-color-dropdown\"},[i(\"div\",{staticClass:\"el-color-dropdown__main-wrapper\"},[i(\"hue-slider\",{ref:\"hue\",staticStyle:{float:\"right\"},attrs:{color:e.color,vertical:\"\"}}),i(\"sv-panel\",{ref:\"sl\",attrs:{color:e.color}})],1),e.showAlpha?i(\"alpha-slider\",{ref:\"alpha\",attrs:{color:e.color}}):e._e(),e.predefine?i(\"predefine\",{attrs:{color:e.color,colors:e.predefine}}):e._e(),i(\"div\",{staticClass:\"el-color-dropdown__btns\"},[i(\"span\",{staticClass:\"el-color-dropdown__value\"},[i(\"el-input\",{attrs:{\"validate-event\":!1,size:\"mini\"},on:{blur:e.handleConfirm},nativeOn:{keyup:function(t){return\"button\"in t||!e._k(t.keyCode,\"enter\",13,t.key,\"Enter\")?e.handleConfirm(t):null}},model:{value:e.customInput,callback:function(t){e.customInput=t},expression:\"customInput\"}})],1),i(\"el-button\",{staticClass:\"el-color-dropdown__link-btn\",attrs:{size:\"mini\",type:\"text\"},on:{click:function(t){e.$emit(\"clear\")}}},[e._v(\"\\n \"+e._s(e.t(\"el.colorpicker.clear\"))+\"\\n \")]),i(\"el-button\",{staticClass:\"el-color-dropdown__btn\",attrs:{plain:\"\",size:\"mini\"},on:{click:e.confirmValue}},[e._v(\"\\n \"+e._s(e.t(\"el.colorpicker.confirm\"))+\"\\n \")])],1)],1)])};Tc._withStripped=!0;var Mc=function(){var e=this.$createElement,t=this._self._c||e;return t(\"div\",{staticClass:\"el-color-svpanel\",style:{backgroundColor:this.background}},[t(\"div\",{staticClass:\"el-color-svpanel__white\"}),t(\"div\",{staticClass:\"el-color-svpanel__black\"}),t(\"div\",{staticClass:\"el-color-svpanel__cursor\",style:{top:this.cursorTop+\"px\",left:this.cursorLeft+\"px\"}},[t(\"div\")])])};Mc._withStripped=!0;var Nc=!1,Pc=function(e,t){if(!h.a.prototype.$isServer){var i=function(e){t.drag&&t.drag(e)},n=function e(n){document.removeEventListener(\"mousemove\",i),document.removeEventListener(\"mouseup\",e),document.onselectstart=null,document.ondragstart=null,Nc=!1,t.end&&t.end(n)};e.addEventListener(\"mousedown\",function(e){Nc||(document.onselectstart=function(){return!1},document.ondragstart=function(){return!1},document.addEventListener(\"mousemove\",i),document.addEventListener(\"mouseup\",n),Nc=!0,t.start&&t.start(e))})}},Oc=r({name:\"el-sl-panel\",props:{color:{required:!0}},computed:{colorValue:function(){return{hue:this.color.get(\"hue\"),value:this.color.get(\"value\")}}},watch:{colorValue:function(){this.update()}},methods:{update:function(){var e=this.color.get(\"saturation\"),t=this.color.get(\"value\"),i=this.$el,n=i.clientWidth,r=i.clientHeight;this.cursorLeft=e*n/100,this.cursorTop=(100-t)*r/100,this.background=\"hsl(\"+this.color.get(\"hue\")+\", 100%, 50%)\"},handleDrag:function(e){var t=this.$el.getBoundingClientRect(),i=e.clientX-t.left,n=e.clientY-t.top;i=Math.max(0,i),i=Math.min(i,t.width),n=Math.max(0,n),n=Math.min(n,t.height),this.cursorLeft=i,this.cursorTop=n,this.color.set({saturation:i/t.width*100,value:100-n/t.height*100})}},mounted:function(){var e=this;Pc(this.$el,{drag:function(t){e.handleDrag(t)},end:function(t){e.handleDrag(t)}}),this.update()},data:function(){return{cursorTop:0,cursorLeft:0,background:\"hsl(0, 100%, 50%)\"}}},Mc,[],!1,null,null,null);Oc.options.__file=\"packages/color-picker/src/components/sv-panel.vue\";var Ic=Oc.exports,Ac=function(){var e=this.$createElement,t=this._self._c||e;return t(\"div\",{staticClass:\"el-color-hue-slider\",class:{\"is-vertical\":this.vertical}},[t(\"div\",{ref:\"bar\",staticClass:\"el-color-hue-slider__bar\",on:{click:this.handleClick}}),t(\"div\",{ref:\"thumb\",staticClass:\"el-color-hue-slider__thumb\",style:{left:this.thumbLeft+\"px\",top:this.thumbTop+\"px\"}})])};Ac._withStripped=!0;var Fc=r({name:\"el-color-hue-slider\",props:{color:{required:!0},vertical:Boolean},data:function(){return{thumbLeft:0,thumbTop:0}},computed:{hueValue:function(){return this.color.get(\"hue\")}},watch:{hueValue:function(){this.update()}},methods:{handleClick:function(e){var t=this.$refs.thumb;e.target!==t&&this.handleDrag(e)},handleDrag:function(e){var t=this.$el.getBoundingClientRect(),i=this.$refs.thumb,n=void 0;if(this.vertical){var r=e.clientY-t.top;r=Math.min(r,t.height-i.offsetHeight/2),r=Math.max(i.offsetHeight/2,r),n=Math.round((r-i.offsetHeight/2)/(t.height-i.offsetHeight)*360)}else{var s=e.clientX-t.left;s=Math.min(s,t.width-i.offsetWidth/2),s=Math.max(i.offsetWidth/2,s),n=Math.round((s-i.offsetWidth/2)/(t.width-i.offsetWidth)*360)}this.color.set(\"hue\",n)},getThumbLeft:function(){if(this.vertical)return 0;var e=this.$el,t=this.color.get(\"hue\");if(!e)return 0;var i=this.$refs.thumb;return Math.round(t*(e.offsetWidth-i.offsetWidth/2)/360)},getThumbTop:function(){if(!this.vertical)return 0;var e=this.$el,t=this.color.get(\"hue\");if(!e)return 0;var i=this.$refs.thumb;return Math.round(t*(e.offsetHeight-i.offsetHeight/2)/360)},update:function(){this.thumbLeft=this.getThumbLeft(),this.thumbTop=this.getThumbTop()}},mounted:function(){var e=this,t=this.$refs,i=t.bar,n=t.thumb,r={drag:function(t){e.handleDrag(t)},end:function(t){e.handleDrag(t)}};Pc(i,r),Pc(n,r),this.update()}},Ac,[],!1,null,null,null);Fc.options.__file=\"packages/color-picker/src/components/hue-slider.vue\";var Lc=Fc.exports,Vc=function(){var e=this.$createElement,t=this._self._c||e;return t(\"div\",{staticClass:\"el-color-alpha-slider\",class:{\"is-vertical\":this.vertical}},[t(\"div\",{ref:\"bar\",staticClass:\"el-color-alpha-slider__bar\",style:{background:this.background},on:{click:this.handleClick}}),t(\"div\",{ref:\"thumb\",staticClass:\"el-color-alpha-slider__thumb\",style:{left:this.thumbLeft+\"px\",top:this.thumbTop+\"px\"}})])};Vc._withStripped=!0;var Bc=r({name:\"el-color-alpha-slider\",props:{color:{required:!0},vertical:Boolean},watch:{\"color._alpha\":function(){this.update()},\"color.value\":function(){this.update()}},methods:{handleClick:function(e){var t=this.$refs.thumb;e.target!==t&&this.handleDrag(e)},handleDrag:function(e){var t=this.$el.getBoundingClientRect(),i=this.$refs.thumb;if(this.vertical){var n=e.clientY-t.top;n=Math.max(i.offsetHeight/2,n),n=Math.min(n,t.height-i.offsetHeight/2),this.color.set(\"alpha\",Math.round((n-i.offsetHeight/2)/(t.height-i.offsetHeight)*100))}else{var r=e.clientX-t.left;r=Math.max(i.offsetWidth/2,r),r=Math.min(r,t.width-i.offsetWidth/2),this.color.set(\"alpha\",Math.round((r-i.offsetWidth/2)/(t.width-i.offsetWidth)*100))}},getThumbLeft:function(){if(this.vertical)return 0;var e=this.$el,t=this.color._alpha;if(!e)return 0;var i=this.$refs.thumb;return Math.round(t*(e.offsetWidth-i.offsetWidth/2)/100)},getThumbTop:function(){if(!this.vertical)return 0;var e=this.$el,t=this.color._alpha;if(!e)return 0;var i=this.$refs.thumb;return Math.round(t*(e.offsetHeight-i.offsetHeight/2)/100)},getBackground:function(){if(this.color&&this.color.value){var e=this.color.toRgb(),t=e.r,i=e.g,n=e.b;return\"linear-gradient(to right, rgba(\"+t+\", \"+i+\", \"+n+\", 0) 0%, rgba(\"+t+\", \"+i+\", \"+n+\", 1) 100%)\"}return null},update:function(){this.thumbLeft=this.getThumbLeft(),this.thumbTop=this.getThumbTop(),this.background=this.getBackground()}},data:function(){return{thumbLeft:0,thumbTop:0,background:null}},mounted:function(){var e=this,t=this.$refs,i=t.bar,n=t.thumb,r={drag:function(t){e.handleDrag(t)},end:function(t){e.handleDrag(t)}};Pc(i,r),Pc(n,r),this.update()}},Vc,[],!1,null,null,null);Bc.options.__file=\"packages/color-picker/src/components/alpha-slider.vue\";var zc=Bc.exports,Hc=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-color-predefine\"},[i(\"div\",{staticClass:\"el-color-predefine__colors\"},e._l(e.rgbaColors,function(t,n){return i(\"div\",{key:e.colors[n],staticClass:\"el-color-predefine__color-selector\",class:{selected:t.selected,\"is-alpha\":t._alpha<100},on:{click:function(t){e.handleSelect(n)}}},[i(\"div\",{style:{\"background-color\":t.value}})])}),0)])};Hc._withStripped=!0;var Rc=r({props:{colors:{type:Array,required:!0},color:{required:!0}},data:function(){return{rgbaColors:this.parseColors(this.colors,this.color)}},methods:{handleSelect:function(e){this.color.fromString(this.colors[e])},parseColors:function(e,t){return e.map(function(e){var i=new Ec;return i.enableAlpha=!0,i.format=\"rgba\",i.fromString(e),i.selected=i.value===t.value,i})}},watch:{\"$parent.currentColor\":function(e){var t=new Ec;t.fromString(e),this.rgbaColors.forEach(function(e){e.selected=t.compare(e)})},colors:function(e){this.rgbaColors=this.parseColors(e,this.color)},color:function(e){this.rgbaColors=this.parseColors(this.colors,e)}}},Hc,[],!1,null,null,null);Rc.options.__file=\"packages/color-picker/src/components/predefine.vue\";var Wc=Rc.exports,jc=r({name:\"el-color-picker-dropdown\",mixins:[Oe,q],components:{SvPanel:Ic,HueSlider:Lc,AlphaSlider:zc,ElInput:ne,ElButton:Et,Predefine:Wc},props:{color:{required:!0},showAlpha:Boolean,predefine:Array},data:function(){return{customInput:\"\"}},computed:{currentColor:function(){var e=this.$parent;return e.value||e.showPanelColor?e.color.value:\"\"}},methods:{confirmValue:function(){this.$emit(\"pick\")},handleConfirm:function(){this.color.fromString(this.customInput)}},mounted:function(){this.$parent.popperElm=this.popperElm=this.$el,this.referenceElm=this.$parent.$el},watch:{showPopper:function(e){var t=this;!0===e&&this.$nextTick(function(){var e=t.$refs,i=e.sl,n=e.hue,r=e.alpha;i&&i.update(),n&&n.update(),r&&r.update()})},currentColor:{immediate:!0,handler:function(e){this.customInput=e}}}},Tc,[],!1,null,null,null);jc.options.__file=\"packages/color-picker/src/components/picker-dropdown.vue\";var qc=jc.exports,Yc=r({name:\"ElColorPicker\",mixins:[l],props:{value:String,showAlpha:Boolean,colorFormat:String,disabled:Boolean,size:String,popperClass:String,predefine:Array},inject:{elForm:{default:\"\"},elFormItem:{default:\"\"}},directives:{Clickoutside:at},computed:{displayedColor:function(){return this.value||this.showPanelColor?this.displayedRgb(this.color,this.showAlpha):\"transparent\"},_elFormItemSize:function(){return(this.elFormItem||{}).elFormItemSize},colorSize:function(){return this.size||this._elFormItemSize||(this.$ELEMENT||{}).size},colorDisabled:function(){return this.disabled||(this.elForm||{}).disabled}},watch:{value:function(e){e?e&&e!==this.color.value&&this.color.fromString(e):this.showPanelColor=!1},color:{deep:!0,handler:function(){this.showPanelColor=!0}},displayedColor:function(e){if(this.showPicker){var t=new Ec({enableAlpha:this.showAlpha,format:this.colorFormat});t.fromString(this.value),e!==this.displayedRgb(t,this.showAlpha)&&this.$emit(\"active-change\",e)}}},methods:{handleTrigger:function(){this.colorDisabled||(this.showPicker=!this.showPicker)},confirmValue:function(){var e=this.color.value;this.$emit(\"input\",e),this.$emit(\"change\",e),this.dispatch(\"ElFormItem\",\"el.form.change\",e),this.showPicker=!1},clearValue:function(){this.$emit(\"input\",null),this.$emit(\"change\",null),null!==this.value&&this.dispatch(\"ElFormItem\",\"el.form.change\",null),this.showPanelColor=!1,this.showPicker=!1,this.resetColor()},hide:function(){this.showPicker=!1,this.resetColor()},resetColor:function(){var e=this;this.$nextTick(function(t){e.value?e.color.fromString(e.value):e.showPanelColor=!1})},displayedRgb:function(e,t){if(!(e instanceof Ec))throw Error(\"color should be instance of Color Class\");var i=e.toRgb(),n=i.r,r=i.g,s=i.b;return t?\"rgba(\"+n+\", \"+r+\", \"+s+\", \"+e.get(\"alpha\")/100+\")\":\"rgb(\"+n+\", \"+r+\", \"+s+\")\"}},mounted:function(){var e=this.value;e&&this.color.fromString(e),this.popperElm=this.$refs.dropdown.$el},data:function(){return{color:new Ec({enableAlpha:this.showAlpha,format:this.colorFormat}),showPicker:!1,showPanelColor:!1}},components:{PickerDropdown:qc}},yc,[],!1,null,null,null);Yc.options.__file=\"packages/color-picker/src/main.vue\";var Kc=Yc.exports;Kc.install=function(e){e.component(Kc.name,Kc)};var Gc=Kc,Uc=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-transfer\"},[i(\"transfer-panel\",e._b({ref:\"leftPanel\",attrs:{data:e.sourceData,title:e.titles[0]||e.t(\"el.transfer.titles.0\"),\"default-checked\":e.leftDefaultChecked,placeholder:e.filterPlaceholder||e.t(\"el.transfer.filterPlaceholder\")},on:{\"checked-change\":e.onSourceCheckedChange}},\"transfer-panel\",e.$props,!1),[e._t(\"left-footer\")],2),i(\"div\",{staticClass:\"el-transfer__buttons\"},[i(\"el-button\",{class:[\"el-transfer__button\",e.hasButtonTexts?\"is-with-texts\":\"\"],attrs:{type:\"primary\",disabled:0===e.rightChecked.length},nativeOn:{click:function(t){return e.addToLeft(t)}}},[i(\"i\",{staticClass:\"el-icon-arrow-left\"}),void 0!==e.buttonTexts[0]?i(\"span\",[e._v(e._s(e.buttonTexts[0]))]):e._e()]),i(\"el-button\",{class:[\"el-transfer__button\",e.hasButtonTexts?\"is-with-texts\":\"\"],attrs:{type:\"primary\",disabled:0===e.leftChecked.length},nativeOn:{click:function(t){return e.addToRight(t)}}},[void 0!==e.buttonTexts[1]?i(\"span\",[e._v(e._s(e.buttonTexts[1]))]):e._e(),i(\"i\",{staticClass:\"el-icon-arrow-right\"})])],1),i(\"transfer-panel\",e._b({ref:\"rightPanel\",attrs:{data:e.targetData,title:e.titles[1]||e.t(\"el.transfer.titles.1\"),\"default-checked\":e.rightDefaultChecked,placeholder:e.filterPlaceholder||e.t(\"el.transfer.filterPlaceholder\")},on:{\"checked-change\":e.onTargetCheckedChange}},\"transfer-panel\",e.$props,!1),[e._t(\"right-footer\")],2)],1)};Uc._withStripped=!0;var Xc=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-transfer-panel\"},[i(\"p\",{staticClass:\"el-transfer-panel__header\"},[i(\"el-checkbox\",{attrs:{indeterminate:e.isIndeterminate},on:{change:e.handleAllCheckedChange},model:{value:e.allChecked,callback:function(t){e.allChecked=t},expression:\"allChecked\"}},[e._v(\"\\n \"+e._s(e.title)+\"\\n \"),i(\"span\",[e._v(e._s(e.checkedSummary))])])],1),i(\"div\",{class:[\"el-transfer-panel__body\",e.hasFooter?\"is-with-footer\":\"\"]},[e.filterable?i(\"el-input\",{staticClass:\"el-transfer-panel__filter\",attrs:{size:\"small\",placeholder:e.placeholder},nativeOn:{mouseenter:function(t){e.inputHover=!0},mouseleave:function(t){e.inputHover=!1}},model:{value:e.query,callback:function(t){e.query=t},expression:\"query\"}},[i(\"i\",{class:[\"el-input__icon\",\"el-icon-\"+e.inputIcon],attrs:{slot:\"prefix\"},on:{click:e.clearQuery},slot:\"prefix\"})]):e._e(),i(\"el-checkbox-group\",{directives:[{name:\"show\",rawName:\"v-show\",value:!e.hasNoMatch&&e.data.length>0,expression:\"!hasNoMatch && data.length > 0\"}],staticClass:\"el-transfer-panel__list\",class:{\"is-filterable\":e.filterable},model:{value:e.checked,callback:function(t){e.checked=t},expression:\"checked\"}},e._l(e.filteredData,function(t){return i(\"el-checkbox\",{key:t[e.keyProp],staticClass:\"el-transfer-panel__item\",attrs:{label:t[e.keyProp],disabled:t[e.disabledProp]}},[i(\"option-content\",{attrs:{option:t}})],1)}),1),i(\"p\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.hasNoMatch,expression:\"hasNoMatch\"}],staticClass:\"el-transfer-panel__empty\"},[e._v(e._s(e.t(\"el.transfer.noMatch\")))]),i(\"p\",{directives:[{name:\"show\",rawName:\"v-show\",value:0===e.data.length&&!e.hasNoMatch,expression:\"data.length === 0 && !hasNoMatch\"}],staticClass:\"el-transfer-panel__empty\"},[e._v(e._s(e.t(\"el.transfer.noData\")))])],1),e.hasFooter?i(\"p\",{staticClass:\"el-transfer-panel__footer\"},[e._t(\"default\")],2):e._e()])};Xc._withStripped=!0;var Jc=r({mixins:[q],name:\"ElTransferPanel\",componentName:\"ElTransferPanel\",components:{ElCheckboxGroup:Yi,ElCheckbox:Vi,ElInput:ne,OptionContent:{props:{option:Object},render:function(e){var t=function e(t){return\"ElTransferPanel\"===t.$options.componentName?t:t.$parent?e(t.$parent):t}(this),i=t.$parent||t;return t.renderContent?t.renderContent(e,this.option):i.$scopedSlots.default?i.$scopedSlots.default({option:this.option}):e(\"span\",[this.option[t.labelProp]||this.option[t.keyProp]])}}},props:{data:{type:Array,default:function(){return[]}},renderContent:Function,placeholder:String,title:String,filterable:Boolean,format:Object,filterMethod:Function,defaultChecked:Array,props:Object},data:function(){return{checked:[],allChecked:!1,query:\"\",inputHover:!1,checkChangeByUser:!0}},watch:{checked:function(e,t){if(this.updateAllChecked(),this.checkChangeByUser){var i=e.concat(t).filter(function(i){return-1===e.indexOf(i)||-1===t.indexOf(i)});this.$emit(\"checked-change\",e,i)}else this.$emit(\"checked-change\",e),this.checkChangeByUser=!0},data:function(){var e=this,t=[],i=this.filteredData.map(function(t){return t[e.keyProp]});this.checked.forEach(function(e){i.indexOf(e)>-1&&t.push(e)}),this.checkChangeByUser=!1,this.checked=t},checkableData:function(){this.updateAllChecked()},defaultChecked:{immediate:!0,handler:function(e,t){var i=this;if(!t||e.length!==t.length||!e.every(function(e){return t.indexOf(e)>-1})){var n=[],r=this.checkableData.map(function(e){return e[i.keyProp]});e.forEach(function(e){r.indexOf(e)>-1&&n.push(e)}),this.checkChangeByUser=!1,this.checked=n}}}},computed:{filteredData:function(){var e=this;return this.data.filter(function(t){return\"function\"==typeof e.filterMethod?e.filterMethod(e.query,t):(t[e.labelProp]||t[e.keyProp].toString()).toLowerCase().indexOf(e.query.toLowerCase())>-1})},checkableData:function(){var e=this;return this.filteredData.filter(function(t){return!t[e.disabledProp]})},checkedSummary:function(){var e=this.checked.length,t=this.data.length,i=this.format,n=i.noChecked,r=i.hasChecked;return n&&r?e>0?r.replace(/\\${checked}/g,e).replace(/\\${total}/g,t):n.replace(/\\${total}/g,t):e+\"/\"+t},isIndeterminate:function(){var e=this.checked.length;return e>0&&e0&&0===this.filteredData.length},inputIcon:function(){return this.query.length>0&&this.inputHover?\"circle-close\":\"search\"},labelProp:function(){return this.props.label||\"label\"},keyProp:function(){return this.props.key||\"key\"},disabledProp:function(){return this.props.disabled||\"disabled\"},hasFooter:function(){return!!this.$slots.default}},methods:{updateAllChecked:function(){var e=this,t=this.checkableData.map(function(t){return t[e.keyProp]});this.allChecked=t.length>0&&t.every(function(t){return e.checked.indexOf(t)>-1})},handleAllCheckedChange:function(e){var t=this;this.checked=e?this.checkableData.map(function(e){return e[t.keyProp]}):[]},clearQuery:function(){\"circle-close\"===this.inputIcon&&(this.query=\"\")}}},Xc,[],!1,null,null,null);Jc.options.__file=\"packages/transfer/src/transfer-panel.vue\";var Zc=r({name:\"ElTransfer\",mixins:[l,q,K],components:{TransferPanel:Jc.exports,ElButton:Et},props:{data:{type:Array,default:function(){return[]}},titles:{type:Array,default:function(){return[]}},buttonTexts:{type:Array,default:function(){return[]}},filterPlaceholder:{type:String,default:\"\"},filterMethod:Function,leftDefaultChecked:{type:Array,default:function(){return[]}},rightDefaultChecked:{type:Array,default:function(){return[]}},renderContent:Function,value:{type:Array,default:function(){return[]}},format:{type:Object,default:function(){return{}}},filterable:Boolean,props:{type:Object,default:function(){return{label:\"label\",key:\"key\",disabled:\"disabled\"}}},targetOrder:{type:String,default:\"original\"}},data:function(){return{leftChecked:[],rightChecked:[]}},computed:{dataObj:function(){var e=this.props.key;return this.data.reduce(function(t,i){return(t[i[e]]=i)&&t},{})},sourceData:function(){var e=this;return this.data.filter(function(t){return-1===e.value.indexOf(t[e.props.key])})},targetData:function(){var e=this;return\"original\"===this.targetOrder?this.data.filter(function(t){return e.value.indexOf(t[e.props.key])>-1}):this.value.reduce(function(t,i){var n=e.dataObj[i];return n&&t.push(n),t},[])},hasButtonTexts:function(){return 2===this.buttonTexts.length}},watch:{value:function(e){this.dispatch(\"ElFormItem\",\"el.form.change\",e)}},methods:{getMigratingConfig:function(){return{props:{\"footer-format\":\"footer-format is renamed to format.\"}}},onSourceCheckedChange:function(e,t){this.leftChecked=e,void 0!==t&&this.$emit(\"left-check-change\",e,t)},onTargetCheckedChange:function(e,t){this.rightChecked=e,void 0!==t&&this.$emit(\"right-check-change\",e,t)},addToLeft:function(){var e=this.value.slice();this.rightChecked.forEach(function(t){var i=e.indexOf(t);i>-1&&e.splice(i,1)}),this.$emit(\"input\",e),this.$emit(\"change\",e,\"left\",this.rightChecked)},addToRight:function(){var e=this,t=this.value.slice(),i=[],n=this.props.key;this.data.forEach(function(t){var r=t[n];e.leftChecked.indexOf(r)>-1&&-1===e.value.indexOf(r)&&i.push(r)}),t=\"unshift\"===this.targetOrder?i.concat(t):t.concat(i),this.$emit(\"input\",t),this.$emit(\"change\",t,\"right\",this.leftChecked)},clearQuery:function(e){\"left\"===e?this.$refs.leftPanel.query=\"\":\"right\"===e&&(this.$refs.rightPanel.query=\"\")}}},Uc,[],!1,null,null,null);Zc.options.__file=\"packages/transfer/src/main.vue\";var Qc=Zc.exports;Qc.install=function(e){e.component(Qc.name,Qc)};var eh=Qc,th=function(){var e=this.$createElement;return(this._self._c||e)(\"section\",{staticClass:\"el-container\",class:{\"is-vertical\":this.isVertical}},[this._t(\"default\")],2)};th._withStripped=!0;var ih=r({name:\"ElContainer\",componentName:\"ElContainer\",props:{direction:String},computed:{isVertical:function(){return\"vertical\"===this.direction||\"horizontal\"!==this.direction&&(!(!this.$slots||!this.$slots.default)&&this.$slots.default.some(function(e){var t=e.componentOptions&&e.componentOptions.tag;return\"el-header\"===t||\"el-footer\"===t}))}}},th,[],!1,null,null,null);ih.options.__file=\"packages/container/src/main.vue\";var nh=ih.exports;nh.install=function(e){e.component(nh.name,nh)};var rh=nh,sh=function(){var e=this.$createElement;return(this._self._c||e)(\"header\",{staticClass:\"el-header\",style:{height:this.height}},[this._t(\"default\")],2)};sh._withStripped=!0;var ah=r({name:\"ElHeader\",componentName:\"ElHeader\",props:{height:{type:String,default:\"60px\"}}},sh,[],!1,null,null,null);ah.options.__file=\"packages/header/src/main.vue\";var oh=ah.exports;oh.install=function(e){e.component(oh.name,oh)};var lh=oh,uh=function(){var e=this.$createElement;return(this._self._c||e)(\"aside\",{staticClass:\"el-aside\",style:{width:this.width}},[this._t(\"default\")],2)};uh._withStripped=!0;var ch=r({name:\"ElAside\",componentName:\"ElAside\",props:{width:{type:String,default:\"300px\"}}},uh,[],!1,null,null,null);ch.options.__file=\"packages/aside/src/main.vue\";var hh=ch.exports;hh.install=function(e){e.component(hh.name,hh)};var dh=hh,ph=function(){var e=this.$createElement;return(this._self._c||e)(\"main\",{staticClass:\"el-main\"},[this._t(\"default\")],2)};ph._withStripped=!0;var fh=r({name:\"ElMain\",componentName:\"ElMain\"},ph,[],!1,null,null,null);fh.options.__file=\"packages/main/src/main.vue\";var mh=fh.exports;mh.install=function(e){e.component(mh.name,mh)};var vh=mh,gh=function(){var e=this.$createElement;return(this._self._c||e)(\"footer\",{staticClass:\"el-footer\",style:{height:this.height}},[this._t(\"default\")],2)};gh._withStripped=!0;var bh=r({name:\"ElFooter\",componentName:\"ElFooter\",props:{height:{type:String,default:\"60px\"}}},gh,[],!1,null,null,null);bh.options.__file=\"packages/footer/src/main.vue\";var yh=bh.exports;yh.install=function(e){e.component(yh.name,yh)};var wh=yh,_h=r({name:\"ElTimeline\",props:{reverse:{type:Boolean,default:!1}},provide:function(){return{timeline:this}},render:function(){var e=arguments[0],t=this.reverse,i={\"el-timeline\":!0,\"is-reverse\":t},n=this.$slots.default||[];return t&&(n=n.reverse()),e(\"ul\",{class:i},[n])}},void 0,void 0,!1,null,null,null);_h.options.__file=\"packages/timeline/src/main.vue\";var xh=_h.exports;xh.install=function(e){e.component(xh.name,xh)};var Ch=xh,kh=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"li\",{staticClass:\"el-timeline-item\"},[i(\"div\",{staticClass:\"el-timeline-item__tail\"}),e.$slots.dot?e._e():i(\"div\",{staticClass:\"el-timeline-item__node\",class:[\"el-timeline-item__node--\"+(e.size||\"\"),\"el-timeline-item__node--\"+(e.type||\"\")],style:{backgroundColor:e.color}},[e.icon?i(\"i\",{staticClass:\"el-timeline-item__icon\",class:e.icon}):e._e()]),e.$slots.dot?i(\"div\",{staticClass:\"el-timeline-item__dot\"},[e._t(\"dot\")],2):e._e(),i(\"div\",{staticClass:\"el-timeline-item__wrapper\"},[e.hideTimestamp||\"top\"!==e.placement?e._e():i(\"div\",{staticClass:\"el-timeline-item__timestamp is-top\"},[e._v(\"\\n \"+e._s(e.timestamp)+\"\\n \")]),i(\"div\",{staticClass:\"el-timeline-item__content\"},[e._t(\"default\")],2),e.hideTimestamp||\"bottom\"!==e.placement?e._e():i(\"div\",{staticClass:\"el-timeline-item__timestamp is-bottom\"},[e._v(\"\\n \"+e._s(e.timestamp)+\"\\n \")])])])};kh._withStripped=!0;var Sh=r({name:\"ElTimelineItem\",inject:[\"timeline\"],props:{timestamp:String,hideTimestamp:{type:Boolean,default:!1},placement:{type:String,default:\"bottom\"},type:String,color:String,size:{type:String,default:\"normal\"},icon:String}},kh,[],!1,null,null,null);Sh.options.__file=\"packages/timeline/src/item.vue\";var Dh=Sh.exports;Dh.install=function(e){e.component(Dh.name,Dh)};var $h=Dh,Eh=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"a\",e._b({class:[\"el-link\",e.type?\"el-link--\"+e.type:\"\",e.disabled&&\"is-disabled\",e.underline&&!e.disabled&&\"is-underline\"],attrs:{href:e.disabled?null:e.href},on:{click:e.handleClick}},\"a\",e.$attrs,!1),[e.icon?i(\"i\",{class:e.icon}):e._e(),e.$slots.default?i(\"span\",{staticClass:\"el-link--inner\"},[e._t(\"default\")],2):e._e(),e.$slots.icon?[e.$slots.icon?e._t(\"icon\"):e._e()]:e._e()],2)};Eh._withStripped=!0;var Th=r({name:\"ElLink\",props:{type:{type:String,default:\"default\"},underline:{type:Boolean,default:!0},disabled:Boolean,href:String,icon:String},methods:{handleClick:function(e){this.disabled||this.href||this.$emit(\"click\",e)}}},Eh,[],!1,null,null,null);Th.options.__file=\"packages/link/src/main.vue\";var Mh=Th.exports;Mh.install=function(e){e.component(Mh.name,Mh)};var Nh=Mh,Ph=function(e,t){var i=t._c;return i(\"div\",t._g(t._b({class:[t.data.staticClass,\"el-divider\",\"el-divider--\"+t.props.direction]},\"div\",t.data.attrs,!1),t.listeners),[t.slots().default&&\"vertical\"!==t.props.direction?i(\"div\",{class:[\"el-divider__text\",\"is-\"+t.props.contentPosition]},[t._t(\"default\")],2):t._e()])};Ph._withStripped=!0;var Oh=r({name:\"ElDivider\",props:{direction:{type:String,default:\"horizontal\",validator:function(e){return-1!==[\"horizontal\",\"vertical\"].indexOf(e)}},contentPosition:{type:String,default:\"center\",validator:function(e){return-1!==[\"left\",\"center\",\"right\"].indexOf(e)}}}},Ph,[],!0,null,null,null);Oh.options.__file=\"packages/divider/src/main.vue\";var Ih=Oh.exports;Ih.install=function(e){e.component(Ih.name,Ih)};var Ah=Ih,Fh=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-image\"},[e.loading?e._t(\"placeholder\",[i(\"div\",{staticClass:\"el-image__placeholder\"})]):e.error?e._t(\"error\",[i(\"div\",{staticClass:\"el-image__error\"},[e._v(e._s(e.t(\"el.image.error\")))])]):i(\"img\",e._g(e._b({staticClass:\"el-image__inner\",class:{\"el-image__inner--center\":e.alignCenter,\"el-image__preview\":e.preview},style:e.imageStyle,attrs:{src:e.src},on:{click:e.clickHandler}},\"img\",e.$attrs,!1),e.$listeners)),e.preview?[e.showViewer?i(\"image-viewer\",{attrs:{\"z-index\":e.zIndex,\"initial-index\":e.imageIndex,\"on-close\":e.closeViewer,\"url-list\":e.previewSrcList}}):e._e()]:e._e()],2)};Fh._withStripped=!0;var Lh=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition\",{attrs:{name:\"viewer-fade\"}},[i(\"div\",{ref:\"el-image-viewer__wrapper\",staticClass:\"el-image-viewer__wrapper\",style:{\"z-index\":e.zIndex},attrs:{tabindex:\"-1\"}},[i(\"div\",{staticClass:\"el-image-viewer__mask\"}),i(\"span\",{staticClass:\"el-image-viewer__btn el-image-viewer__close\",on:{click:e.hide}},[i(\"i\",{staticClass:\"el-icon-circle-close\"})]),e.isSingle?e._e():[i(\"span\",{staticClass:\"el-image-viewer__btn el-image-viewer__prev\",class:{\"is-disabled\":!e.infinite&&e.isFirst},on:{click:e.prev}},[i(\"i\",{staticClass:\"el-icon-arrow-left\"})]),i(\"span\",{staticClass:\"el-image-viewer__btn el-image-viewer__next\",class:{\"is-disabled\":!e.infinite&&e.isLast},on:{click:e.next}},[i(\"i\",{staticClass:\"el-icon-arrow-right\"})])],i(\"div\",{staticClass:\"el-image-viewer__btn el-image-viewer__actions\"},[i(\"div\",{staticClass:\"el-image-viewer__actions__inner\"},[i(\"i\",{staticClass:\"el-icon-zoom-out\",on:{click:function(t){e.handleActions(\"zoomOut\")}}}),i(\"i\",{staticClass:\"el-icon-zoom-in\",on:{click:function(t){e.handleActions(\"zoomIn\")}}}),i(\"i\",{staticClass:\"el-image-viewer__actions__divider\"}),i(\"i\",{class:e.mode.icon,on:{click:e.toggleMode}}),i(\"i\",{staticClass:\"el-image-viewer__actions__divider\"}),i(\"i\",{staticClass:\"el-icon-refresh-left\",on:{click:function(t){e.handleActions(\"anticlocelise\")}}}),i(\"i\",{staticClass:\"el-icon-refresh-right\",on:{click:function(t){e.handleActions(\"clocelise\")}}})])]),i(\"div\",{staticClass:\"el-image-viewer__canvas\"},e._l(e.urlList,function(t,n){return n===e.index?i(\"img\",{key:t,ref:\"img\",refInFor:!0,staticClass:\"el-image-viewer__img\",style:e.imgStyle,attrs:{src:e.currentImg},on:{load:e.handleImgLoad,error:e.handleImgError,mousedown:e.handleMouseDown}}):e._e()}),0)],2)])};Lh._withStripped=!0;var Vh=Object.assign||function(e){for(var t=1;t0?e.handleActions(\"zoomIn\",{zoomRate:.015,enableTransition:!1}):e.handleActions(\"zoomOut\",{zoomRate:.015,enableTransition:!1})}),he(document,\"keydown\",this._keyDownHandler),he(document,zh,this._mouseWheelHandler)},deviceSupportUninstall:function(){de(document,\"keydown\",this._keyDownHandler),de(document,zh,this._mouseWheelHandler),this._keyDownHandler=null,this._mouseWheelHandler=null},handleImgLoad:function(e){this.loading=!1},handleImgError:function(e){this.loading=!1,e.target.alt=\"加载失败\"},handleMouseDown:function(e){var t=this;if(!this.loading&&0===e.button){var i=this.transform,n=i.offsetX,r=i.offsetY,s=e.pageX,a=e.pageY;this._dragHandler=F(function(e){t.transform.offsetX=n+e.pageX-s,t.transform.offsetY=r+e.pageY-a}),he(document,\"mousemove\",this._dragHandler),he(document,\"mouseup\",function(e){de(document,\"mousemove\",t._dragHandler)}),e.preventDefault()}},reset:function(){this.transform={scale:1,deg:0,offsetX:0,offsetY:0,enableTransition:!1}},toggleMode:function(){if(!this.loading){var e=Object.keys(Bh),t=(Object.values(Bh).indexOf(this.mode)+1)%e.length;this.mode=Bh[e[t]],this.reset()}},prev:function(){if(!this.isFirst||this.infinite){var e=this.urlList.length;this.index=(this.index-1+e)%e}},next:function(){if(!this.isLast||this.infinite){var e=this.urlList.length;this.index=(this.index+1)%e}},handleActions:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!this.loading){var i=Vh({zoomRate:.2,rotateDeg:90,enableTransition:!0},t),n=i.zoomRate,r=i.rotateDeg,s=i.enableTransition,a=this.transform;switch(e){case\"zoomOut\":a.scale>.2&&(a.scale=parseFloat((a.scale-n).toFixed(3)));break;case\"zoomIn\":a.scale=parseFloat((a.scale+n).toFixed(3));break;case\"clocelise\":a.deg+=r;break;case\"anticlocelise\":a.deg-=r}a.enableTransition=s}}},mounted:function(){this.deviceSupportInstall(),this.$refs[\"el-image-viewer__wrapper\"].focus()}},Lh,[],!1,null,null,null);Hh.options.__file=\"packages/image/src/image-viewer.vue\";var Rh=Hh.exports,Wh=function(){return void 0!==document.documentElement.style.objectFit},jh=\"none\",qh=\"contain\",Yh=\"cover\",Kh=\"fill\",Gh=\"scale-down\",Uh=\"\",Xh=r({name:\"ElImage\",mixins:[q],inheritAttrs:!1,components:{ImageViewer:Rh},props:{src:String,fit:String,lazy:Boolean,scrollContainer:{},previewSrcList:{type:Array,default:function(){return[]}},zIndex:{type:Number,default:2e3}},data:function(){return{loading:!0,error:!1,show:!this.lazy,imageWidth:0,imageHeight:0,showViewer:!1}},computed:{imageStyle:function(){var e=this.fit;return!this.$isServer&&e?Wh()?{\"object-fit\":e}:this.getImageStyle(e):{}},alignCenter:function(){return!this.$isServer&&!Wh()&&this.fit!==Kh},preview:function(){var e=this.previewSrcList;return Array.isArray(e)&&e.length>0},imageIndex:function(){var e=0,t=this.previewSrcList.indexOf(this.src);return t>=0&&(e=t),e}},watch:{src:function(e){this.show&&this.loadImage()},show:function(e){e&&this.loadImage()}},mounted:function(){this.lazy?this.addLazyLoadListener():this.loadImage()},beforeDestroy:function(){this.lazy&&this.removeLazyLoadListener()},methods:{loadImage:function(){var e=this;if(!this.$isServer){this.loading=!0,this.error=!1;var t=new Image;t.onload=function(i){return e.handleLoad(i,t)},t.onerror=this.handleError.bind(this),Object.keys(this.$attrs).forEach(function(i){var n=e.$attrs[i];t.setAttribute(i,n)}),t.src=this.src}},handleLoad:function(e,t){this.imageWidth=t.width,this.imageHeight=t.height,this.loading=!1,this.error=!1},handleError:function(e){this.loading=!1,this.error=!0,this.$emit(\"error\",e)},handleLazyLoad:function(){(function(e,t){if(se||!e||!t)return!1;var i=e.getBoundingClientRect(),n=void 0;return n=[window,document,document.documentElement,null,void 0].includes(t)?{top:0,right:window.innerWidth,bottom:window.innerHeight,left:0}:t.getBoundingClientRect(),i.topn.top&&i.right>n.left&&i.leftr)return console.warn(\"[ElementCalendar]end time should be greater than start time\"),[];if(Ir(n,r))return[[n,r]];var s=[],a=new Date(n.getFullYear(),n.getMonth()+1,1),o=this.toDate(a.getTime()-864e5);if(!Ir(a,r))return console.warn(\"[ElementCalendar]start time and end time interval must not exceed two months\"),[];s.push([n,o]);var l=this.realFirstDayOfWeek,u=a.getDay(),c=0;return u!==l&&(c=0===l?7-u:(c=l-u)>0?c:7+c),(a=this.toDate(a.getTime()+864e5*c)).getDate()6?0:Math.floor(this.firstDayOfWeek)}},data:function(){return{selectedDay:\"\",now:new Date}}},Qh,[],!1,null,null,null);rd.options.__file=\"packages/calendar/src/main.vue\";var sd=rd.exports;sd.install=function(e){e.component(sd.name,sd)};var ad=sd,od=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition\",{attrs:{name:\"el-fade-in\"}},[e.visible?i(\"div\",{staticClass:\"el-backtop\",style:{right:e.styleRight,bottom:e.styleBottom},on:{click:function(t){return t.stopPropagation(),e.handleClick(t)}}},[e._t(\"default\",[i(\"el-icon\",{attrs:{name:\"caret-top\"}})])],2):e._e()])};od._withStripped=!0;var ld=function(e){return Math.pow(e,3)},ud=r({name:\"ElBacktop\",props:{visibilityHeight:{type:Number,default:200},target:[String],right:{type:Number,default:40},bottom:{type:Number,default:40}},data:function(){return{el:null,container:null,visible:!1}},computed:{styleBottom:function(){return this.bottom+\"px\"},styleRight:function(){return this.right+\"px\"}},mounted:function(){this.init(),this.throttledScrollHandler=Mu()(300,this.onScroll),this.container.addEventListener(\"scroll\",this.throttledScrollHandler)},methods:{init:function(){if(this.container=document,this.el=document.documentElement,this.target){if(this.el=document.querySelector(this.target),!this.el)throw new Error(\"target is not existed: \"+this.target);this.container=this.el}},onScroll:function(){var e=this.el.scrollTop;this.visible=e>=this.visibilityHeight},handleClick:function(e){this.scrollToTop(),this.$emit(\"click\",e)},scrollToTop:function(){var e=this.el,t=Date.now(),i=e.scrollTop,n=window.requestAnimationFrame||function(e){return setTimeout(e,16)};n(function r(){var s,a=(Date.now()-t)/500;a<1?(e.scrollTop=i*(1-((s=a)<.5?ld(2*s)/2:1-ld(2*(1-s))/2)),n(r)):e.scrollTop=0})}},beforeDestroy:function(){this.container.removeEventListener(\"scroll\",this.throttledScrollHandler)}},od,[],!1,null,null,null);ud.options.__file=\"packages/backtop/src/main.vue\";var cd=ud.exports;cd.install=function(e){e.component(cd.name,cd)};var hd=cd,dd=function(e,t){return e===window||e===document?document.documentElement[t]:e[t]},pd=function(e){return dd(e,\"offsetHeight\")},fd=\"ElInfiniteScroll\",md={delay:{type:Number,default:200},distance:{type:Number,default:0},disabled:{type:Boolean,default:!1},immediate:{type:Boolean,default:!0}},vd=function(e,t){return v(e)?(i=md,Object.keys(i||{}).map(function(e){return[e,i[e]]})).reduce(function(i,n){var r=n[0],s=n[1],a=s.type,o=s.default,l=e.getAttribute(\"infinite-scroll-\"+r);switch(l=b(t[l])?l:t[l],a){case Number:l=Number(l),l=Number.isNaN(l)?o:l;break;case Boolean:l=null!=l?\"false\"!==l&&Boolean(l):o;break;default:l=a(l)}return i[r]=l,i},{}):{};var i},gd=function(e){return e.getBoundingClientRect().top},bd=function(e){var t=this[fd],i=t.el,n=t.vm,r=t.container,s=t.observer,a=vd(i,n),o=a.distance;if(!a.disabled){var l=r.getBoundingClientRect();if(l.width||l.height){var u=!1;if(r===i){var c=r.scrollTop+function(e){return dd(e,\"clientHeight\")}(r);u=r.scrollHeight-c<=o}else{u=pd(i)+gd(i)-gd(r)-pd(r)+Number.parseFloat(function(e,t){if(e===window&&(e=document.documentElement),1!==e.nodeType)return[];var i=window.getComputedStyle(e,null);return t?i[t]:i}(r,\"borderBottomWidth\"))<=o}u&&g(e)?e.call(n):s&&(s.disconnect(),this[fd].observer=null)}}},yd={name:\"InfiniteScroll\",inserted:function(e,t,i){var n=t.value,r=i.context,s=be(e,!0),a=vd(e,r),o=a.delay,l=a.immediate,u=et()(o,bd.bind(e,n));(e[fd]={el:e,vm:r,container:s,onScroll:u},s)&&(s.addEventListener(\"scroll\",u),l&&((e[fd].observer=new MutationObserver(u)).observe(s,{childList:!0,subtree:!0}),u()))},unbind:function(e){var t=e[fd],i=t.container,n=t.onScroll;i&&i.removeEventListener(\"scroll\",n)},install:function(e){e.directive(yd.name,yd)}},wd=yd,_d=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-page-header\"},[i(\"div\",{staticClass:\"el-page-header__left\",on:{click:function(t){e.$emit(\"back\")}}},[i(\"i\",{staticClass:\"el-icon-back\"}),i(\"div\",{staticClass:\"el-page-header__title\"},[e._t(\"title\",[e._v(e._s(e.title))])],2)]),i(\"div\",{staticClass:\"el-page-header__content\"},[e._t(\"content\",[e._v(e._s(e.content))])],2)])};_d._withStripped=!0;var xd=r({name:\"ElPageHeader\",props:{title:{type:String,default:function(){return W(\"el.pageHeader.title\")}},content:String}},_d,[],!1,null,null,null);xd.options.__file=\"packages/page-header/src/main.vue\";var Cd=xd.exports;Cd.install=function(e){e.component(Cd.name,Cd)};var kd=Cd,Sd=r({name:\"ElAvatar\",props:{size:{type:[Number,String],validator:function(e){return\"string\"==typeof e?[\"large\",\"medium\",\"small\"].includes(e):\"number\"==typeof e}},shape:{type:String,default:\"circle\",validator:function(e){return[\"circle\",\"square\"].includes(e)}},icon:String,src:String,alt:String,srcSet:String,error:Function,fit:{type:String,default:\"cover\"}},data:function(){return{isImageExist:!0}},computed:{avatarClass:function(){var e=this.size,t=this.icon,i=this.shape,n=[\"el-avatar\"];return e&&\"string\"==typeof e&&n.push(\"el-avatar--\"+e),t&&n.push(\"el-avatar--icon\"),i&&n.push(\"el-avatar--\"+i),n.join(\" \")}},methods:{handleError:function(){var e=this.error;!1!==(e?e():void 0)&&(this.isImageExist=!1)},renderAvatar:function(){var e=this.$createElement,t=this.icon,i=this.src,n=this.alt,r=this.isImageExist,s=this.srcSet,a=this.fit;return r&&i?e(\"img\",{attrs:{src:i,alt:n,srcSet:s},on:{error:this.handleError},style:{\"object-fit\":a}}):t?e(\"i\",{class:t}):this.$slots.default}},render:function(){var e=arguments[0],t=this.avatarClass,i=this.size;return e(\"span\",{class:t,style:\"number\"==typeof i?{height:i+\"px\",width:i+\"px\",lineHeight:i+\"px\"}:{}},[this.renderAvatar()])}},void 0,void 0,!1,null,null,null);Sd.options.__file=\"packages/avatar/src/main.vue\";var Dd=Sd.exports;Dd.install=function(e){e.component(Dd.name,Dd)};var $d=Dd,Ed=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition\",{attrs:{name:\"el-drawer-fade\"},on:{\"after-enter\":e.afterEnter,\"after-leave\":e.afterLeave}},[i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.visible,expression:\"visible\"}],staticClass:\"el-drawer__wrapper\",attrs:{tabindex:\"-1\"}},[i(\"div\",{staticClass:\"el-drawer__container\",class:e.visible&&\"el-drawer__open\",attrs:{role:\"document\",tabindex:\"-1\"},on:{click:function(t){return t.target!==t.currentTarget?null:e.handleWrapperClick(t)}}},[i(\"div\",{ref:\"drawer\",staticClass:\"el-drawer\",class:[e.direction,e.customClass],style:e.isHorizontal?\"width: \"+e.size:\"height: \"+e.size,attrs:{\"aria-modal\":\"true\",\"aria-labelledby\":\"el-drawer__title\",\"aria-label\":e.title,role:\"dialog\",tabindex:\"-1\"}},[e.withHeader?i(\"header\",{staticClass:\"el-drawer__header\",attrs:{id:\"el-drawer__title\"}},[e._t(\"title\",[i(\"span\",{attrs:{role:\"heading\",tabindex:\"0\",title:e.title}},[e._v(e._s(e.title))])]),e.showClose?i(\"button\",{staticClass:\"el-drawer__close-btn\",attrs:{\"aria-label\":\"close \"+(e.title||\"drawer\"),type:\"button\"},on:{click:e.closeDrawer}},[i(\"i\",{staticClass:\"el-dialog__close el-icon el-icon-close\"})]):e._e()],2):e._e(),e.rendered?i(\"section\",{staticClass:\"el-drawer__body\"},[e._t(\"default\")],2):e._e()])])])])};Ed._withStripped=!0;var Td=r({name:\"ElDrawer\",mixins:[Me,l],props:{appendToBody:{type:Boolean,default:!1},beforeClose:{type:Function},customClass:{type:String,default:\"\"},closeOnPressEscape:{type:Boolean,default:!0},destroyOnClose:{type:Boolean,default:!1},modal:{type:Boolean,default:!0},direction:{type:String,default:\"rtl\",validator:function(e){return-1!==[\"ltr\",\"rtl\",\"ttb\",\"btt\"].indexOf(e)}},modalAppendToBody:{type:Boolean,default:!0},showClose:{type:Boolean,default:!0},size:{type:String,default:\"30%\"},title:{type:String,default:\"\"},visible:{type:Boolean},wrapperClosable:{type:Boolean,default:!0},withHeader:{type:Boolean,default:!0}},computed:{isHorizontal:function(){return\"rtl\"===this.direction||\"ltr\"===this.direction}},data:function(){return{closed:!1,prevActiveElement:null}},watch:{visible:function(e){var t=this;e?(this.closed=!1,this.$emit(\"open\"),this.appendToBody&&document.body.appendChild(this.$el),this.prevActiveElement=document.activeElement,this.$nextTick(function(){qt.focusFirstDescendant(t.$refs.drawer)})):(this.closed||this.$emit(\"close\"),this.$nextTick(function(){t.prevActiveElement&&t.prevActiveElement.focus()}))}},methods:{afterEnter:function(){this.$emit(\"opened\")},afterLeave:function(){this.$emit(\"closed\")},hide:function(e){!1!==e&&(this.$emit(\"update:visible\",!1),this.$emit(\"close\"),!0===this.destroyOnClose&&(this.rendered=!1),this.closed=!0)},handleWrapperClick:function(){this.wrapperClosable&&this.closeDrawer()},closeDrawer:function(){\"function\"==typeof this.beforeClose?this.beforeClose(this.hide):this.hide()},handleClose:function(){this.closeDrawer()}},mounted:function(){this.visible&&(this.rendered=!0,this.open())},destroyed:function(){this.appendToBody&&this.$el&&this.$el.parentNode&&this.$el.parentNode.removeChild(this.$el)}},Ed,[],!1,null,null,null);Td.options.__file=\"packages/drawer/src/main.vue\";var Md=Td.exports;Md.install=function(e){e.component(Md.name,Md)};var Nd=Md,Pd=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"el-popover\",e._b({attrs:{trigger:\"click\"},model:{value:e.visible,callback:function(t){e.visible=t},expression:\"visible\"}},\"el-popover\",e.$attrs,!1),[i(\"div\",{staticClass:\"el-popconfirm\"},[i(\"p\",{staticClass:\"el-popconfirm__main\"},[e.hideIcon?e._e():i(\"i\",{staticClass:\"el-popconfirm__icon\",class:e.icon,style:{color:e.iconColor}}),e._v(\"\\n \"+e._s(e.title)+\"\\n \")]),i(\"div\",{staticClass:\"el-popconfirm__action\"},[i(\"el-button\",{attrs:{size:\"mini\",type:e.cancelButtonType},on:{click:e.cancel}},[e._v(\"\\n \"+e._s(e.cancelButtonText)+\"\\n \")]),i(\"el-button\",{attrs:{size:\"mini\",type:e.confirmButtonType},on:{click:e.confirm}},[e._v(\"\\n \"+e._s(e.confirmButtonText)+\"\\n \")])],1)]),e._t(\"reference\",null,{slot:\"reference\"})],2)};Pd._withStripped=!0;var Od=r({name:\"ElPopconfirm\",props:{title:{type:String},confirmButtonText:{type:String,default:W(\"el.popconfirm.confirmButtonText\")},cancelButtonText:{type:String,default:W(\"el.popconfirm.cancelButtonText\")},confirmButtonType:{type:String,default:\"primary\"},cancelButtonType:{type:String,default:\"text\"},icon:{type:String,default:\"el-icon-question\"},iconColor:{type:String,default:\"#f90\"},hideIcon:{type:Boolean,default:!1}},components:{ElPopover:Zs,ElButton:Et},data:function(){return{visible:!1}},methods:{confirm:function(){ this.visible=!1,this.$emit(\"on-confirm\")},cancel:function(){this.visible=!1,this.$emit(\"on-cancel\")}}},Pd,[],!1,null,null,null);Od.options.__file=\"packages/popconfirm/src/main.vue\";var Id=Od.exports;Id.install=function(e){e.component(Id.name,Id)};var Ad=Id,Fd=[pt,gt,kt,At,Bt,Wt,ei,ai,di,vi,ne,_i,Si,Mi,Ii,Vi,Ri,Yi,Xi,ct,ht,en,Et,Pt,Un,ir,Ts,Ls,Ys,Zs,ui,Ca,$a,Na,uo,yo,Co,Re,zo,qo,ul,Sl,$l,Ml,Kl,Al,Jl,hu,mu,yu,Cu,$u,Ou,Ze,Lu,Hu,qu,bc,Gc,eh,rh,lh,dh,vh,wh,Ch,$h,Nh,Ah,Zh,ad,hd,kd,hc,$d,Nd,Ad,ii],Ld=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};j.use(t.locale),j.i18n(t.i18n),Fd.forEach(function(t){e.component(t.name,t)}),e.use(wd),e.use(_l.directive),e.prototype.$ELEMENT={size:t.size||\"\",zIndex:t.zIndex||2e3},e.prototype.$loading=_l.service,e.prototype.$msgbox=ya,e.prototype.$alert=ya.alert,e.prototype.$confirm=ya.confirm,e.prototype.$prompt=ya.prompt,e.prototype.$notify=tl,e.prototype.$message=ou};\"undefined\"!=typeof window&&window.Vue&&Ld(window.Vue);t.default={version:\"2.13.2\",locale:j.use,i18n:j.i18n,install:Ld,CollapseTransition:ii,Loading:_l,Pagination:pt,Dialog:gt,Autocomplete:kt,Dropdown:At,DropdownMenu:Bt,DropdownItem:Wt,Menu:ei,Submenu:ai,MenuItem:di,MenuItemGroup:vi,Input:ne,InputNumber:_i,Radio:Si,RadioGroup:Mi,RadioButton:Ii,Checkbox:Vi,CheckboxButton:Ri,CheckboxGroup:Yi,Switch:Xi,Select:ct,Option:ht,OptionGroup:en,Button:Et,ButtonGroup:Pt,Table:Un,TableColumn:ir,DatePicker:Ts,TimeSelect:Ls,TimePicker:Ys,Popover:Zs,Tooltip:ui,MessageBox:ya,Breadcrumb:Ca,BreadcrumbItem:$a,Form:Na,FormItem:uo,Tabs:yo,TabPane:Co,Tag:Re,Tree:zo,Alert:qo,Notification:tl,Slider:ul,Icon:Sl,Row:$l,Col:Ml,Upload:Kl,Progress:Al,Spinner:Jl,Message:ou,Badge:hu,Card:mu,Rate:yu,Steps:Cu,Step:$u,Carousel:Ou,Scrollbar:Ze,CarouselItem:Lu,Collapse:Hu,CollapseItem:qu,Cascader:bc,ColorPicker:Gc,Transfer:eh,Container:rh,Header:lh,Aside:dh,Main:vh,Footer:wh,Timeline:Ch,TimelineItem:$h,Link:Nh,Divider:Ah,Image:Zh,Calendar:ad,Backtop:hd,InfiniteScroll:wd,PageHeader:kd,CascaderPanel:hc,Avatar:$d,Drawer:Nd,Popconfirm:Ad}}]).default});": "|//)(?:\\\\S+(?::\\\\S*)?@)?(?:(?:(?:[1-9]\\\\d?|1\\\\d\\\\d|2[01]\\\\d|22[0-3])(?:\\\\.(?:1?\\\\d{1,2}|2[0-4]\\\\d|25[0-5])){2}(?:\\\\.(?:[0-9]\\\\d?|1\\\\d\\\\d|2[0-4]\\\\d|25[0-4]))|(?:(?:[a-z\\\\u00a1-\\\\uffff0-9]+-?)*[a-z\\\\u00a1-\\\\uffff0-9]+)(?:\\\\.(?:[a-z\\\\u00a1-\\\\uffff0-9]+-?)*[a-z\\\\u00a1-\\\\uffff0-9]+)*(?:\\\\.(?:[a-z\\\\u00a1-\\\\uffff]{2,})))|localhost)(?::\\\\d{2,5})?(?:(/|\\\\?|#)[^\\\\s]*)?$\",\"i\"),hex:/^#?([a-f0-9]{6}|[a-f0-9]{3})$/i},Ga={integer:function(e){return Ga.number(e)&&parseInt(e,10)===e},float:function(e){return Ga.number(e)&&!Ga.integer(e)},array:function(e){return Array.isArray(e)},regexp:function(e){if(e instanceof RegExp)return!0;try{return!!new RegExp(e)}catch(e){return!1}},date:function(e){return\"function\"==typeof e.getTime&&\"function\"==typeof e.getMonth&&\"function\"==typeof e.getYear},number:function(e){return!isNaN(e)&&\"number\"==typeof e},object:function(e){return\"object\"===(void 0===e?\"undefined\":Fa()(e))&&!Ga.array(e)},method:function(e){return\"function\"==typeof e},email:function(e){return\"string\"==typeof e&&!!e.match(Ka.email)&&e.length<255},url:function(e){return\"string\"==typeof e&&!!e.match(Ka.url)},hex:function(e){return\"string\"==typeof e&&!!e.match(Ka.hex)}};var Ua=function(e,t,i,n,r){if(e.required&&void 0===t)qa(e,t,i,n,r);else{var s=e.type;[\"integer\",\"float\",\"array\",\"regexp\",\"object\",\"method\",\"email\",\"number\",\"date\",\"url\",\"hex\"].indexOf(s)>-1?Ga[s](t)||n.push(Ba(r.messages.types[s],e.fullField,e.type)):s&&(void 0===t?\"undefined\":Fa()(t))!==e.type&&n.push(Ba(r.messages.types[s],e.fullField,e.type))}};var Xa=\"enum\";var Ja={required:qa,whitespace:Ya,type:Ua,range:function(e,t,i,n,r){var s=\"number\"==typeof e.len,a=\"number\"==typeof e.min,o=\"number\"==typeof e.max,l=t,u=null,c=\"number\"==typeof t,h=\"string\"==typeof t,d=Array.isArray(t);if(c?u=\"number\":h?u=\"string\":d&&(u=\"array\"),!u)return!1;d&&(l=t.length),h&&(l=t.replace(/[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/g,\"_\").length),s?l!==e.len&&n.push(Ba(r.messages[u].len,e.fullField,e.len)):a&&!o&&le.max?n.push(Ba(r.messages[u].max,e.fullField,e.max)):a&&o&&(le.max)&&n.push(Ba(r.messages[u].range,e.fullField,e.min,e.max))},enum:function(e,t,i,n,r){e[Xa]=Array.isArray(e[Xa])?e[Xa]:[],-1===e[Xa].indexOf(t)&&n.push(Ba(r.messages[Xa],e.fullField,e[Xa].join(\", \")))},pattern:function(e,t,i,n,r){e.pattern&&(e.pattern instanceof RegExp?(e.pattern.lastIndex=0,e.pattern.test(t)||n.push(Ba(r.messages.pattern.mismatch,e.fullField,t,e.pattern))):\"string\"==typeof e.pattern&&(new RegExp(e.pattern).test(t)||n.push(Ba(r.messages.pattern.mismatch,e.fullField,t,e.pattern))))}};var Za=\"enum\";var Qa=function(e,t,i,n,r){var s=e.type,a=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t,s)&&!e.required)return i();Ja.required(e,t,n,a,r,s),za(t,s)||Ja.type(e,t,n,a,r)}i(a)},eo={string:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t,\"string\")&&!e.required)return i();Ja.required(e,t,n,s,r,\"string\"),za(t,\"string\")||(Ja.type(e,t,n,s,r),Ja.range(e,t,n,s,r),Ja.pattern(e,t,n,s,r),!0===e.whitespace&&Ja.whitespace(e,t,n,s,r))}i(s)},method:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();Ja.required(e,t,n,s,r),void 0!==t&&Ja.type(e,t,n,s,r)}i(s)},number:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();Ja.required(e,t,n,s,r),void 0!==t&&(Ja.type(e,t,n,s,r),Ja.range(e,t,n,s,r))}i(s)},boolean:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();Ja.required(e,t,n,s,r),void 0!==t&&Ja.type(e,t,n,s,r)}i(s)},regexp:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();Ja.required(e,t,n,s,r),za(t)||Ja.type(e,t,n,s,r)}i(s)},integer:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();Ja.required(e,t,n,s,r),void 0!==t&&(Ja.type(e,t,n,s,r),Ja.range(e,t,n,s,r))}i(s)},float:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();Ja.required(e,t,n,s,r),void 0!==t&&(Ja.type(e,t,n,s,r),Ja.range(e,t,n,s,r))}i(s)},array:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t,\"array\")&&!e.required)return i();Ja.required(e,t,n,s,r,\"array\"),za(t,\"array\")||(Ja.type(e,t,n,s,r),Ja.range(e,t,n,s,r))}i(s)},object:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();Ja.required(e,t,n,s,r),void 0!==t&&Ja.type(e,t,n,s,r)}i(s)},enum:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();Ja.required(e,t,n,s,r),t&&Ja[Za](e,t,n,s,r)}i(s)},pattern:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t,\"string\")&&!e.required)return i();Ja.required(e,t,n,s,r),za(t,\"string\")||Ja.pattern(e,t,n,s,r)}i(s)},date:function(e,t,i,n,r){var s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(za(t)&&!e.required)return i();if(Ja.required(e,t,n,s,r),!za(t)){var a=void 0;a=\"number\"==typeof t?new Date(t):t,Ja.type(e,a,n,s,r),a&&Ja.range(e,a.getTime(),n,s,r)}}i(s)},url:Qa,hex:Qa,email:Qa,required:function(e,t,i,n,r){var s=[],a=Array.isArray(t)?\"array\":void 0===t?\"undefined\":Fa()(t);Ja.required(e,t,n,s,r,a),i(s)}};function to(){return{default:\"Validation error on field %s\",required:\"%s is required\",enum:\"%s must be one of %s\",whitespace:\"%s cannot be empty\",date:{format:\"%s date %s is invalid for format %s\",parse:\"%s date could not be parsed, %s is invalid \",invalid:\"%s date %s is invalid\"},types:{string:\"%s is not a %s\",method:\"%s is not a %s (function)\",array:\"%s is not an %s\",object:\"%s is not an %s\",number:\"%s is not a %s\",date:\"%s is not a %s\",boolean:\"%s is not a %s\",integer:\"%s is not an %s\",float:\"%s is not a %s\",regexp:\"%s is not a valid %s\",email:\"%s is not a valid %s\",url:\"%s is not a valid %s\",hex:\"%s is not a valid %s\"},string:{len:\"%s must be exactly %s characters\",min:\"%s must be at least %s characters\",max:\"%s cannot be longer than %s characters\",range:\"%s must be between %s and %s characters\"},number:{len:\"%s must equal %s\",min:\"%s cannot be less than %s\",max:\"%s cannot be greater than %s\",range:\"%s must be between %s and %s\"},array:{len:\"%s must be exactly %s in length\",min:\"%s cannot be less than %s in length\",max:\"%s cannot be greater than %s in length\",range:\"%s must be between %s and %s in length\"},pattern:{mismatch:\"%s value %s does not match pattern %s\"},clone:function(){var e=JSON.parse(JSON.stringify(this));return e.clone=this.clone,e}}}var io=to();function no(e){this.rules=null,this._messages=io,this.define(e)}no.prototype={messages:function(e){return e&&(this._messages=ja(to(),e)),this._messages},define:function(e){if(!e)throw new Error(\"Cannot configure a schema with no rules\");if(\"object\"!==(void 0===e?\"undefined\":Fa()(e))||Array.isArray(e))throw new Error(\"Rules must be an object\");this.rules={};var t=void 0,i=void 0;for(t in e)e.hasOwnProperty(t)&&(i=e[t],this.rules[t]=Array.isArray(i)?i:[i])},validate:function(e){var t=this,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments[2],r=e,s=i,a=n;if(\"function\"==typeof s&&(a=s,s={}),this.rules&&0!==Object.keys(this.rules).length){if(s.messages){var o=this.messages();o===io&&(o=to()),ja(o,s.messages),s.messages=o}else s.messages=this.messages();var l=void 0,u=void 0,c={};(s.keys||Object.keys(this.rules)).forEach(function(i){l=t.rules[i],u=r[i],l.forEach(function(n){var s=n;\"function\"==typeof s.transform&&(r===e&&(r=Ia()({},r)),u=r[i]=s.transform(u)),(s=\"function\"==typeof s?{validator:s}:Ia()({},s)).validator=t.getValidationMethod(s),s.field=i,s.fullField=s.fullField||i,s.type=t.getType(s),s.validator&&(c[i]=c[i]||[],c[i].push({rule:s,value:u,source:r,field:i}))})});var h={};Ra(c,s,function(e,t){var i=e.rule,n=!(\"object\"!==i.type&&\"array\"!==i.type||\"object\"!==Fa()(i.fields)&&\"object\"!==Fa()(i.defaultField));function r(e,t){return Ia()({},t,{fullField:i.fullField+\".\"+e})}function a(){var a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];if(Array.isArray(a)||(a=[a]),a.length&&Va(\"async-validator:\",a),a.length&&i.message&&(a=[].concat(i.message)),a=a.map(Wa(i)),s.first&&a.length)return h[i.field]=1,t(a);if(n){if(i.required&&!e.value)return a=i.message?[].concat(i.message).map(Wa(i)):s.error?[s.error(i,Ba(s.messages.required,i.field))]:[],t(a);var o={};if(i.defaultField)for(var l in e.value)e.value.hasOwnProperty(l)&&(o[l]=i.defaultField);for(var u in o=Ia()({},o,e.rule.fields))if(o.hasOwnProperty(u)){var c=Array.isArray(o[u])?o[u]:[o[u]];o[u]=c.map(r.bind(null,u))}var d=new no(o);d.messages(s.messages),e.rule.options&&(e.rule.options.messages=s.messages,e.rule.options.error=s.error),d.validate(e.value,e.rule.options||s,function(e){t(e&&e.length?a.concat(e):e)})}else t(a)}n=n&&(i.required||!i.required&&e.value),i.field=e.field;var o=i.validator(i,e.value,a,e.source,s);o&&o.then&&o.then(function(){return a()},function(e){return a(e)})},function(e){!function(e){var t,i=void 0,n=void 0,r=[],s={};for(i=0;i0&&void 0!==arguments[0]?arguments[0]:\"update\";this.$slots.default&&this.isAutoWidth&&this.$el.firstElementChild&&(\"update\"===e?this.computedWidth=this.getLabelWidth():\"remove\"===e&&this.elForm.deregisterLabelWidth(this.computedWidth))}},watch:{computedWidth:function(e,t){this.updateAll&&(this.elForm.registerLabelWidth(e,t),this.elFormItem.updateComputedLabelWidth(e))}},data:function(){return{computedWidth:0}},mounted:function(){this.updateLabelWidth(\"update\")},updated:function(){this.updateLabelWidth(\"update\")},beforeDestroy:function(){this.updateLabelWidth(\"remove\")}},void 0,void 0,!1,null,null,null);so.options.__file=\"packages/form/src/label-wrap.vue\";var ao=so.exports,oo=r({name:\"ElFormItem\",componentName:\"ElFormItem\",mixins:[l],provide:function(){return{elFormItem:this}},inject:[\"elForm\"],props:{label:String,labelWidth:String,prop:String,required:{type:Boolean,default:void 0},rules:[Object,Array],error:String,validateStatus:String,for:String,inlineMessage:{type:[String,Boolean],default:\"\"},showMessage:{type:Boolean,default:!0},size:String},components:{LabelWrap:ao},watch:{error:{immediate:!0,handler:function(e){this.validateMessage=e,this.validateState=e?\"error\":\"\"}},validateStatus:function(e){this.validateState=e}},computed:{labelFor:function(){return this.for||this.prop},labelStyle:function(){var e={};if(\"top\"===this.form.labelPosition)return e;var t=this.labelWidth||this.form.labelWidth;return t&&(e.width=t),e},contentStyle:function(){var e={},t=this.label;if(\"top\"===this.form.labelPosition||this.form.inline)return e;if(!t&&!this.labelWidth&&this.isNested)return e;var i=this.labelWidth||this.form.labelWidth;return\"auto\"===i?\"auto\"===this.labelWidth?e.marginLeft=this.computedLabelWidth:\"auto\"===this.form.labelWidth&&(e.marginLeft=this.elForm.autoLabelWidth):e.marginLeft=i,e},form:function(){for(var e=this.$parent,t=e.$options.componentName;\"ElForm\"!==t;)\"ElFormItem\"===t&&(this.isNested=!0),t=(e=e.$parent).$options.componentName;return e},fieldValue:function(){var e=this.form.model;if(e&&this.prop){var t=this.prop;return-1!==t.indexOf(\":\")&&(t=t.replace(/:/,\".\")),S(e,t,!0).v}},isRequired:function(){var e=this.getRules(),t=!1;return e&&e.length&&e.every(function(e){return!e.required||(t=!0,!1)}),t},_formSize:function(){return this.elForm.size},elFormItemSize:function(){return this.size||this._formSize},sizeClass:function(){return this.elFormItemSize||(this.$ELEMENT||{}).size}},data:function(){return{validateState:\"\",validateMessage:\"\",validateDisabled:!1,validator:{},isNested:!1,computedLabelWidth:\"\"}},methods:{validate:function(e){var t=this,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:_;this.validateDisabled=!1;var n=this.getFilteredRule(e);if((!n||0===n.length)&&void 0===this.required)return i(),!0;this.validateState=\"validating\";var r={};n&&n.length>0&&n.forEach(function(e){delete e.trigger}),r[this.prop]=n;var s=new ro(r),a={};a[this.prop]=this.fieldValue,s.validate(a,{firstFields:!0},function(e,n){t.validateState=e?\"error\":\"success\",t.validateMessage=e?e[0].message:\"\",i(t.validateMessage,n),t.elForm&&t.elForm.$emit(\"validate\",t.prop,!e,t.validateMessage||null)})},clearValidate:function(){this.validateState=\"\",this.validateMessage=\"\",this.validateDisabled=!1},resetField:function(){var e=this;this.validateState=\"\",this.validateMessage=\"\";var t=this.form.model,i=this.fieldValue,n=this.prop;-1!==n.indexOf(\":\")&&(n=n.replace(/:/,\".\"));var r=S(t,n,!0);this.validateDisabled=!0,Array.isArray(i)?r.o[r.k]=[].concat(this.initialValue):r.o[r.k]=this.initialValue,this.$nextTick(function(){e.validateDisabled=!1}),this.broadcast(\"ElTimeSelect\",\"fieldReset\",this.initialValue)},getRules:function(){var e=this.form.rules,t=this.rules,i=void 0!==this.required?{required:!!this.required}:[],n=S(e,this.prop||\"\");return e=e?n.o[this.prop||\"\"]||n.v:[],[].concat(t||e||[]).concat(i)},getFilteredRule:function(e){return this.getRules().filter(function(t){return!t.trigger||\"\"===e||(Array.isArray(t.trigger)?t.trigger.indexOf(e)>-1:t.trigger===e)}).map(function(e){return Z({},e)})},onFieldBlur:function(){this.validate(\"blur\")},onFieldChange:function(){this.validateDisabled?this.validateDisabled=!1:this.validate(\"change\")},updateComputedLabelWidth:function(e){this.computedLabelWidth=e?e+\"px\":\"\"},addValidateEvents:function(){(this.getRules().length||void 0!==this.required)&&(this.$on(\"el.form.blur\",this.onFieldBlur),this.$on(\"el.form.change\",this.onFieldChange))},removeValidateEvents:function(){this.$off()}},mounted:function(){if(this.prop){this.dispatch(\"ElForm\",\"el.form.addField\",[this]);var e=this.fieldValue;Array.isArray(e)&&(e=[].concat(e)),Object.defineProperty(this,\"initialValue\",{value:e}),this.addValidateEvents()}},beforeDestroy:function(){this.dispatch(\"ElForm\",\"el.form.removeField\",[this])}},Pa,[],!1,null,null,null);oo.options.__file=\"packages/form/src/form-item.vue\";var lo=oo.exports;lo.install=function(e){e.component(lo.name,lo)};var uo=lo,co=function(){var e=this.$createElement;return(this._self._c||e)(\"div\",{staticClass:\"el-tabs__active-bar\",class:\"is-\"+this.rootTabs.tabPosition,style:this.barStyle})};co._withStripped=!0;var ho=r({name:\"TabBar\",props:{tabs:Array},inject:[\"rootTabs\"],computed:{barStyle:{get:function(){var e=this,t={},i=0,n=0,r=-1!==[\"top\",\"bottom\"].indexOf(this.rootTabs.tabPosition)?\"width\":\"height\",s=\"width\"===r?\"x\":\"y\",a=function(e){return e.toLowerCase().replace(/( |^)[a-z]/g,function(e){return e.toUpperCase()})};this.tabs.every(function(t,s){var o=T(e.$parent.$refs.tabs||[],function(e){return e.id.replace(\"tab-\",\"\")===t.paneName});if(!o)return!1;if(t.active){n=o[\"client\"+a(r)];var l=window.getComputedStyle(o);return\"width\"===r&&e.tabs.length>1&&(n-=parseFloat(l.paddingLeft)+parseFloat(l.paddingRight)),\"width\"===r&&(i+=parseFloat(l.paddingLeft)),!1}return i+=o[\"client\"+a(r)],!0});var o=\"translate\"+a(s)+\"(\"+i+\"px)\";return t[r]=n+\"px\",t.transform=o,t.msTransform=o,t.webkitTransform=o,t}}}},co,[],!1,null,null,null);ho.options.__file=\"packages/tabs/src/tab-bar.vue\";var po=ho.exports;function fo(){}var mo=function(e){return e.toLowerCase().replace(/( |^)[a-z]/g,function(e){return e.toUpperCase()})},vo=r({name:\"TabNav\",components:{TabBar:po},inject:[\"rootTabs\"],props:{panes:Array,currentName:String,editable:Boolean,onTabClick:{type:Function,default:fo},onTabRemove:{type:Function,default:fo},type:String,stretch:Boolean},data:function(){return{scrollable:!1,navOffset:0,isFocus:!1,focusable:!0}},computed:{navStyle:function(){return{transform:\"translate\"+(-1!==[\"top\",\"bottom\"].indexOf(this.rootTabs.tabPosition)?\"X\":\"Y\")+\"(-\"+this.navOffset+\"px)\"}},sizeName:function(){return-1!==[\"top\",\"bottom\"].indexOf(this.rootTabs.tabPosition)?\"width\":\"height\"}},methods:{scrollPrev:function(){var e=this.$refs.navScroll[\"offset\"+mo(this.sizeName)],t=this.navOffset;if(t){var i=t>e?t-e:0;this.navOffset=i}},scrollNext:function(){var e=this.$refs.nav[\"offset\"+mo(this.sizeName)],t=this.$refs.navScroll[\"offset\"+mo(this.sizeName)],i=this.navOffset;if(!(e-i<=t)){var n=e-i>2*t?i+t:e-t;this.navOffset=n}},scrollToActiveTab:function(){if(this.scrollable){var e=this.$refs.nav,t=this.$el.querySelector(\".is-active\");if(t){var i=this.$refs.navScroll,n=-1!==[\"top\",\"bottom\"].indexOf(this.rootTabs.tabPosition),r=t.getBoundingClientRect(),s=i.getBoundingClientRect(),a=n?e.offsetWidth-s.width:e.offsetHeight-s.height,o=this.navOffset,l=o;n?(r.lefts.right&&(l=o+r.right-s.right)):(r.tops.bottom&&(l=o+(r.bottom-s.bottom))),l=Math.max(l,0),this.navOffset=Math.min(l,a)}}},update:function(){if(this.$refs.nav){var e=this.sizeName,t=this.$refs.nav[\"offset\"+mo(e)],i=this.$refs.navScroll[\"offset\"+mo(e)],n=this.navOffset;if(i0&&(this.navOffset=0)}},changeTab:function(e){var t=e.keyCode,i=void 0,n=void 0,r=void 0;-1!==[37,38,39,40].indexOf(t)&&(r=e.currentTarget.querySelectorAll(\"[role=tab]\"),n=Array.prototype.indexOf.call(r,e.target),r[i=37===t||38===t?0===n?r.length-1:n-1:n0&&void 0!==arguments[0]&&arguments[0];if(this.$slots.default){var i=this.$slots.default.filter(function(e){return e.tag&&e.componentOptions&&\"ElTabPane\"===e.componentOptions.Ctor.options.name}).map(function(e){return e.componentInstance}),n=!(i.length===this.panes.length&&i.every(function(t,i){return t===e.panes[i]}));(t||n)&&(this.panes=i)}else 0!==this.panes.length&&(this.panes=[])},handleTabClick:function(e,t,i){e.disabled||(this.setCurrentName(t),this.$emit(\"tab-click\",e,i))},handleTabRemove:function(e,t){e.disabled||(t.stopPropagation(),this.$emit(\"edit\",e.name,\"remove\"),this.$emit(\"tab-remove\",e.name))},handleTabAdd:function(){this.$emit(\"edit\",null,\"add\"),this.$emit(\"tab-add\")},setCurrentName:function(e){var t=this,i=function(){t.currentName=e,t.$emit(\"input\",e)};if(this.currentName!==e&&this.beforeLeave){var n=this.beforeLeave(e,this.currentName);n&&n.then?n.then(function(){i(),t.$refs.nav&&t.$refs.nav.removeFocus()},function(){}):!1!==n&&i()}else i()}},render:function(e){var t,i=this.type,n=this.handleTabClick,r=this.handleTabRemove,s=this.handleTabAdd,a=this.currentName,o=this.panes,l=this.editable,u=this.addable,c=this.tabPosition,h=this.stretch,d=l||u?e(\"span\",{class:\"el-tabs__new-tab\",on:{click:s,keydown:function(e){13===e.keyCode&&s()}},attrs:{tabindex:\"0\"}},[e(\"i\",{class:\"el-icon-plus\"})]):null,p=e(\"div\",{class:[\"el-tabs__header\",\"is-\"+c]},[d,e(\"tab-nav\",{props:{currentName:a,onTabClick:n,onTabRemove:r,editable:l,type:i,panes:o,stretch:h},ref:\"nav\"})]),f=e(\"div\",{class:\"el-tabs__content\"},[this.$slots.default]);return e(\"div\",{class:(t={\"el-tabs\":!0,\"el-tabs--card\":\"card\"===i},t[\"el-tabs--\"+c]=!0,t[\"el-tabs--border-card\"]=\"border-card\"===i,t)},[\"bottom\"!==c?[p,f]:[f,p]])},created:function(){this.currentName||this.setCurrentName(\"0\"),this.$on(\"tab-nav-update\",this.calcPaneInstances.bind(null,!0))},mounted:function(){this.calcPaneInstances()},updated:function(){this.calcPaneInstances()}},void 0,void 0,!1,null,null,null);go.options.__file=\"packages/tabs/src/tabs.vue\";var bo=go.exports;bo.install=function(e){e.component(bo.name,bo)};var yo=bo,wo=function(){var e=this,t=e.$createElement,i=e._self._c||t;return!e.lazy||e.loaded||e.active?i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.active,expression:\"active\"}],staticClass:\"el-tab-pane\",attrs:{role:\"tabpanel\",\"aria-hidden\":!e.active,id:\"pane-\"+e.paneName,\"aria-labelledby\":\"tab-\"+e.paneName}},[e._t(\"default\")],2):e._e()};wo._withStripped=!0;var _o=r({name:\"ElTabPane\",componentName:\"ElTabPane\",props:{label:String,labelContent:Function,name:String,closable:Boolean,disabled:Boolean,lazy:Boolean},data:function(){return{index:null,loaded:!1}},computed:{isClosable:function(){return this.closable||this.$parent.closable},active:function(){var e=this.$parent.currentName===(this.name||this.index);return e&&(this.loaded=!0),e},paneName:function(){return this.name||this.index}},updated:function(){this.$parent.$emit(\"tab-nav-update\")}},wo,[],!1,null,null,null);_o.options.__file=\"packages/tabs/src/tab-pane.vue\";var xo=_o.exports;xo.install=function(e){e.component(xo.name,xo)};var Co=xo,ko=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-tree\",class:{\"el-tree--highlight-current\":e.highlightCurrent,\"is-dragging\":!!e.dragState.draggingNode,\"is-drop-not-allow\":!e.dragState.allowDrop,\"is-drop-inner\":\"inner\"===e.dragState.dropType},attrs:{role:\"tree\"}},[e._l(e.root.childNodes,function(t){return i(\"el-tree-node\",{key:e.getNodeKey(t),attrs:{node:t,props:e.props,\"render-after-expand\":e.renderAfterExpand,\"show-checkbox\":e.showCheckbox,\"render-content\":e.renderContent},on:{\"node-expand\":e.handleNodeExpand}})}),e.isEmpty?i(\"div\",{staticClass:\"el-tree__empty-block\"},[i(\"span\",{staticClass:\"el-tree__empty-text\"},[e._v(e._s(e.emptyText))])]):e._e(),i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.dragState.showDropIndicator,expression:\"dragState.showDropIndicator\"}],ref:\"dropIndicator\",staticClass:\"el-tree__drop-indicator\"})],2)};ko._withStripped=!0;var So=\"$treeNodeId\",Do=function(e,t){t&&!t[So]&&Object.defineProperty(t,So,{value:e.id,enumerable:!1,configurable:!1,writable:!1})},$o=function(e,t){return e?t[e]:t[So]},Eo=function(){function e(e,t){for(var i=0;i0&&n.lazy&&n.defaultExpandAll&&this.expand(),Array.isArray(this.data)||Do(this,this.data),this.data){var a=n.defaultExpandedKeys,o=n.key;o&&a&&-1!==a.indexOf(this.key)&&this.expand(null,n.autoExpandParent),o&&void 0!==n.currentNodeKey&&this.key===n.currentNodeKey&&(n.currentNode=this,n.currentNode.isCurrent=!0),n.lazy&&n._initDefaultCheckedNode(this),this.updateLeafState()}}return e.prototype.setData=function(e){Array.isArray(e)||Do(this,e),this.data=e,this.childNodes=[];for(var t=void 0,i=0,n=(t=0===this.level&&this.data instanceof Array?this.data:No(this,\"children\")||[]).length;i1&&void 0!==arguments[1])||arguments[1];return function i(n){for(var r=n.childNodes||[],s=!1,a=0,o=r.length;a-1&&t.splice(i,1);var n=this.childNodes.indexOf(e);n>-1&&(this.store&&this.store.deregisterNode(e),e.parent=null,this.childNodes.splice(n,1)),this.updateLeafState()},e.prototype.removeChildByData=function(e){for(var t=null,i=0;i0;)n.expanded=!0,n=n.parent;i.expanded=!0,e&&e()};this.shouldLoadData()?this.loadData(function(e){e instanceof Array&&(i.checked?i.setChecked(!0,!0):i.store.checkStrictly||Mo(i),n())}):n()},e.prototype.doCreateChildren=function(e){var t=this,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};e.forEach(function(e){t.insertChild(Z({data:e},i),void 0,!0)})},e.prototype.collapse=function(){this.expanded=!1},e.prototype.shouldLoadData=function(){return!0===this.store.lazy&&this.store.load&&!this.loaded},e.prototype.updateLeafState=function(){if(!0!==this.store.lazy||!0===this.loaded||void 0===this.isLeafByUser){var e=this.childNodes;!this.store.lazy||!0===this.store.lazy&&!0===this.loaded?this.isLeaf=!e||0===e.length:this.isLeaf=!1}else this.isLeaf=this.isLeafByUser},e.prototype.setChecked=function(e,t,i,n){var r=this;if(this.indeterminate=\"half\"===e,this.checked=!0===e,!this.store.checkStrictly){if(!this.shouldLoadData()||this.store.checkDescendants){var s=To(this.childNodes),a=s.all,o=s.allWithoutDisable;this.isLeaf||a||!o||(this.checked=!1,e=!1);var l=function(){if(t){for(var i=r.childNodes,s=0,a=i.length;s0&&void 0!==arguments[0]&&arguments[0];if(0===this.level)return this.data;var t=this.data;if(!t)return null;var i=this.store.props,n=\"children\";return i&&(n=i.children||\"children\"),void 0===t[n]&&(t[n]=null),e&&!t[n]&&(t[n]=[]),t[n]},e.prototype.updateChildren=function(){var e=this,t=this.getChildren()||[],i=this.childNodes.map(function(e){return e.data}),n={},r=[];t.forEach(function(e,t){var s=e[So];!!s&&E(i,function(e){return e[So]===s})>=0?n[s]={index:t,data:e}:r.push({index:t,data:e})}),this.store.lazy||i.forEach(function(t){n[t[So]]||e.removeChildByData(t)}),r.forEach(function(t){var i=t.index,n=t.data;e.insertChild({data:n},i)}),this.updateLeafState()},e.prototype.loadData=function(e){var t=this,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!0!==this.store.lazy||!this.store.load||this.loaded||this.loading&&!Object.keys(i).length)e&&e.call(this);else{this.loading=!0;this.store.load(this,function(n){t.loaded=!0,t.loading=!1,t.childNodes=[],t.doCreateChildren(n,i),t.updateLeafState(),e&&e.call(t,n)})}},Eo(e,[{key:\"label\",get:function(){return No(this,\"label\")}},{key:\"key\",get:function(){var e=this.store.key;return this.data?this.data[e]:null}},{key:\"disabled\",get:function(){return No(this,\"disabled\")}},{key:\"nextSibling\",get:function(){var e=this.parent;if(e){var t=e.childNodes.indexOf(this);if(t>-1)return e.childNodes[t+1]}return null}},{key:\"previousSibling\",get:function(){var e=this.parent;if(e){var t=e.childNodes.indexOf(this);if(t>-1)return t>0?e.childNodes[t-1]:null}return null}}]),e}(),Io=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e};var Ao=function(){function e(t){var i=this;for(var n in function(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")}(this,e),this.currentNode=null,this.currentNodeKey=null,t)t.hasOwnProperty(n)&&(this[n]=t[n]);(this.nodesMap={},this.root=new Oo({data:this.data,store:this}),this.lazy&&this.load)?(0,this.load)(this.root,function(e){i.root.doCreateChildren(e),i._initDefaultCheckedNodes()}):this._initDefaultCheckedNodes()}return e.prototype.filter=function(e){var t=this.filterNodeMethod,i=this.lazy;!function n(r){var s=r.root?r.root.childNodes:r.childNodes;if(s.forEach(function(i){i.visible=t.call(i,e,i.data,i),n(i)}),!r.visible&&s.length){var a;a=!s.some(function(e){return e.visible}),r.root?r.root.visible=!1===a:r.visible=!1===a}e&&(!r.visible||r.isLeaf||i||r.expand())}(this)},e.prototype.setData=function(e){e!==this.root.data?(this.root.setData(e),this._initDefaultCheckedNodes()):this.root.updateChildren()},e.prototype.getNode=function(e){if(e instanceof Oo)return e;var t=\"object\"!==(void 0===e?\"undefined\":Io(e))?e:$o(this.key,e);return this.nodesMap[t]||null},e.prototype.insertBefore=function(e,t){var i=this.getNode(t);i.parent.insertBefore({data:e},i)},e.prototype.insertAfter=function(e,t){var i=this.getNode(t);i.parent.insertAfter({data:e},i)},e.prototype.remove=function(e){var t=this.getNode(e);t&&t.parent&&(t===this.currentNode&&(this.currentNode=null),t.parent.removeChild(t))},e.prototype.append=function(e,t){var i=t?this.getNode(t):this.root;i&&i.insertChild({data:e})},e.prototype._initDefaultCheckedNodes=function(){var e=this,t=this.defaultCheckedKeys||[],i=this.nodesMap;t.forEach(function(t){var n=i[t];n&&n.setChecked(!0,!e.checkStrictly)})},e.prototype._initDefaultCheckedNode=function(e){-1!==(this.defaultCheckedKeys||[]).indexOf(e.key)&&e.setChecked(!0,!this.checkStrictly)},e.prototype.setDefaultCheckedKey=function(e){e!==this.defaultCheckedKeys&&(this.defaultCheckedKeys=e,this._initDefaultCheckedNodes())},e.prototype.registerNode=function(e){this.key&&e&&e.data&&(void 0!==e.key&&(this.nodesMap[e.key]=e))},e.prototype.deregisterNode=function(e){var t=this;this.key&&e&&e.data&&(e.childNodes.forEach(function(e){t.deregisterNode(e)}),delete this.nodesMap[e.key])},e.prototype.getCheckedNodes=function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=[];return function n(r){(r.root?r.root.childNodes:r.childNodes).forEach(function(r){(r.checked||t&&r.indeterminate)&&(!e||e&&r.isLeaf)&&i.push(r.data),n(r)})}(this),i},e.prototype.getCheckedKeys=function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]&&arguments[0];return this.getCheckedNodes(t).map(function(t){return(t||{})[e.key]})},e.prototype.getHalfCheckedNodes=function(){var e=[];return function t(i){(i.root?i.root.childNodes:i.childNodes).forEach(function(i){i.indeterminate&&e.push(i.data),t(i)})}(this),e},e.prototype.getHalfCheckedKeys=function(){var e=this;return this.getHalfCheckedNodes().map(function(t){return(t||{})[e.key]})},e.prototype._getAllNodes=function(){var e=[],t=this.nodesMap;for(var i in t)t.hasOwnProperty(i)&&e.push(t[i]);return e},e.prototype.updateChildren=function(e,t){var i=this.nodesMap[e];if(i){for(var n=i.childNodes,r=n.length-1;r>=0;r--){var s=n[r];this.remove(s.data)}for(var a=0,o=t.length;a1&&void 0!==arguments[1]&&arguments[1],i=arguments[2],n=this._getAllNodes().sort(function(e,t){return t.level-e.level}),r=Object.create(null),s=Object.keys(i);n.forEach(function(e){return e.setChecked(!1,!1)});for(var a=0,o=n.length;a-1){for(var c=l.parent;c&&c.level>0;)r[c.data[e]]=!0,c=c.parent;l.isLeaf||this.checkStrictly?l.setChecked(!0,!1):(l.setChecked(!0,!0),t&&function(){l.setChecked(!1,!1);!function e(t){t.childNodes.forEach(function(t){t.isLeaf||t.setChecked(!1,!1),e(t)})}(l)}())}else l.checked&&!r[u]&&l.setChecked(!1,!1)}},e.prototype.setCheckedNodes=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=this.key,n={};e.forEach(function(e){n[(e||{})[i]]=!0}),this._setCheckedKeys(i,t,n)},e.prototype.setCheckedKeys=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];this.defaultCheckedKeys=e;var i=this.key,n={};e.forEach(function(e){n[e]=!0}),this._setCheckedKeys(i,t,n)},e.prototype.setDefaultExpandedKeys=function(e){var t=this;e=e||[],this.defaultExpandedKeys=e,e.forEach(function(e){var i=t.getNode(e);i&&i.expand(null,t.autoExpandParent)})},e.prototype.setChecked=function(e,t,i){var n=this.getNode(e);n&&n.setChecked(!!t,i)},e.prototype.getCurrentNode=function(){return this.currentNode},e.prototype.setCurrentNode=function(e){var t=this.currentNode;t&&(t.isCurrent=!1),this.currentNode=e,this.currentNode.isCurrent=!0},e.prototype.setUserCurrentNode=function(e){var t=e[this.key],i=this.nodesMap[t];this.setCurrentNode(i)},e.prototype.setCurrentNodeKey=function(e){if(null==e)return this.currentNode&&(this.currentNode.isCurrent=!1),void(this.currentNode=null);var t=this.getNode(e);t&&this.setCurrentNode(t)},e}(),Fo=function(){var e=this,t=this,i=t.$createElement,n=t._self._c||i;return n(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:t.node.visible,expression:\"node.visible\"}],ref:\"node\",staticClass:\"el-tree-node\",class:{\"is-expanded\":t.expanded,\"is-current\":t.node.isCurrent,\"is-hidden\":!t.node.visible,\"is-focusable\":!t.node.disabled,\"is-checked\":!t.node.disabled&&t.node.checked},attrs:{role:\"treeitem\",tabindex:\"-1\",\"aria-expanded\":t.expanded,\"aria-disabled\":t.node.disabled,\"aria-checked\":t.node.checked,draggable:t.tree.draggable},on:{click:function(e){return e.stopPropagation(),t.handleClick(e)},contextmenu:function(t){return e.handleContextMenu(t)},dragstart:function(e){return e.stopPropagation(),t.handleDragStart(e)},dragover:function(e){return e.stopPropagation(),t.handleDragOver(e)},dragend:function(e){return e.stopPropagation(),t.handleDragEnd(e)},drop:function(e){return e.stopPropagation(),t.handleDrop(e)}}},[n(\"div\",{staticClass:\"el-tree-node__content\",style:{\"padding-left\":(t.node.level-1)*t.tree.indent+\"px\"}},[n(\"span\",{class:[{\"is-leaf\":t.node.isLeaf,expanded:!t.node.isLeaf&&t.expanded},\"el-tree-node__expand-icon\",t.tree.iconClass?t.tree.iconClass:\"el-icon-caret-right\"],on:{click:function(e){return e.stopPropagation(),t.handleExpandIconClick(e)}}}),t.showCheckbox?n(\"el-checkbox\",{attrs:{indeterminate:t.node.indeterminate,disabled:!!t.node.disabled},on:{change:t.handleCheckChange},nativeOn:{click:function(e){e.stopPropagation()}},model:{value:t.node.checked,callback:function(e){t.$set(t.node,\"checked\",e)},expression:\"node.checked\"}}):t._e(),t.node.loading?n(\"span\",{staticClass:\"el-tree-node__loading-icon el-icon-loading\"}):t._e(),n(\"node-content\",{attrs:{node:t.node}})],1),n(\"el-collapse-transition\",[!t.renderAfterExpand||t.childNodeRendered?n(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:t.expanded,expression:\"expanded\"}],staticClass:\"el-tree-node__children\",attrs:{role:\"group\",\"aria-expanded\":t.expanded}},t._l(t.node.childNodes,function(e){return n(\"el-tree-node\",{key:t.getNodeKey(e),attrs:{\"render-content\":t.renderContent,\"render-after-expand\":t.renderAfterExpand,\"show-checkbox\":t.showCheckbox,node:e},on:{\"node-expand\":t.handleChildNodeExpand}})}),1):t._e()])],1)};Fo._withStripped=!0;var Lo=r({name:\"ElTreeNode\",componentName:\"ElTreeNode\",mixins:[l],props:{node:{default:function(){return{}}},props:{},renderContent:Function,renderAfterExpand:{type:Boolean,default:!0},showCheckbox:{type:Boolean,default:!1}},components:{ElCollapseTransition:ii,ElCheckbox:Vi,NodeContent:{props:{node:{required:!0}},render:function(e){var t=this.$parent,i=t.tree,n=this.node,r=n.data,s=n.store;return t.renderContent?t.renderContent.call(t._renderProxy,e,{_self:i.$vnode.context,node:n,data:r,store:s}):i.$scopedSlots.default?i.$scopedSlots.default({node:n,data:r}):e(\"span\",{class:\"el-tree-node__label\"},[n.label])}}},data:function(){return{tree:null,expanded:!1,childNodeRendered:!1,oldChecked:null,oldIndeterminate:null}},watch:{\"node.indeterminate\":function(e){this.handleSelectChange(this.node.checked,e)},\"node.checked\":function(e){this.handleSelectChange(e,this.node.indeterminate)},\"node.expanded\":function(e){var t=this;this.$nextTick(function(){return t.expanded=e}),e&&(this.childNodeRendered=!0)}},methods:{getNodeKey:function(e){return $o(this.tree.nodeKey,e.data)},handleSelectChange:function(e,t){this.oldChecked!==e&&this.oldIndeterminate!==t&&this.tree.$emit(\"check-change\",this.node.data,e,t),this.oldChecked=e,this.indeterminate=t},handleClick:function(){var e=this.tree.store;e.setCurrentNode(this.node),this.tree.$emit(\"current-change\",e.currentNode?e.currentNode.data:null,e.currentNode),this.tree.currentNode=this,this.tree.expandOnClickNode&&this.handleExpandIconClick(),this.tree.checkOnClickNode&&!this.node.disabled&&this.handleCheckChange(null,{target:{checked:!this.node.checked}}),this.tree.$emit(\"node-click\",this.node.data,this.node,this)},handleContextMenu:function(e){this.tree._events[\"node-contextmenu\"]&&this.tree._events[\"node-contextmenu\"].length>0&&(e.stopPropagation(),e.preventDefault()),this.tree.$emit(\"node-contextmenu\",e,this.node.data,this.node,this)},handleExpandIconClick:function(){this.node.isLeaf||(this.expanded?(this.tree.$emit(\"node-collapse\",this.node.data,this.node,this),this.node.collapse()):(this.node.expand(),this.$emit(\"node-expand\",this.node.data,this.node,this)))},handleCheckChange:function(e,t){var i=this;this.node.setChecked(t.target.checked,!this.tree.checkStrictly),this.$nextTick(function(){var e=i.tree.store;i.tree.$emit(\"check\",i.node.data,{checkedNodes:e.getCheckedNodes(),checkedKeys:e.getCheckedKeys(),halfCheckedNodes:e.getHalfCheckedNodes(),halfCheckedKeys:e.getHalfCheckedKeys()})})},handleChildNodeExpand:function(e,t,i){this.broadcast(\"ElTreeNode\",\"tree-node-expand\",t),this.tree.$emit(\"node-expand\",e,t,i)},handleDragStart:function(e){this.tree.draggable&&this.tree.$emit(\"tree-node-drag-start\",e,this)},handleDragOver:function(e){this.tree.draggable&&(this.tree.$emit(\"tree-node-drag-over\",e,this),e.preventDefault())},handleDrop:function(e){e.preventDefault()},handleDragEnd:function(e){this.tree.draggable&&this.tree.$emit(\"tree-node-drag-end\",e,this)}},created:function(){var e=this,t=this.$parent;t.isTree?this.tree=t:this.tree=t.tree;var i=this.tree;i||console.warn(\"Can not find node's tree.\");var n=(i.props||{}).children||\"children\";this.$watch(\"node.data.\"+n,function(){e.node.updateChildren()}),this.node.expanded&&(this.expanded=!0,this.childNodeRendered=!0),this.tree.accordion&&this.$on(\"tree-node-expand\",function(t){e.node!==t&&e.node.collapse()})}},Fo,[],!1,null,null,null);Lo.options.__file=\"packages/tree/src/tree-node.vue\";var Vo=r({name:\"ElTree\",mixins:[l],components:{ElTreeNode:Lo.exports},data:function(){return{store:null,root:null,currentNode:null,treeItems:null,checkboxItems:[],dragState:{showDropIndicator:!1,draggingNode:null,dropNode:null,allowDrop:!0}}},props:{data:{type:Array},emptyText:{type:String,default:function(){return W(\"el.tree.emptyText\")}},renderAfterExpand:{type:Boolean,default:!0},nodeKey:String,checkStrictly:Boolean,defaultExpandAll:Boolean,expandOnClickNode:{type:Boolean,default:!0},checkOnClickNode:Boolean,checkDescendants:{type:Boolean,default:!1},autoExpandParent:{type:Boolean,default:!0},defaultCheckedKeys:Array,defaultExpandedKeys:Array,currentNodeKey:[String,Number],renderContent:Function,showCheckbox:{type:Boolean,default:!1},draggable:{type:Boolean,default:!1},allowDrag:Function,allowDrop:Function,props:{default:function(){return{children:\"children\",label:\"label\",disabled:\"disabled\"}}},lazy:{type:Boolean,default:!1},highlightCurrent:Boolean,load:Function,filterNodeMethod:Function,accordion:Boolean,indent:{type:Number,default:18},iconClass:String},computed:{children:{set:function(e){this.data=e},get:function(){return this.data}},treeItemArray:function(){return Array.prototype.slice.call(this.treeItems)},isEmpty:function(){var e=this.root.childNodes;return!e||0===e.length||e.every(function(e){return!e.visible})}},watch:{defaultCheckedKeys:function(e){this.store.setDefaultCheckedKey(e)},defaultExpandedKeys:function(e){this.store.defaultExpandedKeys=e,this.store.setDefaultExpandedKeys(e)},data:function(e){this.store.setData(e)},checkboxItems:function(e){Array.prototype.forEach.call(e,function(e){e.setAttribute(\"tabindex\",-1)})},checkStrictly:function(e){this.store.checkStrictly=e}},methods:{filter:function(e){if(!this.filterNodeMethod)throw new Error(\"[Tree] filterNodeMethod is required when filter\");this.store.filter(e)},getNodeKey:function(e){return $o(this.nodeKey,e.data)},getNodePath:function(e){if(!this.nodeKey)throw new Error(\"[Tree] nodeKey is required in getNodePath\");var t=this.store.getNode(e);if(!t)return[];for(var i=[t.data],n=t.parent;n&&n!==this.root;)i.push(n.data),n=n.parent;return i.reverse()},getCheckedNodes:function(e,t){return this.store.getCheckedNodes(e,t)},getCheckedKeys:function(e){return this.store.getCheckedKeys(e)},getCurrentNode:function(){var e=this.store.getCurrentNode();return e?e.data:null},getCurrentKey:function(){if(!this.nodeKey)throw new Error(\"[Tree] nodeKey is required in getCurrentKey\");var e=this.getCurrentNode();return e?e[this.nodeKey]:null},setCheckedNodes:function(e,t){if(!this.nodeKey)throw new Error(\"[Tree] nodeKey is required in setCheckedNodes\");this.store.setCheckedNodes(e,t)},setCheckedKeys:function(e,t){if(!this.nodeKey)throw new Error(\"[Tree] nodeKey is required in setCheckedKeys\");this.store.setCheckedKeys(e,t)},setChecked:function(e,t,i){this.store.setChecked(e,t,i)},getHalfCheckedNodes:function(){return this.store.getHalfCheckedNodes()},getHalfCheckedKeys:function(){return this.store.getHalfCheckedKeys()},setCurrentNode:function(e){if(!this.nodeKey)throw new Error(\"[Tree] nodeKey is required in setCurrentNode\");this.store.setUserCurrentNode(e)},setCurrentKey:function(e){if(!this.nodeKey)throw new Error(\"[Tree] nodeKey is required in setCurrentKey\");this.store.setCurrentNodeKey(e)},getNode:function(e){return this.store.getNode(e)},remove:function(e){this.store.remove(e)},append:function(e,t){this.store.append(e,t)},insertBefore:function(e,t){this.store.insertBefore(e,t)},insertAfter:function(e,t){this.store.insertAfter(e,t)},handleNodeExpand:function(e,t,i){this.broadcast(\"ElTreeNode\",\"tree-node-expand\",t),this.$emit(\"node-expand\",e,t,i)},updateKeyChildren:function(e,t){if(!this.nodeKey)throw new Error(\"[Tree] nodeKey is required in updateKeyChild\");this.store.updateChildren(e,t)},initTabIndex:function(){this.treeItems=this.$el.querySelectorAll(\".is-focusable[role=treeitem]\"),this.checkboxItems=this.$el.querySelectorAll(\"input[type=checkbox]\");var e=this.$el.querySelectorAll(\".is-checked[role=treeitem]\");e.length?e[0].setAttribute(\"tabindex\",0):this.treeItems[0]&&this.treeItems[0].setAttribute(\"tabindex\",0)},handleKeydown:function(e){var t=e.target;if(-1!==t.className.indexOf(\"el-tree-node\")){var i=e.keyCode;this.treeItems=this.$el.querySelectorAll(\".is-focusable[role=treeitem]\");var n=this.treeItemArray.indexOf(t),r=void 0;[38,40].indexOf(i)>-1&&(e.preventDefault(),r=38===i?0!==n?n-1:0:n-1&&(e.preventDefault(),t.click());var s=t.querySelector('[type=\"checkbox\"]');[13,32].indexOf(i)>-1&&s&&(e.preventDefault(),s.click())}}},created:function(){var e=this;this.isTree=!0,this.store=new Ao({key:this.nodeKey,data:this.data,lazy:this.lazy,props:this.props,load:this.load,currentNodeKey:this.currentNodeKey,checkStrictly:this.checkStrictly,checkDescendants:this.checkDescendants,defaultCheckedKeys:this.defaultCheckedKeys,defaultExpandedKeys:this.defaultExpandedKeys,autoExpandParent:this.autoExpandParent,defaultExpandAll:this.defaultExpandAll,filterNodeMethod:this.filterNodeMethod}),this.root=this.store.root;var t=this.dragState;this.$on(\"tree-node-drag-start\",function(i,n){if(\"function\"==typeof e.allowDrag&&!e.allowDrag(n.node))return i.preventDefault(),!1;i.dataTransfer.effectAllowed=\"move\";try{i.dataTransfer.setData(\"text/plain\",\"\")}catch(e){}t.draggingNode=n,e.$emit(\"node-drag-start\",n.node,i)}),this.$on(\"tree-node-drag-over\",function(i,n){var r=function(e,t){for(var i=e;i&&\"BODY\"!==i.tagName;){if(i.__vue__&&i.__vue__.$options.name===t)return i.__vue__;i=i.parentNode}return null}(i.target,\"ElTreeNode\"),s=t.dropNode;s&&s!==r&&me(s.$el,\"is-drop-inner\");var a=t.draggingNode;if(a&&r){var o=!0,l=!0,u=!0,c=!0;\"function\"==typeof e.allowDrop&&(o=e.allowDrop(a.node,r.node,\"prev\"),c=l=e.allowDrop(a.node,r.node,\"inner\"),u=e.allowDrop(a.node,r.node,\"next\")),i.dataTransfer.dropEffect=l?\"move\":\"none\",(o||l||u)&&s!==r&&(s&&e.$emit(\"node-drag-leave\",a.node,s.node,i),e.$emit(\"node-drag-enter\",a.node,r.node,i)),(o||l||u)&&(t.dropNode=r),r.node.nextSibling===a.node&&(u=!1),r.node.previousSibling===a.node&&(o=!1),r.node.contains(a.node,!1)&&(l=!1),(a.node===r.node||a.node.contains(r.node))&&(o=!1,l=!1,u=!1);var h=r.$el.getBoundingClientRect(),d=e.$el.getBoundingClientRect(),p=void 0,f=o?l?.25:u?.45:1:-1,m=u?l?.75:o?.55:0:1,v=-9999,g=i.clientY-h.top;p=gh.height*m?\"after\":l?\"inner\":\"none\";var b=r.$el.querySelector(\".el-tree-node__expand-icon\").getBoundingClientRect(),y=e.$refs.dropIndicator;\"before\"===p?v=b.top-d.top:\"after\"===p&&(v=b.bottom-d.top),y.style.top=v+\"px\",y.style.left=b.right-d.left+\"px\",\"inner\"===p?fe(r.$el,\"is-drop-inner\"):me(r.$el,\"is-drop-inner\"),t.showDropIndicator=\"before\"===p||\"after\"===p,t.allowDrop=t.showDropIndicator||c,t.dropType=p,e.$emit(\"node-drag-over\",a.node,r.node,i)}}),this.$on(\"tree-node-drag-end\",function(i){var n=t.draggingNode,r=t.dropType,s=t.dropNode;if(i.preventDefault(),i.dataTransfer.dropEffect=\"move\",n&&s){var a={data:n.node.data};\"none\"!==r&&n.node.remove(),\"before\"===r?s.node.parent.insertBefore(a,s.node):\"after\"===r?s.node.parent.insertAfter(a,s.node):\"inner\"===r&&s.node.insertChild(a),\"none\"!==r&&e.store.registerNode(a),me(s.$el,\"is-drop-inner\"),e.$emit(\"node-drag-end\",n.node,s.node,r,i),\"none\"!==r&&e.$emit(\"node-drop\",n.node,s.node,r,i)}n&&!s&&e.$emit(\"node-drag-end\",n.node,null,r,i),t.showDropIndicator=!1,t.draggingNode=null,t.dropNode=null,t.allowDrop=!0})},mounted:function(){this.initTabIndex(),this.$el.addEventListener(\"keydown\",this.handleKeydown)},updated:function(){this.treeItems=this.$el.querySelectorAll(\"[role=treeitem]\"),this.checkboxItems=this.$el.querySelectorAll(\"input[type=checkbox]\")}},ko,[],!1,null,null,null);Vo.options.__file=\"packages/tree/src/tree.vue\";var Bo=Vo.exports;Bo.install=function(e){e.component(Bo.name,Bo)};var zo=Bo,Ho=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition\",{attrs:{name:\"el-alert-fade\"}},[i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.visible,expression:\"visible\"}],staticClass:\"el-alert\",class:[e.typeClass,e.center?\"is-center\":\"\",\"is-\"+e.effect],attrs:{role:\"alert\"}},[e.showIcon?i(\"i\",{staticClass:\"el-alert__icon\",class:[e.iconClass,e.isBigIcon]}):e._e(),i(\"div\",{staticClass:\"el-alert__content\"},[e.title||e.$slots.title?i(\"span\",{staticClass:\"el-alert__title\",class:[e.isBoldTitle]},[e._t(\"title\",[e._v(e._s(e.title))])],2):e._e(),e.$slots.default&&!e.description?i(\"p\",{staticClass:\"el-alert__description\"},[e._t(\"default\")],2):e._e(),e.description&&!e.$slots.default?i(\"p\",{staticClass:\"el-alert__description\"},[e._v(e._s(e.description))]):e._e(),i(\"i\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.closable,expression:\"closable\"}],staticClass:\"el-alert__closebtn\",class:{\"is-customed\":\"\"!==e.closeText,\"el-icon-close\":\"\"===e.closeText},on:{click:function(t){e.close()}}},[e._v(e._s(e.closeText))])])])])};Ho._withStripped=!0;var Ro={success:\"el-icon-success\",warning:\"el-icon-warning\",error:\"el-icon-error\"},Wo=r({name:\"ElAlert\",props:{title:{type:String,default:\"\"},description:{type:String,default:\"\"},type:{type:String,default:\"info\"},closable:{type:Boolean,default:!0},closeText:{type:String,default:\"\"},showIcon:Boolean,center:Boolean,effect:{type:String,default:\"light\",validator:function(e){return-1!==[\"light\",\"dark\"].indexOf(e)}}},data:function(){return{visible:!0}},methods:{close:function(){this.visible=!1,this.$emit(\"close\")}},computed:{typeClass:function(){return\"el-alert--\"+this.type},iconClass:function(){return Ro[this.type]||\"el-icon-info\"},isBigIcon:function(){return this.description||this.$slots.default?\"is-big\":\"\"},isBoldTitle:function(){return this.description||this.$slots.default?\"is-bold\":\"\"}}},Ho,[],!1,null,null,null);Wo.options.__file=\"packages/alert/src/main.vue\";var jo=Wo.exports;jo.install=function(e){e.component(jo.name,jo)};var qo=jo,Yo=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition\",{attrs:{name:\"el-notification-fade\"}},[i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.visible,expression:\"visible\"}],class:[\"el-notification\",e.customClass,e.horizontalClass],style:e.positionStyle,attrs:{role:\"alert\"},on:{mouseenter:function(t){e.clearTimer()},mouseleave:function(t){e.startTimer()},click:e.click}},[e.type||e.iconClass?i(\"i\",{staticClass:\"el-notification__icon\",class:[e.typeClass,e.iconClass]}):e._e(),i(\"div\",{staticClass:\"el-notification__group\",class:{\"is-with-icon\":e.typeClass||e.iconClass}},[i(\"h2\",{staticClass:\"el-notification__title\",domProps:{textContent:e._s(e.title)}}),i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.message,expression:\"message\"}],staticClass:\"el-notification__content\"},[e._t(\"default\",[e.dangerouslyUseHTMLString?i(\"p\",{domProps:{innerHTML:e._s(e.message)}}):i(\"p\",[e._v(e._s(e.message))])])],2),e.showClose?i(\"div\",{staticClass:\"el-notification__closeBtn el-icon-close\",on:{click:function(t){return t.stopPropagation(),e.close(t)}}}):e._e()])])])};Yo._withStripped=!0;var Ko={success:\"success\",info:\"info\",warning:\"warning\",error:\"error\"},Go=r({data:function(){return{visible:!1,title:\"\",message:\"\",duration:4500,type:\"\",showClose:!0,customClass:\"\",iconClass:\"\",onClose:null,onClick:null,closed:!1,verticalOffset:0,timer:null,dangerouslyUseHTMLString:!1,position:\"top-right\"}},computed:{typeClass:function(){return this.type&&Ko[this.type]?\"el-icon-\"+Ko[this.type]:\"\"},horizontalClass:function(){return this.position.indexOf(\"right\")>-1?\"right\":\"left\"},verticalProperty:function(){return/^top-/.test(this.position)?\"top\":\"bottom\"},positionStyle:function(){var e;return(e={})[this.verticalProperty]=this.verticalOffset+\"px\",e}},watch:{closed:function(e){e&&(this.visible=!1,this.$el.addEventListener(\"transitionend\",this.destroyElement))}},methods:{destroyElement:function(){this.$el.removeEventListener(\"transitionend\",this.destroyElement),this.$destroy(!0),this.$el.parentNode.removeChild(this.$el)},click:function(){\"function\"==typeof this.onClick&&this.onClick()},close:function(){this.closed=!0,\"function\"==typeof this.onClose&&this.onClose()},clearTimer:function(){clearTimeout(this.timer)},startTimer:function(){var e=this;this.duration>0&&(this.timer=setTimeout(function(){e.closed||e.close()},this.duration))},keydown:function(e){46===e.keyCode||8===e.keyCode?this.clearTimer():27===e.keyCode?this.closed||this.close():this.startTimer()}},mounted:function(){var e=this;this.duration>0&&(this.timer=setTimeout(function(){e.closed||e.close()},this.duration)),document.addEventListener(\"keydown\",this.keydown)},beforeDestroy:function(){document.removeEventListener(\"keydown\",this.keydown)}},Yo,[],!1,null,null,null);Go.options.__file=\"packages/notification/src/main.vue\";var Uo=Go.exports,Xo=h.a.extend(Uo),Jo=void 0,Zo=[],Qo=1,el=function e(t){if(!h.a.prototype.$isServer){var i=(t=Z({},t)).onClose,n=\"notification_\"+Qo++,r=t.position||\"top-right\";t.onClose=function(){e.close(n,i)},Jo=new Xo({data:t}),ua(t.message)&&(Jo.$slots.default=[t.message],t.message=\"REPLACED_BY_VNODE\"),Jo.id=n,Jo.$mount(),document.body.appendChild(Jo.$el),Jo.visible=!0,Jo.dom=Jo.$el,Jo.dom.style.zIndex=Se.nextZIndex();var s=t.offset||0;return Zo.filter(function(e){return e.position===r}).forEach(function(e){s+=e.$el.offsetHeight+16}),s+=16,Jo.verticalOffset=s,Zo.push(Jo),Jo}};[\"success\",\"warning\",\"info\",\"error\"].forEach(function(e){el[e]=function(t){return(\"string\"==typeof t||ua(t))&&(t={message:t}),t.type=e,el(t)}}),el.close=function(e,t){var i=-1,n=Zo.length,r=Zo.filter(function(t,n){return t.id===e&&(i=n,!0)})[0];if(r&&(\"function\"==typeof t&&t(r),Zo.splice(i,1),!(n<=1)))for(var s=r.position,a=r.dom.offsetHeight,o=i;o=0;e--)Zo[e].close()};var tl=el,il=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-slider\",class:{\"is-vertical\":e.vertical,\"el-slider--with-input\":e.showInput},attrs:{role:\"slider\",\"aria-valuemin\":e.min,\"aria-valuemax\":e.max,\"aria-orientation\":e.vertical?\"vertical\":\"horizontal\",\"aria-disabled\":e.sliderDisabled}},[e.showInput&&!e.range?i(\"el-input-number\",{ref:\"input\",staticClass:\"el-slider__input\",attrs:{step:e.step,disabled:e.sliderDisabled,controls:e.showInputControls,min:e.min,max:e.max,debounce:e.debounce,size:e.inputSize},on:{change:e.emitChange},model:{value:e.firstValue,callback:function(t){e.firstValue=t},expression:\"firstValue\"}}):e._e(),i(\"div\",{ref:\"slider\",staticClass:\"el-slider__runway\",class:{\"show-input\":e.showInput,disabled:e.sliderDisabled},style:e.runwayStyle,on:{click:e.onSliderClick}},[i(\"div\",{staticClass:\"el-slider__bar\",style:e.barStyle}),i(\"slider-button\",{ref:\"button1\",attrs:{vertical:e.vertical,\"tooltip-class\":e.tooltipClass},model:{value:e.firstValue,callback:function(t){e.firstValue=t},expression:\"firstValue\"}}),e.range?i(\"slider-button\",{ref:\"button2\",attrs:{vertical:e.vertical,\"tooltip-class\":e.tooltipClass},model:{value:e.secondValue,callback:function(t){e.secondValue=t},expression:\"secondValue\"}}):e._e(),e._l(e.stops,function(t,n){return e.showStops?i(\"div\",{key:n,staticClass:\"el-slider__stop\",style:e.getStopStyle(t)}):e._e()}),e.markList.length>0?[i(\"div\",e._l(e.markList,function(t,n){return i(\"div\",{key:n,staticClass:\"el-slider__stop el-slider__marks-stop\",style:e.getStopStyle(t.position)})}),0),i(\"div\",{staticClass:\"el-slider__marks\"},e._l(e.markList,function(t,n){return i(\"slider-marker\",{key:n,style:e.getStopStyle(t.position),attrs:{mark:t.mark}})}),1)]:e._e()],2)],1)};il._withStripped=!0;var nl=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{ref:\"button\",staticClass:\"el-slider__button-wrapper\",class:{hover:e.hovering,dragging:e.dragging},style:e.wrapperStyle,attrs:{tabindex:\"0\"},on:{mouseenter:e.handleMouseEnter,mouseleave:e.handleMouseLeave,mousedown:e.onButtonDown,touchstart:e.onButtonDown,focus:e.handleMouseEnter,blur:e.handleMouseLeave,keydown:[function(t){return\"button\"in t||!e._k(t.keyCode,\"left\",37,t.key,[\"Left\",\"ArrowLeft\"])?\"button\"in t&&0!==t.button?null:e.onLeftKeyDown(t):null},function(t){return\"button\"in t||!e._k(t.keyCode,\"right\",39,t.key,[\"Right\",\"ArrowRight\"])?\"button\"in t&&2!==t.button?null:e.onRightKeyDown(t):null},function(t){return\"button\"in t||!e._k(t.keyCode,\"down\",40,t.key,[\"Down\",\"ArrowDown\"])?(t.preventDefault(),e.onLeftKeyDown(t)):null},function(t){return\"button\"in t||!e._k(t.keyCode,\"up\",38,t.key,[\"Up\",\"ArrowUp\"])?(t.preventDefault(),e.onRightKeyDown(t)):null}]}},[i(\"el-tooltip\",{ref:\"tooltip\",attrs:{placement:\"top\",\"popper-class\":e.tooltipClass,disabled:!e.showTooltip}},[i(\"span\",{attrs:{slot:\"content\"},slot:\"content\"},[e._v(e._s(e.formatValue))]),i(\"div\",{staticClass:\"el-slider__button\",class:{hover:e.hovering,dragging:e.dragging}})])],1)};nl._withStripped=!0;var rl=r({name:\"ElSliderButton\",components:{ElTooltip:ui},props:{value:{type:Number,default:0},vertical:{type:Boolean,default:!1},tooltipClass:String},data:function(){return{hovering:!1,dragging:!1,isClick:!1,startX:0,currentX:0,startY:0,currentY:0,startPosition:0,newPosition:null,oldValue:this.value}},computed:{disabled:function(){return this.$parent.sliderDisabled},max:function(){return this.$parent.max},min:function(){return this.$parent.min},step:function(){return this.$parent.step},showTooltip:function(){return this.$parent.showTooltip},precision:function(){return this.$parent.precision},currentPosition:function(){return(this.value-this.min)/(this.max-this.min)*100+\"%\"},enableFormat:function(){return this.$parent.formatTooltip instanceof Function},formatValue:function(){return this.enableFormat&&this.$parent.formatTooltip(this.value)||this.value},wrapperStyle:function(){return this.vertical?{bottom:this.currentPosition}:{left:this.currentPosition}}},watch:{dragging:function(e){this.$parent.dragging=e}},methods:{displayTooltip:function(){this.$refs.tooltip&&(this.$refs.tooltip.showPopper=!0)},hideTooltip:function(){this.$refs.tooltip&&(this.$refs.tooltip.showPopper=!1)},handleMouseEnter:function(){this.hovering=!0,this.displayTooltip()},handleMouseLeave:function(){this.hovering=!1,this.hideTooltip()},onButtonDown:function(e){this.disabled||(e.preventDefault(),this.onDragStart(e),window.addEventListener(\"mousemove\",this.onDragging),window.addEventListener(\"touchmove\",this.onDragging),window.addEventListener(\"mouseup\",this.onDragEnd),window.addEventListener(\"touchend\",this.onDragEnd),window.addEventListener(\"contextmenu\",this.onDragEnd))},onLeftKeyDown:function(){this.disabled||(this.newPosition=parseFloat(this.currentPosition)-this.step/(this.max-this.min)*100,this.setPosition(this.newPosition),this.$parent.emitChange())},onRightKeyDown:function(){this.disabled||(this.newPosition=parseFloat(this.currentPosition)+this.step/(this.max-this.min)*100,this.setPosition(this.newPosition),this.$parent.emitChange())},onDragStart:function(e){this.dragging=!0,this.isClick=!0,\"touchstart\"===e.type&&(e.clientY=e.touches[0].clientY,e.clientX=e.touches[0].clientX),this.vertical?this.startY=e.clientY:this.startX=e.clientX,this.startPosition=parseFloat(this.currentPosition),this.newPosition=this.startPosition},onDragging:function(e){if(this.dragging){this.isClick=!1,this.displayTooltip(),this.$parent.resetSize();var t=0;\"touchmove\"===e.type&&(e.clientY=e.touches[0].clientY,e.clientX=e.touches[0].clientX),this.vertical?(this.currentY=e.clientY,t=(this.startY-this.currentY)/this.$parent.sliderSize*100):(this.currentX=e.clientX,t=(this.currentX-this.startX)/this.$parent.sliderSize*100),this.newPosition=this.startPosition+t,this.setPosition(this.newPosition)}},onDragEnd:function(){var e=this;this.dragging&&(setTimeout(function(){e.dragging=!1,e.hideTooltip(),e.isClick||(e.setPosition(e.newPosition),e.$parent.emitChange())},0),window.removeEventListener(\"mousemove\",this.onDragging),window.removeEventListener(\"touchmove\",this.onDragging),window.removeEventListener(\"mouseup\",this.onDragEnd),window.removeEventListener(\"touchend\",this.onDragEnd),window.removeEventListener(\"contextmenu\",this.onDragEnd))},setPosition:function(e){var t=this;if(null!==e&&!isNaN(e)){e<0?e=0:e>100&&(e=100);var i=100/((this.max-this.min)/this.step),n=Math.round(e/i)*i*(this.max-this.min)*.01+this.min;n=parseFloat(n.toFixed(this.precision)),this.$emit(\"input\",n),this.$nextTick(function(){t.displayTooltip(),t.$refs.tooltip&&t.$refs.tooltip.updatePopper()}),this.dragging||this.value===this.oldValue||(this.oldValue=this.value)}}}},nl,[],!1,null,null,null);rl.options.__file=\"packages/slider/src/button.vue\";var sl=rl.exports,al={name:\"ElMarker\",props:{mark:{type:[String,Object]}},render:function(){var e=arguments[0],t=\"string\"==typeof this.mark?this.mark:this.mark.label;return e(\"div\",{class:\"el-slider__marks-text\",style:this.mark.style||{}},[t])}},ol=r({name:\"ElSlider\",mixins:[l],inject:{elForm:{default:\"\"}},props:{min:{type:Number,default:0},max:{type:Number,default:100},step:{type:Number,default:1},value:{type:[Number,Array],default:0},showInput:{type:Boolean,default:!1},showInputControls:{type:Boolean,default:!0},inputSize:{type:String,default:\"small\"},showStops:{type:Boolean,default:!1},showTooltip:{type:Boolean,default:!0},formatTooltip:Function,disabled:{type:Boolean,default:!1},range:{type:Boolean,default:!1},vertical:{type:Boolean,default:!1},height:{type:String},debounce:{type:Number,default:300},label:{type:String},tooltipClass:String,marks:Object},components:{ElInputNumber:_i,SliderButton:sl,SliderMarker:al},data:function(){return{firstValue:null,secondValue:null,oldValue:null,dragging:!1,sliderSize:1}},watch:{value:function(e,t){this.dragging||Array.isArray(e)&&Array.isArray(t)&&e.every(function(e,i){return e===t[i]})||this.setValues()},dragging:function(e){e||this.setValues()},firstValue:function(e){this.range?this.$emit(\"input\",[this.minValue,this.maxValue]):this.$emit(\"input\",e)},secondValue:function(){this.range&&this.$emit(\"input\",[this.minValue,this.maxValue])},min:function(){this.setValues()},max:function(){this.setValues()}},methods:{valueChanged:function(){var e=this;return this.range?![this.minValue,this.maxValue].every(function(t,i){return t===e.oldValue[i]}):this.value!==this.oldValue},setValues:function(){if(this.min>this.max)console.error(\"[Element Error][Slider]min should not be greater than max.\");else{var e=this.value;this.range&&Array.isArray(e)?e[1]this.max?this.$emit(\"input\",[this.max,this.max]):e[0]this.max?this.$emit(\"input\",[e[0],this.max]):(this.firstValue=e[0],this.secondValue=e[1],this.valueChanged()&&(this.dispatch(\"ElFormItem\",\"el.form.change\",[this.minValue,this.maxValue]),this.oldValue=e.slice())):this.range||\"number\"!=typeof e||isNaN(e)||(ethis.max?this.$emit(\"input\",this.max):(this.firstValue=e,this.valueChanged()&&(this.dispatch(\"ElFormItem\",\"el.form.change\",e),this.oldValue=e)))}},setPosition:function(e){var t=this.min+e*(this.max-this.min)/100;if(this.range){var i=void 0;i=Math.abs(this.minValue-t)this.secondValue?\"button1\":\"button2\",this.$refs[i].setPosition(e)}else this.$refs.button1.setPosition(e)},onSliderClick:function(e){if(!this.sliderDisabled&&!this.dragging){if(this.resetSize(),this.vertical){var t=this.$refs.slider.getBoundingClientRect().bottom;this.setPosition((t-e.clientY)/this.sliderSize*100)}else{var i=this.$refs.slider.getBoundingClientRect().left;this.setPosition((e.clientX-i)/this.sliderSize*100)}this.emitChange()}},resetSize:function(){this.$refs.slider&&(this.sliderSize=this.$refs.slider[\"client\"+(this.vertical?\"Height\":\"Width\")])},emitChange:function(){var e=this;this.$nextTick(function(){e.$emit(\"change\",e.range?[e.minValue,e.maxValue]:e.value)})},getStopStyle:function(e){return this.vertical?{bottom:e+\"%\"}:{left:e+\"%\"}}},computed:{stops:function(){var e=this;if(!this.showStops||this.min>this.max)return[];if(0===this.step)return[];for(var t=(this.max-this.min)/this.step,i=100*this.step/(this.max-this.min),n=[],r=1;r100*(e.maxValue-e.min)/(e.max-e.min)}):n.filter(function(t){return t>100*(e.firstValue-e.min)/(e.max-e.min)})},markList:function(){var e=this;return this.marks?Object.keys(this.marks).map(parseFloat).sort(function(e,t){return e-t}).filter(function(t){return t<=e.max&&t>=e.min}).map(function(t){return{point:t,position:100*(t-e.min)/(e.max-e.min),mark:e.marks[t]}}):[]},minValue:function(){return Math.min(this.firstValue,this.secondValue)},maxValue:function(){return Math.max(this.firstValue,this.secondValue)},barSize:function(){return this.range?100*(this.maxValue-this.minValue)/(this.max-this.min)+\"%\":100*(this.firstValue-this.min)/(this.max-this.min)+\"%\"},barStart:function(){return this.range?100*(this.minValue-this.min)/(this.max-this.min)+\"%\":\"0%\"},precision:function(){var e=[this.min,this.max,this.step].map(function(e){var t=(\"\"+e).split(\".\")[1];return t?t.length:0});return Math.max.apply(null,e)},runwayStyle:function(){return this.vertical?{height:this.height}:{}},barStyle:function(){return this.vertical?{height:this.barSize,bottom:this.barStart}:{width:this.barSize,left:this.barStart}},sliderDisabled:function(){return this.disabled||(this.elForm||{}).disabled}},mounted:function(){var e=void 0;this.range?(Array.isArray(this.value)?(this.firstValue=Math.max(this.min,this.value[0]),this.secondValue=Math.min(this.max,this.value[1])):(this.firstValue=this.min,this.secondValue=this.max),this.oldValue=[this.firstValue,this.secondValue],e=this.firstValue+\"-\"+this.secondValue):(\"number\"!=typeof this.value||isNaN(this.value)?this.firstValue=this.min:this.firstValue=Math.min(this.max,Math.max(this.min,this.value)),this.oldValue=this.firstValue,e=this.firstValue),this.$el.setAttribute(\"aria-valuetext\",e),this.$el.setAttribute(\"aria-label\",this.label?this.label:\"slider between \"+this.min+\" and \"+this.max),this.resetSize(),window.addEventListener(\"resize\",this.resetSize)},beforeDestroy:function(){window.removeEventListener(\"resize\",this.resetSize)}},il,[],!1,null,null,null);ol.options.__file=\"packages/slider/src/main.vue\";var ll=ol.exports;ll.install=function(e){e.component(ll.name,ll)};var ul=ll,cl=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition\",{attrs:{name:\"el-loading-fade\"},on:{\"after-leave\":e.handleAfterLeave}},[i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.visible,expression:\"visible\"}],staticClass:\"el-loading-mask\",class:[e.customClass,{\"is-fullscreen\":e.fullscreen}],style:{backgroundColor:e.background||\"\"}},[i(\"div\",{staticClass:\"el-loading-spinner\"},[e.spinner?i(\"i\",{class:e.spinner}):i(\"svg\",{staticClass:\"circular\",attrs:{viewBox:\"25 25 50 50\"}},[i(\"circle\",{staticClass:\"path\",attrs:{cx:\"50\",cy:\"50\",r:\"20\",fill:\"none\"}})]),e.text?i(\"p\",{staticClass:\"el-loading-text\"},[e._v(e._s(e.text))]):e._e()])])])};cl._withStripped=!0;var hl=r({data:function(){return{text:null,spinner:null,background:null,fullscreen:!0,visible:!1,customClass:\"\"}},methods:{handleAfterLeave:function(){this.$emit(\"after-leave\")},setText:function(e){this.text=e}}},cl,[],!1,null,null,null);hl.options.__file=\"packages/loading/src/loading.vue\";var dl=hl.exports,pl=function(e,t){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:300,n=arguments.length>3&&void 0!==arguments[3]&&arguments[3];if(!e||!t)throw new Error(\"instance & callback is required\");var r=!1,s=function(){r||(r=!0,t&&t.apply(null,arguments))};n?e.$once(\"after-leave\",s):e.$on(\"after-leave\",s),setTimeout(function(){s()},i+100)},fl=h.a.extend(dl),ml={install:function(e){if(!e.prototype.$isServer){var t=function(t,n){n.value?e.nextTick(function(){n.modifiers.fullscreen?(t.originalPosition=ve(document.body,\"position\"),t.originalOverflow=ve(document.body,\"overflow\"),t.maskStyle.zIndex=Se.nextZIndex(),fe(t.mask,\"is-fullscreen\"),i(document.body,t,n)):(me(t.mask,\"is-fullscreen\"),n.modifiers.body?(t.originalPosition=ve(document.body,\"position\"),[\"top\",\"left\"].forEach(function(e){var i=\"top\"===e?\"scrollTop\":\"scrollLeft\";t.maskStyle[e]=t.getBoundingClientRect()[e]+document.body[i]+document.documentElement[i]-parseInt(ve(document.body,\"margin-\"+e),10)+\"px\"}),[\"height\",\"width\"].forEach(function(e){t.maskStyle[e]=t.getBoundingClientRect()[e]+\"px\"}),i(document.body,t,n)):(t.originalPosition=ve(t,\"position\"),i(t,t,n)))}):(pl(t.instance,function(e){if(t.instance.hiding){t.domVisible=!1;var i=n.modifiers.fullscreen||n.modifiers.body?document.body:t;me(i,\"el-loading-parent--relative\"),me(i,\"el-loading-parent--hidden\"),t.instance.hiding=!1}},300,!0),t.instance.visible=!1,t.instance.hiding=!0)},i=function(t,i,n){i.domVisible||\"none\"===ve(i,\"display\")||\"hidden\"===ve(i,\"visibility\")?i.domVisible&&!0===i.instance.hiding&&(i.instance.visible=!0,i.instance.hiding=!1):(Object.keys(i.maskStyle).forEach(function(e){i.mask.style[e]=i.maskStyle[e]}),\"absolute\"!==i.originalPosition&&\"fixed\"!==i.originalPosition&&fe(t,\"el-loading-parent--relative\"),n.modifiers.fullscreen&&n.modifiers.lock&&fe(t,\"el-loading-parent--hidden\"),i.domVisible=!0,t.appendChild(i.mask),e.nextTick(function(){i.instance.hiding?i.instance.$emit(\"after-leave\"):i.instance.visible=!0}),i.domInserted=!0)};e.directive(\"loading\",{bind:function(e,i,n){var r=e.getAttribute(\"element-loading-text\"),s=e.getAttribute(\"element-loading-spinner\"),a=e.getAttribute(\"element-loading-background\"),o=e.getAttribute(\"element-loading-custom-class\"),l=n.context,u=new fl({el:document.createElement(\"div\"),data:{text:l&&l[r]||r,spinner:l&&l[s]||s,background:l&&l[a]||a,customClass:l&&l[o]||o,fullscreen:!!i.modifiers.fullscreen}});e.instance=u,e.mask=u.$el,e.maskStyle={},i.value&&t(e,i)},update:function(e,i){e.instance.setText(e.getAttribute(\"element-loading-text\")),i.oldValue!==i.value&&t(e,i)},unbind:function(e,i){e.domInserted&&(e.mask&&e.mask.parentNode&&e.mask.parentNode.removeChild(e.mask),t(e,{value:!1,modifiers:i.modifiers})),e.instance&&e.instance.$destroy()}})}}},vl=ml,gl=h.a.extend(dl),bl={text:null,fullscreen:!0,body:!1,lock:!1,customClass:\"\"},yl=void 0;gl.prototype.originalPosition=\"\",gl.prototype.originalOverflow=\"\",gl.prototype.close=function(){var e=this;this.fullscreen&&(yl=void 0),pl(this,function(t){var i=e.fullscreen||e.body?document.body:e.target;me(i,\"el-loading-parent--relative\"),me(i,\"el-loading-parent--hidden\"),e.$el&&e.$el.parentNode&&e.$el.parentNode.removeChild(e.$el),e.$destroy()},300),this.visible=!1};var wl=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!h.a.prototype.$isServer){if(\"string\"==typeof(e=Z({},bl,e)).target&&(e.target=document.querySelector(e.target)),e.target=e.target||document.body,e.target!==document.body?e.fullscreen=!1:e.body=!0,e.fullscreen&&yl)return yl;var t=e.body?document.body:e.target,i=new gl({el:document.createElement(\"div\"),data:e});return function(e,t,i){var n={};e.fullscreen?(i.originalPosition=ve(document.body,\"position\"),i.originalOverflow=ve(document.body,\"overflow\"),n.zIndex=Se.nextZIndex()):e.body?(i.originalPosition=ve(document.body,\"position\"),[\"top\",\"left\"].forEach(function(t){var i=\"top\"===t?\"scrollTop\":\"scrollLeft\";n[t]=e.target.getBoundingClientRect()[t]+document.body[i]+document.documentElement[i]+\"px\"}),[\"height\",\"width\"].forEach(function(t){n[t]=e.target.getBoundingClientRect()[t]+\"px\"})):i.originalPosition=ve(t,\"position\"),Object.keys(n).forEach(function(e){i.$el.style[e]=n[e]})}(e,t,i),\"absolute\"!==i.originalPosition&&\"fixed\"!==i.originalPosition&&fe(t,\"el-loading-parent--relative\"),e.fullscreen&&e.lock&&fe(t,\"el-loading-parent--hidden\"),t.appendChild(i.$el),h.a.nextTick(function(){i.visible=!0}),e.fullscreen&&(yl=i),i}},_l={install:function(e){e.use(vl),e.prototype.$loading=wl},directive:vl,service:wl},xl=function(){var e=this.$createElement;return(this._self._c||e)(\"i\",{class:\"el-icon-\"+this.name})};xl._withStripped=!0;var Cl=r({name:\"ElIcon\",props:{name:String}},xl,[],!1,null,null,null);Cl.options.__file=\"packages/icon/src/icon.vue\";var kl=Cl.exports;kl.install=function(e){e.component(kl.name,kl)};var Sl=kl,Dl={name:\"ElRow\",componentName:\"ElRow\",props:{tag:{type:String,default:\"div\"},gutter:Number,type:String,justify:{type:String,default:\"start\"},align:{type:String,default:\"top\"}},computed:{style:function(){var e={};return this.gutter&&(e.marginLeft=\"-\"+this.gutter/2+\"px\",e.marginRight=e.marginLeft),e}},render:function(e){return e(this.tag,{class:[\"el-row\",\"start\"!==this.justify?\"is-justify-\"+this.justify:\"\",\"top\"!==this.align?\"is-align-\"+this.align:\"\",{\"el-row--flex\":\"flex\"===this.type}],style:this.style},this.$slots.default)},install:function(e){e.component(Dl.name,Dl)}},$l=Dl,El=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e},Tl={name:\"ElCol\",props:{span:{type:Number,default:24},tag:{type:String,default:\"div\"},offset:Number,pull:Number,push:Number,xs:[Number,Object],sm:[Number,Object],md:[Number,Object],lg:[Number,Object],xl:[Number,Object]},computed:{gutter:function(){for(var e=this.$parent;e&&\"ElRow\"!==e.$options.componentName;)e=e.$parent;return e?e.gutter:0}},render:function(e){var t=this,i=[],n={};return this.gutter&&(n.paddingLeft=this.gutter/2+\"px\",n.paddingRight=n.paddingLeft),[\"span\",\"offset\",\"pull\",\"push\"].forEach(function(e){(t[e]||0===t[e])&&i.push(\"span\"!==e?\"el-col-\"+e+\"-\"+t[e]:\"el-col-\"+t[e])}),[\"xs\",\"sm\",\"md\",\"lg\",\"xl\"].forEach(function(e){if(\"number\"==typeof t[e])i.push(\"el-col-\"+e+\"-\"+t[e]);else if(\"object\"===El(t[e])){var n=t[e];Object.keys(n).forEach(function(t){i.push(\"span\"!==t?\"el-col-\"+e+\"-\"+t+\"-\"+n[t]:\"el-col-\"+e+\"-\"+n[t])})}}),e(this.tag,{class:[\"el-col\",i],style:n},this.$slots.default)},install:function(e){e.component(Tl.name,Tl)}},Ml=Tl,Nl=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition-group\",{class:[\"el-upload-list\",\"el-upload-list--\"+e.listType,{\"is-disabled\":e.disabled}],attrs:{tag:\"ul\",name:\"el-list\"}},e._l(e.files,function(t){return i(\"li\",{key:t.uid,class:[\"el-upload-list__item\",\"is-\"+t.status,e.focusing?\"focusing\":\"\"],attrs:{tabindex:\"0\"},on:{keydown:function(i){if(!(\"button\"in i)&&e._k(i.keyCode,\"delete\",[8,46],i.key,[\"Backspace\",\"Delete\",\"Del\"]))return null;!e.disabled&&e.$emit(\"remove\",t)},focus:function(t){e.focusing=!0},blur:function(t){e.focusing=!1},click:function(t){e.focusing=!1}}},[e._t(\"default\",[\"uploading\"!==t.status&&[\"picture-card\",\"picture\"].indexOf(e.listType)>-1?i(\"img\",{staticClass:\"el-upload-list__item-thumbnail\",attrs:{src:t.url,alt:\"\"}}):e._e(),i(\"a\",{staticClass:\"el-upload-list__item-name\",on:{click:function(i){e.handleClick(t)}}},[i(\"i\",{staticClass:\"el-icon-document\"}),e._v(e._s(t.name)+\"\\n \")]),i(\"label\",{staticClass:\"el-upload-list__item-status-label\"},[i(\"i\",{class:{\"el-icon-upload-success\":!0,\"el-icon-circle-check\":\"text\"===e.listType,\"el-icon-check\":[\"picture-card\",\"picture\"].indexOf(e.listType)>-1}})]),e.disabled?e._e():i(\"i\",{staticClass:\"el-icon-close\",on:{click:function(i){e.$emit(\"remove\",t)}}}),e.disabled?e._e():i(\"i\",{staticClass:\"el-icon-close-tip\"},[e._v(e._s(e.t(\"el.upload.deleteTip\")))]),\"uploading\"===t.status?i(\"el-progress\",{attrs:{type:\"picture-card\"===e.listType?\"circle\":\"line\",\"stroke-width\":\"picture-card\"===e.listType?6:2,percentage:e.parsePercentage(t.percentage)}}):e._e(),\"picture-card\"===e.listType?i(\"span\",{staticClass:\"el-upload-list__item-actions\"},[e.handlePreview&&\"picture-card\"===e.listType?i(\"span\",{staticClass:\"el-upload-list__item-preview\",on:{click:function(i){e.handlePreview(t)}}},[i(\"i\",{staticClass:\"el-icon-zoom-in\"})]):e._e(),e.disabled?e._e():i(\"span\",{staticClass:\"el-upload-list__item-delete\",on:{click:function(i){e.$emit(\"remove\",t)}}},[i(\"i\",{staticClass:\"el-icon-delete\"})])]):e._e()],{file:t})],2)}),0)};Nl._withStripped=!0;var Pl=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-progress\",class:[\"el-progress--\"+e.type,e.status?\"is-\"+e.status:\"\",{\"el-progress--without-text\":!e.showText,\"el-progress--text-inside\":e.textInside}],attrs:{role:\"progressbar\",\"aria-valuenow\":e.percentage,\"aria-valuemin\":\"0\",\"aria-valuemax\":\"100\"}},[\"line\"===e.type?i(\"div\",{staticClass:\"el-progress-bar\"},[i(\"div\",{staticClass:\"el-progress-bar__outer\",style:{height:e.strokeWidth+\"px\"}},[i(\"div\",{staticClass:\"el-progress-bar__inner\",style:e.barStyle},[e.showText&&e.textInside?i(\"div\",{staticClass:\"el-progress-bar__innerText\"},[e._v(e._s(e.content))]):e._e()])])]):i(\"div\",{staticClass:\"el-progress-circle\",style:{height:e.width+\"px\",width:e.width+\"px\"}},[i(\"svg\",{attrs:{viewBox:\"0 0 100 100\"}},[i(\"path\",{staticClass:\"el-progress-circle__track\",style:e.trailPathStyle,attrs:{d:e.trackPath,stroke:\"#e5e9f2\",\"stroke-width\":e.relativeStrokeWidth,fill:\"none\"}}),i(\"path\",{staticClass:\"el-progress-circle__path\",style:e.circlePathStyle,attrs:{d:e.trackPath,stroke:e.stroke,fill:\"none\",\"stroke-linecap\":e.strokeLinecap,\"stroke-width\":e.percentage?e.relativeStrokeWidth:0}})])]),e.showText&&!e.textInside?i(\"div\",{staticClass:\"el-progress__text\",style:{fontSize:e.progressTextSize+\"px\"}},[e.status?i(\"i\",{class:e.iconClass}):[e._v(e._s(e.content))]],2):e._e()])};Pl._withStripped=!0;var Ol=r({name:\"ElProgress\",props:{type:{type:String,default:\"line\",validator:function(e){return[\"line\",\"circle\",\"dashboard\"].indexOf(e)>-1}},percentage:{type:Number,default:0,required:!0,validator:function(e){return e>=0&&e<=100}},status:{type:String,validator:function(e){return[\"success\",\"exception\",\"warning\"].indexOf(e)>-1}},strokeWidth:{type:Number,default:6},strokeLinecap:{type:String,default:\"round\"},textInside:{type:Boolean,default:!1},width:{type:Number,default:126},showText:{type:Boolean,default:!0},color:{type:[String,Array,Function],default:\"\"},format:Function},computed:{barStyle:function(){var e={};return e.width=this.percentage+\"%\",e.backgroundColor=this.getCurrentColor(this.percentage),e},relativeStrokeWidth:function(){return(this.strokeWidth/this.width*100).toFixed(1)},radius:function(){return\"circle\"===this.type||\"dashboard\"===this.type?parseInt(50-parseFloat(this.relativeStrokeWidth)/2,10):0},trackPath:function(){var e=this.radius,t=\"dashboard\"===this.type;return\"\\n M 50 50\\n m 0 \"+(t?\"\":\"-\")+e+\"\\n a \"+e+\" \"+e+\" 0 1 1 0 \"+(t?\"-\":\"\")+2*e+\"\\n a \"+e+\" \"+e+\" 0 1 1 0 \"+(t?\"\":\"-\")+2*e+\"\\n \"},perimeter:function(){return 2*Math.PI*this.radius},rate:function(){return\"dashboard\"===this.type?.75:1},strokeDashoffset:function(){return-1*this.perimeter*(1-this.rate)/2+\"px\"},trailPathStyle:function(){return{strokeDasharray:this.perimeter*this.rate+\"px, \"+this.perimeter+\"px\",strokeDashoffset:this.strokeDashoffset}},circlePathStyle:function(){return{strokeDasharray:this.perimeter*this.rate*(this.percentage/100)+\"px, \"+this.perimeter+\"px\",strokeDashoffset:this.strokeDashoffset,transition:\"stroke-dasharray 0.6s ease 0s, stroke 0.6s ease\"}},stroke:function(){var e=void 0;if(this.color)e=this.getCurrentColor(this.percentage);else switch(this.status){case\"success\":e=\"#13ce66\";break;case\"exception\":e=\"#ff4949\";break;case\"warning\":e=\"#e6a23c\";break;default:e=\"#20a0ff\"}return e},iconClass:function(){return\"warning\"===this.status?\"el-icon-warning\":\"line\"===this.type?\"success\"===this.status?\"el-icon-circle-check\":\"el-icon-circle-close\":\"success\"===this.status?\"el-icon-check\":\"el-icon-close\"},progressTextSize:function(){return\"line\"===this.type?12+.4*this.strokeWidth:.111111*this.width+2},content:function(){return\"function\"==typeof this.format?this.format(this.percentage)||\"\":this.percentage+\"%\"}},methods:{getCurrentColor:function(e){return\"function\"==typeof this.color?this.color(e):\"string\"==typeof this.color?this.color:this.getLevelColor(e)},getLevelColor:function(e){for(var t=this.getColorArray().sort(function(e,t){return e.percentage-t.percentage}),i=0;ie)return t[i].color;return t[t.length-1].color},getColorArray:function(){var e=this.color,t=100/e.length;return e.map(function(e,i){return\"string\"==typeof e?{color:e,progress:(i+1)*t}:e})}}},Pl,[],!1,null,null,null);Ol.options.__file=\"packages/progress/src/progress.vue\";var Il=Ol.exports;Il.install=function(e){e.component(Il.name,Il)};var Al=Il,Fl=r({name:\"ElUploadList\",mixins:[q],data:function(){return{focusing:!1}},components:{ElProgress:Al},props:{files:{type:Array,default:function(){return[]}},disabled:{type:Boolean,default:!1},handlePreview:Function,listType:String},methods:{parsePercentage:function(e){return parseInt(e,10)},handleClick:function(e){this.handlePreview&&this.handlePreview(e)}}},Nl,[],!1,null,null,null);Fl.options.__file=\"packages/upload/src/upload-list.vue\";var Ll=Fl.exports,Vl=i(6),Bl=i.n(Vl);var zl=function(){var e=this,t=e.$createElement;return(e._self._c||t)(\"div\",{staticClass:\"el-upload-dragger\",class:{\"is-dragover\":e.dragover},on:{drop:function(t){return t.preventDefault(),e.onDrop(t)},dragover:function(t){return t.preventDefault(),e.onDragover(t)},dragleave:function(t){t.preventDefault(),e.dragover=!1}}},[e._t(\"default\")],2)};zl._withStripped=!0;var Hl=r({name:\"ElUploadDrag\",props:{disabled:Boolean},inject:{uploader:{default:\"\"}},data:function(){return{dragover:!1}},methods:{onDragover:function(){this.disabled||(this.dragover=!0)},onDrop:function(e){if(!this.disabled&&this.uploader){var t=this.uploader.accept;this.dragover=!1,t?this.$emit(\"file\",[].slice.call(e.dataTransfer.files).filter(function(e){var i=e.type,n=e.name,r=n.indexOf(\".\")>-1?\".\"+n.split(\".\").pop():\"\",s=i.replace(/\\/.*$/,\"\");return t.split(\",\").map(function(e){return e.trim()}).filter(function(e){return e}).some(function(e){return/\\..+$/.test(e)?r===e:/\\/\\*$/.test(e)?s===e.replace(/\\/\\*$/,\"\"):!!/^[^\\/]+\\/[^\\/]+$/.test(e)&&i===e})})):this.$emit(\"file\",e.dataTransfer.files)}}}},zl,[],!1,null,null,null);Hl.options.__file=\"packages/upload/src/upload-dragger.vue\";var Rl=r({inject:[\"uploader\"],components:{UploadDragger:Hl.exports},props:{type:String,action:{type:String,required:!0},name:{type:String,default:\"file\"},data:Object,headers:Object,withCredentials:Boolean,multiple:Boolean,accept:String,onStart:Function,onProgress:Function,onSuccess:Function,onError:Function,beforeUpload:Function,drag:Boolean,onPreview:{type:Function,default:function(){}},onRemove:{type:Function,default:function(){}},fileList:Array,autoUpload:Boolean,listType:String,httpRequest:{type:Function,default:function(e){if(\"undefined\"!=typeof XMLHttpRequest){var t=new XMLHttpRequest,i=e.action;t.upload&&(t.upload.onprogress=function(t){t.total>0&&(t.percent=t.loaded/t.total*100),e.onProgress(t)});var n=new FormData;e.data&&Object.keys(e.data).forEach(function(t){n.append(t,e.data[t])}),n.append(e.filename,e.file,e.file.name),t.onerror=function(t){e.onError(t)},t.onload=function(){if(t.status<200||t.status>=300)return e.onError(function(e,t,i){var n=void 0;n=i.response?\"\"+(i.response.error||i.response):i.responseText?\"\"+i.responseText:\"fail to post \"+e+\" \"+i.status;var r=new Error(n);return r.status=i.status,r.method=\"post\",r.url=e,r}(i,0,t));e.onSuccess(function(e){var t=e.responseText||e.response;if(!t)return t;try{return JSON.parse(t)}catch(e){return t}}(t))},t.open(\"post\",i,!0),e.withCredentials&&\"withCredentials\"in t&&(t.withCredentials=!0);var r=e.headers||{};for(var s in r)r.hasOwnProperty(s)&&null!==r[s]&&t.setRequestHeader(s,r[s]);return t.send(n),t}}},disabled:Boolean,limit:Number,onExceed:Function},data:function(){return{mouseover:!1,reqs:{}}},methods:{isImage:function(e){return-1!==e.indexOf(\"image\")},handleChange:function(e){var t=e.target.files;t&&this.uploadFiles(t)},uploadFiles:function(e){var t=this;if(this.limit&&this.fileList.length+e.length>this.limit)this.onExceed&&this.onExceed(e,this.fileList);else{var i=Array.prototype.slice.call(e);this.multiple||(i=i.slice(0,1)),0!==i.length&&i.forEach(function(e){t.onStart(e),t.autoUpload&&t.upload(e)})}},upload:function(e){var t=this;if(this.$refs.input.value=null,!this.beforeUpload)return this.post(e);var i=this.beforeUpload(e);i&&i.then?i.then(function(i){var n=Object.prototype.toString.call(i);if(\"[object File]\"===n||\"[object Blob]\"===n){for(var r in\"[object Blob]\"===n&&(i=new File([i],e.name,{type:e.type})),e)e.hasOwnProperty(r)&&(i[r]=e[r]);t.post(i)}else t.post(e)},function(){t.onRemove(null,e)}):!1!==i?this.post(e):this.onRemove(null,e)},abort:function(e){var t=this.reqs;if(e){var i=e;e.uid&&(i=e.uid),t[i]&&t[i].abort()}else Object.keys(t).forEach(function(e){t[e]&&t[e].abort(),delete t[e]})},post:function(e){var t=this,i=e.uid,n={headers:this.headers,withCredentials:this.withCredentials,file:e,data:this.data,filename:this.name,action:this.action,onProgress:function(i){t.onProgress(i,e)},onSuccess:function(n){t.onSuccess(n,e),delete t.reqs[i]},onError:function(n){t.onError(n,e),delete t.reqs[i]}},r=this.httpRequest(n);this.reqs[i]=r,r&&r.then&&r.then(n.onSuccess,n.onError)},handleClick:function(){this.disabled||(this.$refs.input.value=null,this.$refs.input.click())},handleKeydown:function(e){e.target===e.currentTarget&&(13!==e.keyCode&&32!==e.keyCode||this.handleClick())}},render:function(e){var t=this.handleClick,i=this.drag,n=this.name,r=this.handleChange,s=this.multiple,a=this.accept,o=this.listType,l=this.uploadFiles,u=this.disabled,c={class:{\"el-upload\":!0},on:{click:t,keydown:this.handleKeydown}};return c.class[\"el-upload--\"+o]=!0,e(\"div\",Bl()([c,{attrs:{tabindex:\"0\"}}]),[i?e(\"upload-dragger\",{attrs:{disabled:u},on:{file:l}},[this.$slots.default]):this.$slots.default,e(\"input\",{class:\"el-upload__input\",attrs:{type:\"file\",name:n,multiple:s,accept:a},ref:\"input\",on:{change:r}})])}},void 0,void 0,!1,null,null,null);Rl.options.__file=\"packages/upload/src/upload.vue\";var Wl=Rl.exports;function jl(){}var ql=r({name:\"ElUpload\",mixins:[K],components:{ElProgress:Al,UploadList:Ll,Upload:Wl},provide:function(){return{uploader:this}},inject:{elForm:{default:\"\"}},props:{action:{type:String,required:!0},headers:{type:Object,default:function(){return{}}},data:Object,multiple:Boolean,name:{type:String,default:\"file\"},drag:Boolean,dragger:Boolean,withCredentials:Boolean,showFileList:{type:Boolean,default:!0},accept:String,type:{type:String,default:\"select\"},beforeUpload:Function,beforeRemove:Function,onRemove:{type:Function,default:jl},onChange:{type:Function,default:jl},onPreview:{type:Function},onSuccess:{type:Function,default:jl},onProgress:{type:Function,default:jl},onError:{type:Function,default:jl},fileList:{type:Array,default:function(){return[]}},autoUpload:{type:Boolean,default:!0},listType:{type:String,default:\"text\"},httpRequest:Function,disabled:Boolean,limit:Number,onExceed:{type:Function,default:jl}},data:function(){return{uploadFiles:[],dragOver:!1,draging:!1,tempIndex:1}},computed:{uploadDisabled:function(){return this.disabled||(this.elForm||{}).disabled}},watch:{listType:function(e){\"picture-card\"!==e&&\"picture\"!==e||(this.uploadFiles=this.uploadFiles.map(function(e){if(!e.url&&e.raw)try{e.url=URL.createObjectURL(e.raw)}catch(e){console.error(\"[Element Error][Upload]\",e)}return e}))},fileList:{immediate:!0,handler:function(e){var t=this;this.uploadFiles=e.map(function(e){return e.uid=e.uid||Date.now()+t.tempIndex++,e.status=e.status||\"success\",e})}}},methods:{handleStart:function(e){e.uid=Date.now()+this.tempIndex++;var t={status:\"ready\",name:e.name,size:e.size,percentage:0,uid:e.uid,raw:e};if(\"picture-card\"===this.listType||\"picture\"===this.listType)try{t.url=URL.createObjectURL(e)}catch(e){return void console.error(\"[Element Error][Upload]\",e)}this.uploadFiles.push(t),this.onChange(t,this.uploadFiles)},handleProgress:function(e,t){var i=this.getFile(t);this.onProgress(e,i,this.uploadFiles),i.status=\"uploading\",i.percentage=e.percent||0},handleSuccess:function(e,t){var i=this.getFile(t);i&&(i.status=\"success\",i.response=e,this.onSuccess(e,i,this.uploadFiles),this.onChange(i,this.uploadFiles))},handleError:function(e,t){var i=this.getFile(t),n=this.uploadFiles;i.status=\"fail\",n.splice(n.indexOf(i),1),this.onError(e,i,this.uploadFiles),this.onChange(i,this.uploadFiles)},handleRemove:function(e,t){var i=this;t&&(e=this.getFile(t));var n=function(){i.abort(e);var t=i.uploadFiles;t.splice(t.indexOf(e),1),i.onRemove(e,t)};if(this.beforeRemove){if(\"function\"==typeof this.beforeRemove){var r=this.beforeRemove(e,this.uploadFiles);r&&r.then?r.then(function(){n()},jl):!1!==r&&n()}}else n()},getFile:function(e){var t=this.uploadFiles,i=void 0;return t.every(function(t){return!(i=e.uid===t.uid?t:null)}),i},abort:function(e){this.$refs[\"upload-inner\"].abort(e)},clearFiles:function(){this.uploadFiles=[]},submit:function(){var e=this;this.uploadFiles.filter(function(e){return\"ready\"===e.status}).forEach(function(t){e.$refs[\"upload-inner\"].upload(t.raw)})},getMigratingConfig:function(){return{props:{\"default-file-list\":\"default-file-list is renamed to file-list.\",\"show-upload-list\":\"show-upload-list is renamed to show-file-list.\",\"thumbnail-mode\":\"thumbnail-mode has been deprecated, you can implement the same effect according to this case: http://element.eleme.io/#/zh-CN/component/upload#yong-hu-tou-xiang-shang-chuan\"}}}},beforeDestroy:function(){this.uploadFiles.forEach(function(e){e.url&&0===e.url.indexOf(\"blob:\")&&URL.revokeObjectURL(e.url)})},render:function(e){var t=this,i=void 0;this.showFileList&&(i=e(Ll,{attrs:{disabled:this.uploadDisabled,listType:this.listType,files:this.uploadFiles,handlePreview:this.onPreview},on:{remove:this.handleRemove}},[function(e){if(t.$scopedSlots.file)return t.$scopedSlots.file({file:e.file})}]));var n=e(\"upload\",{props:{type:this.type,drag:this.drag,action:this.action,multiple:this.multiple,\"before-upload\":this.beforeUpload,\"with-credentials\":this.withCredentials,headers:this.headers,name:this.name,data:this.data,accept:this.accept,fileList:this.uploadFiles,autoUpload:this.autoUpload,listType:this.listType,disabled:this.uploadDisabled,limit:this.limit,\"on-exceed\":this.onExceed,\"on-start\":this.handleStart,\"on-progress\":this.handleProgress,\"on-success\":this.handleSuccess,\"on-error\":this.handleError,\"on-preview\":this.onPreview,\"on-remove\":this.handleRemove,\"http-request\":this.httpRequest},ref:\"upload-inner\"},[this.$slots.trigger||this.$slots.default]);return e(\"div\",[\"picture-card\"===this.listType?i:\"\",this.$slots.trigger?[n,this.$slots.default]:n,this.$slots.tip,\"picture-card\"!==this.listType?i:\"\"])}},void 0,void 0,!1,null,null,null);ql.options.__file=\"packages/upload/src/index.vue\";var Yl=ql.exports;Yl.install=function(e){e.component(Yl.name,Yl)};var Kl=Yl,Gl=function(){var e=this.$createElement,t=this._self._c||e;return t(\"span\",{staticClass:\"el-spinner\"},[t(\"svg\",{staticClass:\"el-spinner-inner\",style:{width:this.radius/2+\"px\",height:this.radius/2+\"px\"},attrs:{viewBox:\"0 0 50 50\"}},[t(\"circle\",{staticClass:\"path\",attrs:{cx:\"25\",cy:\"25\",r:\"20\",fill:\"none\",stroke:this.strokeColor,\"stroke-width\":this.strokeWidth}})])])};Gl._withStripped=!0;var Ul=r({name:\"ElSpinner\",props:{type:String,radius:{type:Number,default:100},strokeWidth:{type:Number,default:5},strokeColor:{type:String,default:\"#efefef\"}}},Gl,[],!1,null,null,null);Ul.options.__file=\"packages/spinner/src/spinner.vue\";var Xl=Ul.exports;Xl.install=function(e){e.component(Xl.name,Xl)};var Jl=Xl,Zl=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition\",{attrs:{name:\"el-message-fade\"},on:{\"after-leave\":e.handleAfterLeave}},[i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.visible,expression:\"visible\"}],class:[\"el-message\",e.type&&!e.iconClass?\"el-message--\"+e.type:\"\",e.center?\"is-center\":\"\",e.showClose?\"is-closable\":\"\",e.customClass],style:e.positionStyle,attrs:{role:\"alert\"},on:{mouseenter:e.clearTimer,mouseleave:e.startTimer}},[e.iconClass?i(\"i\",{class:e.iconClass}):i(\"i\",{class:e.typeClass}),e._t(\"default\",[e.dangerouslyUseHTMLString?i(\"p\",{staticClass:\"el-message__content\",domProps:{innerHTML:e._s(e.message)}}):i(\"p\",{staticClass:\"el-message__content\"},[e._v(e._s(e.message))])]),e.showClose?i(\"i\",{staticClass:\"el-message__closeBtn el-icon-close\",on:{click:e.close}}):e._e()],2)])};Zl._withStripped=!0;var Ql={success:\"success\",info:\"info\",warning:\"warning\",error:\"error\"},eu=r({data:function(){return{visible:!1,message:\"\",duration:3e3,type:\"info\",iconClass:\"\",customClass:\"\",onClose:null,showClose:!1,closed:!1,verticalOffset:20,timer:null,dangerouslyUseHTMLString:!1,center:!1}},computed:{typeClass:function(){return this.type&&!this.iconClass?\"el-message__icon el-icon-\"+Ql[this.type]:\"\"},positionStyle:function(){return{top:this.verticalOffset+\"px\"}}},watch:{closed:function(e){e&&(this.visible=!1)}},methods:{handleAfterLeave:function(){this.$destroy(!0),this.$el.parentNode.removeChild(this.$el)},close:function(){this.closed=!0,\"function\"==typeof this.onClose&&this.onClose(this)},clearTimer:function(){clearTimeout(this.timer)},startTimer:function(){var e=this;this.duration>0&&(this.timer=setTimeout(function(){e.closed||e.close()},this.duration))},keydown:function(e){27===e.keyCode&&(this.closed||this.close())}},mounted:function(){this.startTimer(),document.addEventListener(\"keydown\",this.keydown)},beforeDestroy:function(){document.removeEventListener(\"keydown\",this.keydown)}},Zl,[],!1,null,null,null);eu.options.__file=\"packages/message/src/main.vue\";var tu=eu.exports,iu=h.a.extend(tu),nu=void 0,ru=[],su=1,au=function e(t){if(!h.a.prototype.$isServer){\"string\"==typeof(t=t||{})&&(t={message:t});var i=t.onClose,n=\"message_\"+su++;t.onClose=function(){e.close(n,i)},(nu=new iu({data:t})).id=n,ua(nu.message)&&(nu.$slots.default=[nu.message],nu.message=null),nu.$mount(),document.body.appendChild(nu.$el);var r=t.offset||20;return ru.forEach(function(e){r+=e.$el.offsetHeight+16}),nu.verticalOffset=r,nu.visible=!0,nu.$el.style.zIndex=Se.nextZIndex(),ru.push(nu),nu}};[\"success\",\"warning\",\"info\",\"error\"].forEach(function(e){au[e]=function(t){return\"string\"==typeof t&&(t={message:t}),t.type=e,au(t)}}),au.close=function(e,t){for(var i=ru.length,n=-1,r=void 0,s=0;sru.length-1))for(var a=n;a=0;e--)ru[e].close()};var ou=au,lu=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-badge\"},[e._t(\"default\"),i(\"transition\",{attrs:{name:\"el-zoom-in-center\"}},[i(\"sup\",{directives:[{name:\"show\",rawName:\"v-show\",value:!e.hidden&&(e.content||0===e.content||e.isDot),expression:\"!hidden && (content || content === 0 || isDot)\"}],staticClass:\"el-badge__content\",class:[\"el-badge__content--\"+e.type,{\"is-fixed\":e.$slots.default,\"is-dot\":e.isDot}],domProps:{textContent:e._s(e.content)}})])],2)};lu._withStripped=!0;var uu=r({name:\"ElBadge\",props:{value:[String,Number],max:Number,isDot:Boolean,hidden:Boolean,type:{type:String,validator:function(e){return[\"primary\",\"success\",\"warning\",\"info\",\"danger\"].indexOf(e)>-1}}},computed:{content:function(){if(!this.isDot){var e=this.value,t=this.max;return\"number\"==typeof e&&\"number\"==typeof t&&t0&&e-1this.value,i=this.allowHalf&&this.pointerAtLeftHalf&&e-.5<=this.currentValue&&e>this.currentValue;return t||i},getIconStyle:function(e){var t=this.rateDisabled?this.disabledVoidColor:this.voidColor;return{color:e<=this.currentValue?this.activeColor:t}},selectValue:function(e){this.rateDisabled||(this.allowHalf&&this.pointerAtLeftHalf?(this.$emit(\"input\",this.currentValue),this.$emit(\"change\",this.currentValue)):(this.$emit(\"input\",e),this.$emit(\"change\",e)))},handleKey:function(e){if(!this.rateDisabled){var t=this.currentValue,i=e.keyCode;38===i||39===i?(this.allowHalf?t+=.5:t+=1,e.stopPropagation(),e.preventDefault()):37!==i&&40!==i||(this.allowHalf?t-=.5:t-=1,e.stopPropagation(),e.preventDefault()),t=(t=t<0?0:t)>this.max?this.max:t,this.$emit(\"input\",t),this.$emit(\"change\",t)}},setCurrentValue:function(e,t){if(!this.rateDisabled){if(this.allowHalf){var i=t.target;pe(i,\"el-rate__item\")&&(i=i.querySelector(\".el-rate__icon\")),pe(i,\"el-rate__decimal\")&&(i=i.parentNode),this.pointerAtLeftHalf=2*t.offsetX<=i.clientWidth,this.currentValue=this.pointerAtLeftHalf?e-.5:e}else this.currentValue=e;this.hoverIndex=e}},resetCurrentValue:function(){this.rateDisabled||(this.allowHalf&&(this.pointerAtLeftHalf=this.value!==Math.floor(this.value)),this.currentValue=this.value,this.hoverIndex=-1)}},created:function(){this.value||this.$emit(\"input\",0)}},vu,[],!1,null,null,null);gu.options.__file=\"packages/rate/src/main.vue\";var bu=gu.exports;bu.install=function(e){e.component(bu.name,bu)};var yu=bu,wu=function(){var e=this.$createElement;return(this._self._c||e)(\"div\",{staticClass:\"el-steps\",class:[!this.simple&&\"el-steps--\"+this.direction,this.simple&&\"el-steps--simple\"]},[this._t(\"default\")],2)};wu._withStripped=!0;var _u=r({name:\"ElSteps\",mixins:[K],props:{space:[Number,String],active:Number,direction:{type:String,default:\"horizontal\"},alignCenter:Boolean,simple:Boolean,finishStatus:{type:String,default:\"finish\"},processStatus:{type:String,default:\"process\"}},data:function(){return{steps:[],stepOffset:0}},methods:{getMigratingConfig:function(){return{props:{center:\"center is removed.\"}}}},watch:{active:function(e,t){this.$emit(\"change\",e,t)},steps:function(e){e.forEach(function(e,t){e.index=t})}}},wu,[],!1,null,null,null);_u.options.__file=\"packages/steps/src/steps.vue\";var xu=_u.exports;xu.install=function(e){e.component(xu.name,xu)};var Cu=xu,ku=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-step\",class:[!e.isSimple&&\"is-\"+e.$parent.direction,e.isSimple&&\"is-simple\",e.isLast&&!e.space&&!e.isCenter&&\"is-flex\",e.isCenter&&!e.isVertical&&!e.isSimple&&\"is-center\"],style:e.style},[i(\"div\",{staticClass:\"el-step__head\",class:\"is-\"+e.currentStatus},[i(\"div\",{staticClass:\"el-step__line\",style:e.isLast?\"\":{marginRight:e.$parent.stepOffset+\"px\"}},[i(\"i\",{staticClass:\"el-step__line-inner\",style:e.lineStyle})]),i(\"div\",{staticClass:\"el-step__icon\",class:\"is-\"+(e.icon?\"icon\":\"text\")},[\"success\"!==e.currentStatus&&\"error\"!==e.currentStatus?e._t(\"icon\",[e.icon?i(\"i\",{staticClass:\"el-step__icon-inner\",class:[e.icon]}):e._e(),e.icon||e.isSimple?e._e():i(\"div\",{staticClass:\"el-step__icon-inner\"},[e._v(e._s(e.index+1))])]):i(\"i\",{staticClass:\"el-step__icon-inner is-status\",class:[\"el-icon-\"+(\"success\"===e.currentStatus?\"check\":\"close\")]})],2)]),i(\"div\",{staticClass:\"el-step__main\"},[i(\"div\",{ref:\"title\",staticClass:\"el-step__title\",class:[\"is-\"+e.currentStatus]},[e._t(\"title\",[e._v(e._s(e.title))])],2),e.isSimple?i(\"div\",{staticClass:\"el-step__arrow\"}):i(\"div\",{staticClass:\"el-step__description\",class:[\"is-\"+e.currentStatus]},[e._t(\"description\",[e._v(e._s(e.description))])],2)])])};ku._withStripped=!0;var Su=r({name:\"ElStep\",props:{title:String,icon:String,description:String,status:String},data:function(){return{index:-1,lineStyle:{},internalStatus:\"\"}},beforeCreate:function(){this.$parent.steps.push(this)},beforeDestroy:function(){var e=this.$parent.steps,t=e.indexOf(this);t>=0&&e.splice(t,1)},computed:{currentStatus:function(){return this.status||this.internalStatus},prevStatus:function(){var e=this.$parent.steps[this.index-1];return e?e.currentStatus:\"wait\"},isCenter:function(){return this.$parent.alignCenter},isVertical:function(){return\"vertical\"===this.$parent.direction},isSimple:function(){return this.$parent.simple},isLast:function(){var e=this.$parent;return e.steps[e.steps.length-1]===this},stepsCount:function(){return this.$parent.steps.length},space:function(){var e=this.isSimple,t=this.$parent.space;return e?\"\":t},style:function(){var e={},t=this.$parent.steps.length,i=\"number\"==typeof this.space?this.space+\"px\":this.space?this.space:100/(t-(this.isCenter?0:1))+\"%\";return e.flexBasis=i,this.isVertical?e:(this.isLast?e.maxWidth=100/this.stepsCount+\"%\":e.marginRight=-this.$parent.stepOffset+\"px\",e)}},methods:{updateStatus:function(e){var t=this.$parent.$children[this.index-1];e>this.index?this.internalStatus=this.$parent.finishStatus:e===this.index&&\"error\"!==this.prevStatus?this.internalStatus=this.$parent.processStatus:this.internalStatus=\"wait\",t&&t.calcProgress(this.internalStatus)},calcProgress:function(e){var t=100,i={};i.transitionDelay=150*this.index+\"ms\",e===this.$parent.processStatus?(this.currentStatus,t=0):\"wait\"===e&&(t=0,i.transitionDelay=-150*this.index+\"ms\"),i.borderWidth=t&&!this.isSimple?\"1px\":0,\"vertical\"===this.$parent.direction?i.height=t+\"%\":i.width=t+\"%\",this.lineStyle=i}},mounted:function(){var e=this,t=this.$watch(\"index\",function(i){e.$watch(\"$parent.active\",e.updateStatus,{immediate:!0}),e.$watch(\"$parent.processStatus\",function(){var t=e.$parent.active;e.updateStatus(t)},{immediate:!0}),t()})}},ku,[],!1,null,null,null);Su.options.__file=\"packages/steps/src/step.vue\";var Du=Su.exports;Du.install=function(e){e.component(Du.name,Du)};var $u=Du,Eu=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{class:e.carouselClasses,on:{mouseenter:function(t){return t.stopPropagation(),e.handleMouseEnter(t)},mouseleave:function(t){return t.stopPropagation(),e.handleMouseLeave(t)}}},[i(\"div\",{staticClass:\"el-carousel__container\",style:{height:e.height}},[e.arrowDisplay?i(\"transition\",{attrs:{name:\"carousel-arrow-left\"}},[i(\"button\",{directives:[{name:\"show\",rawName:\"v-show\",value:(\"always\"===e.arrow||e.hover)&&(e.loop||e.activeIndex>0),expression:\"(arrow === 'always' || hover) && (loop || activeIndex > 0)\"}],staticClass:\"el-carousel__arrow el-carousel__arrow--left\",attrs:{type:\"button\"},on:{mouseenter:function(t){e.handleButtonEnter(\"left\")},mouseleave:e.handleButtonLeave,click:function(t){t.stopPropagation(),e.throttledArrowClick(e.activeIndex-1)}}},[i(\"i\",{staticClass:\"el-icon-arrow-left\"})])]):e._e(),e.arrowDisplay?i(\"transition\",{attrs:{name:\"carousel-arrow-right\"}},[i(\"button\",{directives:[{name:\"show\",rawName:\"v-show\",value:(\"always\"===e.arrow||e.hover)&&(e.loop||e.activeIndex0})},carouselClasses:function(){var e=[\"el-carousel\",\"el-carousel--\"+this.direction];return\"card\"===this.type&&e.push(\"el-carousel--card\"),e},indicatorsClasses:function(){var e=[\"el-carousel__indicators\",\"el-carousel__indicators--\"+this.direction];return this.hasLabel&&e.push(\"el-carousel__indicators--labels\"),\"outside\"!==this.indicatorPosition&&\"card\"!==this.type||e.push(\"el-carousel__indicators--outside\"),e}},watch:{items:function(e){e.length>0&&this.setActiveItem(this.initialIndex)},activeIndex:function(e,t){this.resetItemPosition(t),t>-1&&this.$emit(\"change\",e,t)},autoplay:function(e){e?this.startTimer():this.pauseTimer()},loop:function(){this.setActiveItem(this.activeIndex)}},methods:{handleMouseEnter:function(){this.hover=!0,this.pauseTimer()},handleMouseLeave:function(){this.hover=!1,this.startTimer()},itemInStage:function(e,t){var i=this.items.length;return t===i-1&&e.inStage&&this.items[0].active||e.inStage&&this.items[t+1]&&this.items[t+1].active?\"left\":!!(0===t&&e.inStage&&this.items[i-1].active||e.inStage&&this.items[t-1]&&this.items[t-1].active)&&\"right\"},handleButtonEnter:function(e){var t=this;\"vertical\"!==this.direction&&this.items.forEach(function(i,n){e===t.itemInStage(i,n)&&(i.hover=!0)})},handleButtonLeave:function(){\"vertical\"!==this.direction&&this.items.forEach(function(e){e.hover=!1})},updateItems:function(){this.items=this.$children.filter(function(e){return\"ElCarouselItem\"===e.$options.name})},resetItemPosition:function(e){var t=this;this.items.forEach(function(i,n){i.translateItem(n,t.activeIndex,e)})},playSlides:function(){this.activeIndex0&&(e=this.items.indexOf(t[0]))}if(e=Number(e),isNaN(e)||e!==Math.floor(e))console.warn(\"[Element Warn][Carousel]index must be an integer.\");else{var i=this.items.length,n=this.activeIndex;this.activeIndex=e<0?this.loop?i-1:0:e>=i?this.loop?0:i-1:e,n===this.activeIndex&&this.resetItemPosition(n)}},prev:function(){this.setActiveItem(this.activeIndex-1)},next:function(){this.setActiveItem(this.activeIndex+1)},handleIndicatorClick:function(e){this.activeIndex=e},handleIndicatorHover:function(e){\"hover\"===this.trigger&&e!==this.activeIndex&&(this.activeIndex=e)}},created:function(){var e=this;this.throttledArrowClick=Mu()(300,!0,function(t){e.setActiveItem(t)}),this.throttledIndicatorHover=Mu()(300,function(t){e.handleIndicatorHover(t)})},mounted:function(){var e=this;this.updateItems(),this.$nextTick(function(){Ye(e.$el,e.resetItemPosition),e.initialIndex=0&&(e.activeIndex=e.initialIndex),e.startTimer()})},beforeDestroy:function(){this.$el&&Ke(this.$el,this.resetItemPosition),this.pauseTimer()}},Eu,[],!1,null,null,null);Nu.options.__file=\"packages/carousel/src/main.vue\";var Pu=Nu.exports;Pu.install=function(e){e.component(Pu.name,Pu)};var Ou=Pu,Iu=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.ready,expression:\"ready\"}],staticClass:\"el-carousel__item\",class:{\"is-active\":e.active,\"el-carousel__item--card\":\"card\"===e.$parent.type,\"is-in-stage\":e.inStage,\"is-hover\":e.hover,\"is-animating\":e.animating},style:e.itemStyle,on:{click:e.handleItemClick}},[\"card\"===e.$parent.type?i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:!e.active,expression:\"!active\"}],staticClass:\"el-carousel__mask\"}):e._e(),e._t(\"default\")],2)};Iu._withStripped=!0;var Au=r({name:\"ElCarouselItem\",props:{name:String,label:{type:[String,Number],default:\"\"}},data:function(){return{hover:!1,translate:0,scale:1,active:!1,ready:!1,inStage:!1,animating:!1}},methods:{processIndex:function(e,t,i){return 0===t&&e===i-1?-1:t===i-1&&0===e?i:e=i/2?i+1:e>t+1&&e-t>=i/2?-2:e},calcCardTranslate:function(e,t){var i=this.$parent.$el.offsetWidth;return this.inStage?i*(1.17*(e-t)+1)/4:e2&&this.$parent.loop&&(e=this.processIndex(e,t,s)),\"card\"===n)\"vertical\"===r&&console.warn(\"[Element Warn][Carousel]vertical direction is not supported in card mode\"),this.inStage=Math.round(Math.abs(e-t))<=1,this.active=e===t,this.translate=this.calcCardTranslate(e,t),this.scale=this.active?1:.83;else{this.active=e===t;var a=\"vertical\"===r;this.translate=this.calcTranslate(e,t,a)}this.ready=!0},handleItemClick:function(){var e=this.$parent;if(e&&\"card\"===e.type){var t=e.items.indexOf(this);e.setActiveItem(t)}}},computed:{parentDirection:function(){return this.$parent.direction},itemStyle:function(){return function(e){if(\"object\"!==(void 0===e?\"undefined\":y(e)))return e;var t=[\"ms-\",\"webkit-\"];return[\"transform\",\"transition\",\"animation\"].forEach(function(i){var n=e[i];i&&n&&t.forEach(function(t){e[t+i]=n})}),e}({transform:(\"vertical\"===this.parentDirection?\"translateY\":\"translateX\")+\"(\"+this.translate+\"px) scale(\"+this.scale+\")\"})}},created:function(){this.$parent&&this.$parent.updateItems()},destroyed:function(){this.$parent&&this.$parent.updateItems()}},Iu,[],!1,null,null,null);Au.options.__file=\"packages/carousel/src/item.vue\";var Fu=Au.exports;Fu.install=function(e){e.component(Fu.name,Fu)};var Lu=Fu,Vu=function(){var e=this.$createElement;return(this._self._c||e)(\"div\",{staticClass:\"el-collapse\",attrs:{role:\"tablist\",\"aria-multiselectable\":\"true\"}},[this._t(\"default\")],2)};Vu._withStripped=!0;var Bu=r({name:\"ElCollapse\",componentName:\"ElCollapse\",props:{accordion:Boolean,value:{type:[Array,String,Number],default:function(){return[]}}},data:function(){return{activeNames:[].concat(this.value)}},provide:function(){return{collapse:this}},watch:{value:function(e){this.activeNames=[].concat(e)}},methods:{setActiveNames:function(e){e=[].concat(e);var t=this.accordion?e[0]:e;this.activeNames=e,this.$emit(\"input\",t),this.$emit(\"change\",t)},handleItemClick:function(e){if(this.accordion)this.setActiveNames(!this.activeNames[0]&&0!==this.activeNames[0]||this.activeNames[0]!==e.name?e.name:\"\");else{var t=this.activeNames.slice(0),i=t.indexOf(e.name);i>-1?t.splice(i,1):t.push(e.name),this.setActiveNames(t)}}},created:function(){this.$on(\"item-click\",this.handleItemClick)}},Vu,[],!1,null,null,null);Bu.options.__file=\"packages/collapse/src/collapse.vue\";var zu=Bu.exports;zu.install=function(e){e.component(zu.name,zu)};var Hu=zu,Ru=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-collapse-item\",class:{\"is-active\":e.isActive,\"is-disabled\":e.disabled}},[i(\"div\",{attrs:{role:\"tab\",\"aria-expanded\":e.isActive,\"aria-controls\":\"el-collapse-content-\"+e.id,\"aria-describedby\":\"el-collapse-content-\"+e.id}},[i(\"div\",{staticClass:\"el-collapse-item__header\",class:{focusing:e.focusing,\"is-active\":e.isActive},attrs:{role:\"button\",id:\"el-collapse-head-\"+e.id,tabindex:e.disabled?void 0:0},on:{click:e.handleHeaderClick,keyup:function(t){return\"button\"in t||!e._k(t.keyCode,\"space\",32,t.key,[\" \",\"Spacebar\"])||!e._k(t.keyCode,\"enter\",13,t.key,\"Enter\")?(t.stopPropagation(),e.handleEnterClick(t)):null},focus:e.handleFocus,blur:function(t){e.focusing=!1}}},[e._t(\"title\",[e._v(e._s(e.title))]),i(\"i\",{staticClass:\"el-collapse-item__arrow el-icon-arrow-right\",class:{\"is-active\":e.isActive}})],2)]),i(\"el-collapse-transition\",[i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.isActive,expression:\"isActive\"}],staticClass:\"el-collapse-item__wrap\",attrs:{role:\"tabpanel\",\"aria-hidden\":!e.isActive,\"aria-labelledby\":\"el-collapse-head-\"+e.id,id:\"el-collapse-content-\"+e.id}},[i(\"div\",{staticClass:\"el-collapse-item__content\"},[e._t(\"default\")],2)])])],1)};Ru._withStripped=!0;var Wu=r({name:\"ElCollapseItem\",componentName:\"ElCollapseItem\",mixins:[l],components:{ElCollapseTransition:ii},data:function(){return{contentWrapStyle:{height:\"auto\",display:\"block\"},contentHeight:0,focusing:!1,isClick:!1,id:D()}},inject:[\"collapse\"],props:{title:String,name:{type:[String,Number],default:function(){return this._uid}},disabled:Boolean},computed:{isActive:function(){return this.collapse.activeNames.indexOf(this.name)>-1}},methods:{handleFocus:function(){var e=this;setTimeout(function(){e.isClick?e.isClick=!1:e.focusing=!0},50)},handleHeaderClick:function(){this.disabled||(this.dispatch(\"ElCollapse\",\"item-click\",this),this.focusing=!1,this.isClick=!0)},handleEnterClick:function(){this.dispatch(\"ElCollapse\",\"item-click\",this)}}},Ru,[],!1,null,null,null);Wu.options.__file=\"packages/collapse/src/collapse-item.vue\";var ju=Wu.exports;ju.install=function(e){e.component(ju.name,ju)};var qu=ju,Yu=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{directives:[{name:\"clickoutside\",rawName:\"v-clickoutside\",value:function(){return e.toggleDropDownVisible(!1)},expression:\"() => toggleDropDownVisible(false)\"}],ref:\"reference\",class:[\"el-cascader\",e.realSize&&\"el-cascader--\"+e.realSize,{\"is-disabled\":e.isDisabled}],on:{mouseenter:function(t){e.inputHover=!0},mouseleave:function(t){e.inputHover=!1},click:function(){return e.toggleDropDownVisible(!e.readonly||void 0)},keydown:e.handleKeyDown}},[i(\"el-input\",{ref:\"input\",class:{\"is-focus\":e.dropDownVisible},attrs:{size:e.realSize,placeholder:e.placeholder,readonly:e.readonly,disabled:e.isDisabled,\"validate-event\":!1},on:{focus:e.handleFocus,blur:e.handleBlur,input:e.handleInput},model:{value:e.multiple?e.presentText:e.inputValue,callback:function(t){e.multiple?e.presentText:e.inputValue=t},expression:\"multiple ? presentText : inputValue\"}},[i(\"template\",{slot:\"suffix\"},[e.clearBtnVisible?i(\"i\",{key:\"clear\",staticClass:\"el-input__icon el-icon-circle-close\",on:{click:function(t){return t.stopPropagation(),e.handleClear(t)}}}):i(\"i\",{key:\"arrow-down\",class:[\"el-input__icon\",\"el-icon-arrow-down\",e.dropDownVisible&&\"is-reverse\"],on:{click:function(t){t.stopPropagation(),e.toggleDropDownVisible()}}})])],2),e.multiple?i(\"div\",{staticClass:\"el-cascader__tags\"},[e._l(e.presentTags,function(t,n){return i(\"el-tag\",{key:t.key,attrs:{type:\"info\",size:e.tagSize,hit:t.hitState,closable:t.closable,\"disable-transitions\":\"\"},on:{close:function(t){e.deleteTag(n)}}},[i(\"span\",[e._v(e._s(t.text))])])}),e.filterable&&!e.isDisabled?i(\"input\",{directives:[{name:\"model\",rawName:\"v-model.trim\",value:e.inputValue,expression:\"inputValue\",modifiers:{trim:!0}}],staticClass:\"el-cascader__search-input\",attrs:{type:\"text\",placeholder:e.presentTags.length?\"\":e.placeholder},domProps:{value:e.inputValue},on:{input:[function(t){t.target.composing||(e.inputValue=t.target.value.trim())},function(t){return e.handleInput(e.inputValue,t)}],click:function(t){t.stopPropagation(),e.toggleDropDownVisible(!0)},keydown:function(t){return\"button\"in t||!e._k(t.keyCode,\"delete\",[8,46],t.key,[\"Backspace\",\"Delete\",\"Del\"])?e.handleDelete(t):null},blur:function(t){e.$forceUpdate()}}}):e._e()],2):e._e(),i(\"transition\",{attrs:{name:\"el-zoom-in-top\"},on:{\"after-leave\":e.handleDropdownLeave}},[i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.dropDownVisible,expression:\"dropDownVisible\"}],ref:\"popper\",class:[\"el-popper\",\"el-cascader__dropdown\",e.popperClass]},[i(\"el-cascader-panel\",{directives:[{name:\"show\",rawName:\"v-show\",value:!e.filtering,expression:\"!filtering\"}],ref:\"panel\",attrs:{options:e.options,props:e.config,border:!1,\"render-label\":e.$scopedSlots.default},on:{\"expand-change\":e.handleExpandChange,close:function(t){e.toggleDropDownVisible(!1)}},model:{value:e.checkedValue,callback:function(t){e.checkedValue=t},expression:\"checkedValue\"}}),e.filterable?i(\"el-scrollbar\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.filtering,expression:\"filtering\"}],ref:\"suggestionPanel\",staticClass:\"el-cascader__suggestion-panel\",attrs:{tag:\"ul\",\"view-class\":\"el-cascader__suggestion-list\"},nativeOn:{keydown:function(t){return e.handleSuggestionKeyDown(t)}}},[e.suggestions.length?e._l(e.suggestions,function(t,n){return i(\"li\",{key:t.uid,class:[\"el-cascader__suggestion-item\",t.checked&&\"is-checked\"],attrs:{tabindex:-1},on:{click:function(t){e.handleSuggestionClick(n)}}},[i(\"span\",[e._v(e._s(t.text))]),t.checked?i(\"i\",{staticClass:\"el-icon-check\"}):e._e()])}):e._t(\"empty\",[i(\"li\",{staticClass:\"el-cascader__empty-text\"},[e._v(e._s(e.t(\"el.cascader.noMatch\")))])])],2):e._e()],1)])],1)};Yu._withStripped=!0;var Ku=function(){var e=this.$createElement,t=this._self._c||e;return t(\"div\",{class:[\"el-cascader-panel\",this.border&&\"is-bordered\"],on:{keydown:this.handleKeyDown}},this._l(this.menus,function(e,i){return t(\"cascader-menu\",{key:i,ref:\"menu\",refInFor:!0,attrs:{index:i,nodes:e}})}),1)};Ku._withStripped=!0;var Gu=function(e){return e.stopPropagation()},Uu=r({inject:[\"panel\"],components:{ElCheckbox:Vi,ElRadio:Si},props:{node:{required:!0},nodeId:String},computed:{config:function(){return this.panel.config},isLeaf:function(){return this.node.isLeaf},isDisabled:function(){return this.node.isDisabled},checkedValue:function(){return this.panel.checkedValue},isChecked:function(){return this.node.isSameNode(this.checkedValue)},inActivePath:function(){return this.isInPath(this.panel.activePath)},inCheckedPath:function(){var e=this;return!!this.config.checkStrictly&&this.panel.checkedNodePaths.some(function(t){return e.isInPath(t)})},value:function(){return this.node.getValueByOption()}},methods:{handleExpand:function(){var e=this,t=this.panel,i=this.node,n=this.isDisabled,r=this.config,s=r.multiple;!r.checkStrictly&&n||i.loading||(r.lazy&&!i.loaded?t.lazyLoad(i,function(){var t=e.isLeaf;if(t||e.handleExpand(),s){var n=!!t&&i.checked;e.handleMultiCheckChange(n)}}):t.handleExpand(i))},handleCheckChange:function(){var e=this.panel,t=this.value,i=this.node;e.handleCheckChange(t),e.handleExpand(i)},handleMultiCheckChange:function(e){this.node.doCheck(e),this.panel.calculateMultiCheckedValue()},isInPath:function(e){var t=this.node;return(e[t.level-1]||{}).uid===t.uid},renderPrefix:function(e){var t=this.isLeaf,i=this.isChecked,n=this.config,r=n.checkStrictly;return n.multiple?this.renderCheckbox(e):r?this.renderRadio(e):t&&i?this.renderCheckIcon(e):null},renderPostfix:function(e){var t=this.node,i=this.isLeaf;return t.loading?this.renderLoadingIcon(e):i?null:this.renderExpandIcon(e)},renderCheckbox:function(e){var t=this.node,i=this.config,n=this.isDisabled,r={on:{change:this.handleMultiCheckChange},nativeOn:{}};return i.checkStrictly&&(r.nativeOn.click=Gu),e(\"el-checkbox\",Bl()([{attrs:{value:t.checked,indeterminate:t.indeterminate,disabled:n}},r]))},renderRadio:function(e){var t=this.checkedValue,i=this.value,n=this.isDisabled;return I(i,t)&&(i=t),e(\"el-radio\",{attrs:{value:t,label:i,disabled:n},on:{change:this.handleCheckChange},nativeOn:{click:Gu}},[e(\"span\")])},renderCheckIcon:function(e){return e(\"i\",{class:\"el-icon-check el-cascader-node__prefix\"})},renderLoadingIcon:function(e){return e(\"i\",{class:\"el-icon-loading el-cascader-node__postfix\"})},renderExpandIcon:function(e){return e(\"i\",{class:\"el-icon-arrow-right el-cascader-node__postfix\"})},renderContent:function(e){var t=this.panel,i=this.node,n=t.renderLabelFn;return e(\"span\",{class:\"el-cascader-node__label\"},[(n?n({node:i,data:i.data}):null)||i.label])}},render:function(e){var t=this,i=this.inActivePath,n=this.inCheckedPath,r=this.isChecked,s=this.isLeaf,a=this.isDisabled,o=this.config,l=this.nodeId,u=o.expandTrigger,c=o.checkStrictly,h=o.multiple,d=!c&&a,p={on:{}};return\"click\"===u?p.on.click=this.handleExpand:(p.on.mouseenter=function(e){t.handleExpand(),t.$emit(\"expand\",e)},p.on.focus=function(e){t.handleExpand(),t.$emit(\"expand\",e)}),!s||a||c||h||(p.on.click=this.handleCheckChange),e(\"li\",Bl()([{attrs:{role:\"menuitem\",id:l,\"aria-expanded\":i,tabindex:d?null:-1},class:{\"el-cascader-node\":!0,\"is-selectable\":c,\"in-active-path\":i,\"in-checked-path\":n,\"is-active\":r,\"is-disabled\":d}},p]),[this.renderPrefix(e),this.renderContent(e),this.renderPostfix(e)])}},void 0,void 0,!1,null,null,null);Uu.options.__file=\"packages/cascader-panel/src/cascader-node.vue\";var Xu=r({name:\"ElCascaderMenu\",mixins:[q],inject:[\"panel\"],components:{ElScrollbar:Ze,CascaderNode:Uu.exports},props:{nodes:{type:Array,required:!0},index:Number},data:function(){return{activeNode:null,hoverTimer:null,id:D()}},computed:{isEmpty:function(){return!this.nodes.length},menuId:function(){return\"cascader-menu-\"+this.id+\"-\"+this.index}},methods:{handleExpand:function(e){this.activeNode=e.target},handleMouseMove:function(e){var t=this.activeNode,i=this.hoverTimer,n=this.$refs.hoverZone;if(t&&n)if(t.contains(e.target)){clearTimeout(i);var r=this.$el.getBoundingClientRect().left,s=e.clientX-r,a=this.$el,o=a.offsetWidth,l=a.offsetHeight,u=t.offsetTop,c=u+t.offsetHeight;n.innerHTML='\\n \\n \\n '}else i||(this.hoverTimer=setTimeout(this.clearHoverZone,this.panel.config.hoverThreshold))},clearHoverZone:function(){var e=this.$refs.hoverZone;e&&(e.innerHTML=\"\")},renderEmptyText:function(e){return e(\"div\",{class:\"el-cascader-menu__empty-text\"},[this.t(\"el.cascader.noData\")])},renderNodeList:function(e){var t=this.menuId,i=this.panel.isHoverMenu,n={on:{}};i&&(n.on.expand=this.handleExpand);var r=this.nodes.map(function(i,r){var s=i.hasChildren;return e(\"cascader-node\",Bl()([{key:i.uid,attrs:{node:i,\"node-id\":t+\"-\"+r,\"aria-haspopup\":s,\"aria-owns\":s?t:null}},n]))});return[].concat(r,[i?e(\"svg\",{ref:\"hoverZone\",class:\"el-cascader-menu__hover-zone\"}):null])}},render:function(e){var t=this.isEmpty,i=this.menuId,n={nativeOn:{}};return this.panel.isHoverMenu&&(n.nativeOn.mousemove=this.handleMouseMove),e(\"el-scrollbar\",Bl()([{attrs:{tag:\"ul\",role:\"menu\",id:i,\"wrap-class\":\"el-cascader-menu__wrap\",\"view-class\":{\"el-cascader-menu__list\":!0,\"is-empty\":t}},class:\"el-cascader-menu\"},n]),[t?this.renderEmptyText(e):this.renderNodeList(e)])}},void 0,void 0,!1,null,null,null);Xu.options.__file=\"packages/cascader-panel/src/cascader-menu.vue\";var Ju=Xu.exports,Zu=function(){function e(e,t){for(var i=0;i1?t-1:0),n=1;n1?n-1:0),s=1;s0},e.prototype.syncCheckState=function(e){var t=this.getValueByOption(),i=this.isSameNode(e,t);this.doCheck(i)},e.prototype.doCheck=function(e){this.checked!==e&&(this.config.checkStrictly?this.checked=e:(this.broadcast(\"check\",e),this.setCheckState(e),this.emit(\"check\")))},Zu(e,[{key:\"isDisabled\",get:function(){var e=this.data,t=this.parent,i=this.config,n=i.disabled,r=i.checkStrictly;return e[n]||!r&&t&&t.isDisabled}},{key:\"isLeaf\",get:function(){var e=this.data,t=this.loaded,i=this.hasChildren,n=this.children,r=this.config,s=r.lazy,a=r.leaf;if(s){var o=Q(e[a])?e[a]:!!t&&!n.length;return this.hasChildren=!o,o}return!i}}]),e}();var tc=function(){function e(t,i){!function(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")}(this,e),this.config=i,this.initNodes(t)}return e.prototype.initNodes=function(e){var t=this;e=M(e),this.nodes=e.map(function(e){return new ec(e,t.config)}),this.flattedNodes=this.getFlattedNodes(!1,!1),this.leafNodes=this.getFlattedNodes(!0,!1)},e.prototype.appendNode=function(e,t){var i=new ec(e,this.config,t);(t?t.children:this.nodes).push(i)},e.prototype.appendNodes=function(e,t){var i=this;(e=M(e)).forEach(function(e){return i.appendNode(e,t)})},e.prototype.getNodes=function(){return this.nodes},e.prototype.getFlattedNodes=function(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=e?this.leafNodes:this.flattedNodes;return t?i:function e(t,i){return t.reduce(function(t,n){return n.isLeaf?t.push(n):(!i&&t.push(n),t=t.concat(e(n.children,i))),t},[])}(this.nodes,e)},e.prototype.getNodeByValue=function(e){if(e){var t=this.getFlattedNodes(!1,!this.config.lazy).filter(function(t){return $(t.path,e)||t.value===e});return t&&t.length?t[0]:null}return null},e}(),ic=Object.assign||function(e){for(var t=1;t0){var l=i.store.getNodeByValue(s);l.data[o]||i.lazyLoad(l,function(){i.handleExpand(l)}),i.loadCount===i.checkedValue.length&&i.$parent.computePresentText()}}t&&t(n)})},calculateMultiCheckedValue:function(){this.checkedValue=this.getCheckedNodes(this.leafOnly).map(function(e){return e.getValueByOption()})},scrollIntoView:function(){this.$isServer||(this.$refs.menu||[]).forEach(function(e){var t=e.$el;t&&ot(t.querySelector(\".el-scrollbar__wrap\"),t.querySelector(\".el-cascader-node.is-active\")||t.querySelector(\".el-cascader-node.in-active-path\"))})},getNodeByValue:function(e){return this.store.getNodeByValue(e)},getFlattedNodes:function(e){var t=!this.config.lazy;return this.store.getFlattedNodes(e,t)},getCheckedNodes:function(e){var t=this.checkedValue;return this.multiple?this.getFlattedNodes(e).filter(function(e){return e.checked}):A(t)?[]:[this.getNodeByValue(t)]},clearCheckedNodes:function(){var e=this.config,t=this.leafOnly,i=e.multiple,n=e.emitPath;i?(this.getCheckedNodes(t).filter(function(e){return!e.isDisabled}).forEach(function(e){return e.doCheck(!1)}),this.calculateMultiCheckedValue()):this.checkedValue=n?[]:null}}},Ku,[],!1,null,null,null);uc.options.__file=\"packages/cascader-panel/src/cascader-panel.vue\";var cc=uc.exports;cc.install=function(e){e.component(cc.name,cc)};var hc=cc,dc=qt.keys,pc={expandTrigger:{newProp:\"expandTrigger\",type:String},changeOnSelect:{newProp:\"checkStrictly\",type:Boolean},hoverThreshold:{newProp:\"hoverThreshold\",type:Number}},fc={props:{placement:{type:String,default:\"bottom-start\"},appendToBody:Oe.props.appendToBody,visibleArrow:{type:Boolean,default:!0},arrowOffset:Oe.props.arrowOffset,offset:Oe.props.offset,boundariesPadding:Oe.props.boundariesPadding,popperOptions:Oe.props.popperOptions},methods:Oe.methods,data:Oe.data,beforeDestroy:Oe.beforeDestroy},mc={medium:36,small:32,mini:28},vc=r({name:\"ElCascader\",directives:{Clickoutside:at},mixins:[fc,l,q,K],inject:{elForm:{default:\"\"},elFormItem:{default:\"\"}},components:{ElInput:ne,ElTag:Re,ElScrollbar:Ze,ElCascaderPanel:hc},props:{value:{},options:Array,props:Object,size:String,placeholder:{type:String,default:function(){return W(\"el.cascader.placeholder\")}},disabled:Boolean,clearable:Boolean,filterable:Boolean,filterMethod:Function,separator:{type:String,default:\" / \"},showAllLevels:{type:Boolean,default:!0},collapseTags:Boolean,debounce:{type:Number,default:300},beforeFilter:{type:Function,default:function(){return function(){}}},popperClass:String},data:function(){return{dropDownVisible:!1,checkedValue:this.value||null,inputHover:!1,inputValue:null,presentText:null,presentTags:[],checkedNodes:[],filtering:!1,suggestions:[],inputInitialHeight:0,pressDeleteCount:0}},computed:{realSize:function(){var e=(this.elFormItem||{}).elFormItemSize;return this.size||e||(this.$ELEMENT||{}).size},tagSize:function(){return[\"small\",\"mini\"].indexOf(this.realSize)>-1?\"mini\":\"small\"},isDisabled:function(){return this.disabled||(this.elForm||{}).disabled},config:function(){var e=this.props||{},t=this.$attrs;return Object.keys(pc).forEach(function(i){var n=pc[i],r=n.newProp,s=n.type,a=t[i]||t[N(i)];Q(i)&&!Q(e[r])&&(s===Boolean&&\"\"===a&&(a=!0),e[r]=a)}),e},multiple:function(){return this.config.multiple},leafOnly:function(){return!this.config.checkStrictly},readonly:function(){return!this.filterable||this.multiple},clearBtnVisible:function(){return!(!this.clearable||this.isDisabled||this.filtering||!this.inputHover)&&(this.multiple?!!this.checkedNodes.filter(function(e){return!e.isDisabled}).length:!!this.presentText)},panel:function(){return this.$refs.panel}},watch:{disabled:function(){this.computePresentContent()},value:function(e){I(e,this.checkedValue)||(this.checkedValue=e,this.computePresentContent())},checkedValue:function(e){var t=this.value,i=this.dropDownVisible,n=this.config,r=n.checkStrictly,s=n.multiple;I(e,t)&&!b(t)||(this.computePresentContent(),s||r||!i||this.toggleDropDownVisible(!1),this.$emit(\"input\",e),this.$emit(\"change\",e),this.dispatch(\"ElFormItem\",\"el.form.change\",[e]))},options:{handler:function(){this.$nextTick(this.computePresentContent)},deep:!0},presentText:function(e){this.inputValue=e},presentTags:function(e,t){this.multiple&&(e.length||t.length)&&this.$nextTick(this.updateStyle)},filtering:function(e){this.$nextTick(this.updatePopper)}},mounted:function(){var e=this,t=this.$refs.input;t&&t.$el&&(this.inputInitialHeight=t.$el.offsetHeight||mc[this.realSize]||40),A(this.value)||this.computePresentContent(),this.filterHandler=et()(this.debounce,function(){var t=e.inputValue;if(t){var i=e.beforeFilter(t);i&&i.then?i.then(e.getSuggestions):!1!==i?e.getSuggestions():e.filtering=!1}else e.filtering=!1}),Ye(this.$el,this.updateStyle)},beforeDestroy:function(){Ke(this.$el,this.updateStyle)},methods:{getMigratingConfig:function(){return{props:{\"expand-trigger\":\"expand-trigger is removed, use `props.expandTrigger` instead.\",\"change-on-select\":\"change-on-select is removed, use `props.checkStrictly` instead.\",\"hover-threshold\":\"hover-threshold is removed, use `props.hoverThreshold` instead\"},events:{\"active-item-change\":\"active-item-change is renamed to expand-change\"}}},toggleDropDownVisible:function(e){var t=this;if(!this.isDisabled){var i=this.dropDownVisible,n=this.$refs.input;(e=Q(e)?e:!i)!==i&&(this.dropDownVisible=e,e&&this.$nextTick(function(){t.updatePopper(),t.panel.scrollIntoView()}),n.$refs.input.setAttribute(\"aria-expanded\",e),this.$emit(\"visible-change\",e))}},handleDropdownLeave:function(){this.filtering=!1,this.inputValue=this.presentText},handleKeyDown:function(e){switch(e.keyCode){case dc.enter:this.toggleDropDownVisible();break;case dc.down:this.toggleDropDownVisible(!0),this.focusFirstNode(),e.preventDefault();break;case dc.esc:case dc.tab:this.toggleDropDownVisible(!1)}},handleFocus:function(e){this.$emit(\"focus\",e)},handleBlur:function(e){this.$emit(\"blur\",e)},handleInput:function(e,t){!this.dropDownVisible&&this.toggleDropDownVisible(!0),t&&t.isComposing||(e?this.filterHandler():this.filtering=!1)},handleClear:function(){this.presentText=\"\",this.panel.clearCheckedNodes()},handleExpandChange:function(e){this.$nextTick(this.updatePopper.bind(this)),this.$emit(\"expand-change\",e),this.$emit(\"active-item-change\",e)},focusFirstNode:function(){var e=this;this.$nextTick(function(){var t=e.filtering,i=e.$refs,n=i.popper,r=i.suggestionPanel,s=null;t&&r?s=r.$el.querySelector(\".el-cascader__suggestion-item\"):s=n.querySelector(\".el-cascader-menu\").querySelector('.el-cascader-node[tabindex=\"-1\"]');s&&(s.focus(),!t&&s.click())})},computePresentContent:function(){var e=this;this.$nextTick(function(){e.config.multiple?(e.computePresentTags(),e.presentText=e.presentTags.length?\" \":null):e.computePresentText()})},computePresentText:function(){var e=this.checkedValue,t=this.config;if(!A(e)){var i=this.panel.getNodeByValue(e);if(i&&(t.checkStrictly||i.isLeaf))return void(this.presentText=i.getText(this.showAllLevels,this.separator))}this.presentText=null},computePresentTags:function(){var e=this.isDisabled,t=this.leafOnly,i=this.showAllLevels,n=this.separator,r=this.collapseTags,s=this.getCheckedNodes(t),a=[],o=function(t){return{node:t,key:t.uid,text:t.getText(i,n),hitState:!1,closable:!e&&!t.isDisabled}};if(s.length){var l=s[0],u=s.slice(1),c=u.length;a.push(o(l)),c&&(r?a.push({key:-1,text:\"+ \"+c,closable:!1}):u.forEach(function(e){return a.push(o(e))}))}this.checkedNodes=s,this.presentTags=a},getSuggestions:function(){var e=this,t=this.filterMethod;g(t)||(t=function(e,t){return e.text.includes(t)});var i=this.panel.getFlattedNodes(this.leafOnly).filter(function(i){return!i.isDisabled&&(i.text=i.getText(e.showAllLevels,e.separator)||\"\",t(i,e.inputValue))});this.multiple?this.presentTags.forEach(function(e){e.hitState=!1}):i.forEach(function(t){t.checked=I(e.checkedValue,t.getValueByOption())}),this.filtering=!0,this.suggestions=i,this.$nextTick(this.updatePopper)},handleSuggestionKeyDown:function(e){var t=e.keyCode,i=e.target;switch(t){case dc.enter:i.click();break;case dc.up:var n=i.previousElementSibling;n&&n.focus();break;case dc.down:var r=i.nextElementSibling;r&&r.focus();break;case dc.esc:case dc.tab:this.toggleDropDownVisible(!1)}},handleDelete:function(){var e=this.inputValue,t=this.pressDeleteCount,i=this.presentTags,n=i.length-1,r=i[n];this.pressDeleteCount=e?0:t+1,r&&this.pressDeleteCount&&(r.hitState?this.deleteTag(n):r.hitState=!0)},handleSuggestionClick:function(e){var t=this.multiple,i=this.suggestions[e];if(t){var n=i.checked;i.doCheck(!n),this.panel.calculateMultiCheckedValue()}else this.checkedValue=i.getValueByOption(),this.toggleDropDownVisible(!1)},deleteTag:function(e){var t=this.checkedValue,i=t[e];this.checkedValue=t.filter(function(t,i){return i!==e}),this.$emit(\"remove-tag\",i)},updateStyle:function(){var e=this.$el,t=this.inputInitialHeight;if(!this.$isServer&&e){var i=this.$refs.suggestionPanel,n=e.querySelector(\".el-input__inner\");if(n){var r=e.querySelector(\".el-cascader__tags\"),s=null;if(i&&(s=i.$el))s.querySelector(\".el-cascader__suggestion-list\").style.minWidth=n.offsetWidth+\"px\";if(r){var a=r.offsetHeight,o=Math.max(a+6,t)+\"px\";n.style.height=o,this.updatePopper()}}}},getCheckedNodes:function(e){return this.panel.getCheckedNodes(e)}}},Yu,[],!1,null,null,null);vc.options.__file=\"packages/cascader/src/cascader.vue\";var gc=vc.exports;gc.install=function(e){e.component(gc.name,gc)};var bc=gc,yc=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{directives:[{name:\"clickoutside\",rawName:\"v-clickoutside\",value:e.hide,expression:\"hide\"}],class:[\"el-color-picker\",e.colorDisabled?\"is-disabled\":\"\",e.colorSize?\"el-color-picker--\"+e.colorSize:\"\"]},[e.colorDisabled?i(\"div\",{staticClass:\"el-color-picker__mask\"}):e._e(),i(\"div\",{staticClass:\"el-color-picker__trigger\",on:{click:e.handleTrigger}},[i(\"span\",{staticClass:\"el-color-picker__color\",class:{\"is-alpha\":e.showAlpha}},[i(\"span\",{staticClass:\"el-color-picker__color-inner\",style:{backgroundColor:e.displayedColor}}),e.value||e.showPanelColor?e._e():i(\"span\",{staticClass:\"el-color-picker__empty el-icon-close\"})]),i(\"span\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.value||e.showPanelColor,expression:\"value || showPanelColor\"}],staticClass:\"el-color-picker__icon el-icon-arrow-down\"})]),i(\"picker-dropdown\",{ref:\"dropdown\",class:[\"el-color-picker__panel\",e.popperClass||\"\"],attrs:{color:e.color,\"show-alpha\":e.showAlpha,predefine:e.predefine},on:{pick:e.confirmValue,clear:e.clearValue},model:{value:e.showPicker,callback:function(t){e.showPicker=t},expression:\"showPicker\"}})],1)};yc._withStripped=!0;var wc=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e};var _c=function(e,t,i){return[e,t*i/((e=(2-t)*i)<1?e:2-e)||0,e/2]},xc=function(e,t){var i;\"string\"==typeof(i=e)&&-1!==i.indexOf(\".\")&&1===parseFloat(i)&&(e=\"100%\");var n=function(e){return\"string\"==typeof e&&-1!==e.indexOf(\"%\")}(e);return e=Math.min(t,Math.max(0,parseFloat(e))),n&&(e=parseInt(e*t,10)/100),Math.abs(e-t)<1e-6?1:e%t/parseFloat(t)},Cc={10:\"A\",11:\"B\",12:\"C\",13:\"D\",14:\"E\",15:\"F\"},kc={A:10,B:11,C:12,D:13,E:14,F:15},Sc=function(e){return 2===e.length?16*(kc[e[0].toUpperCase()]||+e[0])+(kc[e[1].toUpperCase()]||+e[1]):kc[e[1].toUpperCase()]||+e[1]},Dc=function(e,t,i){e=xc(e,255),t=xc(t,255),i=xc(i,255);var n,r=Math.max(e,t,i),s=Math.min(e,t,i),a=void 0,o=r,l=r-s;if(n=0===r?0:l/r,r===s)a=0;else{switch(r){case e:a=(t-i)/l+(t2?parseFloat(e):parseInt(e,10)});if(4===n.length?this._alpha=Math.floor(100*parseFloat(n[3])):3===n.length&&(this._alpha=100),n.length>=3){var r=function(e,t,i){i/=100;var n=t/=100,r=Math.max(i,.01);return t*=(i*=2)<=1?i:2-i,n*=r<=1?r:2-r,{h:e,s:100*(0===i?2*n/(r+n):2*t/(i+t)),v:(i+t)/2*100}}(n[0],n[1],n[2]);i(r.h,r.s,r.v)}}else if(-1!==e.indexOf(\"hsv\")){var s=e.replace(/hsva|hsv|\\(|\\)/gm,\"\").split(/\\s|,/g).filter(function(e){return\"\"!==e}).map(function(e,t){return t>2?parseFloat(e):parseInt(e,10)});4===s.length?this._alpha=Math.floor(100*parseFloat(s[3])):3===s.length&&(this._alpha=100),s.length>=3&&i(s[0],s[1],s[2])}else if(-1!==e.indexOf(\"rgb\")){var a=e.replace(/rgba|rgb|\\(|\\)/gm,\"\").split(/\\s|,/g).filter(function(e){return\"\"!==e}).map(function(e,t){return t>2?parseFloat(e):parseInt(e,10)});if(4===a.length?this._alpha=Math.floor(100*parseFloat(a[3])):3===a.length&&(this._alpha=100),a.length>=3){var o=Dc(a[0],a[1],a[2]);i(o.h,o.s,o.v)}}else if(-1!==e.indexOf(\"#\")){var l=e.replace(\"#\",\"\").trim();if(!/^(?:[0-9a-fA-F]{3}){1,2}$/.test(l))return;var u=void 0,c=void 0,h=void 0;3===l.length?(u=Sc(l[0]+l[0]),c=Sc(l[1]+l[1]),h=Sc(l[2]+l[2])):6!==l.length&&8!==l.length||(u=Sc(l.substring(0,2)),c=Sc(l.substring(2,4)),h=Sc(l.substring(4,6))),8===l.length?this._alpha=Math.floor(Sc(l.substring(6))/255*100):3!==l.length&&6!==l.length||(this._alpha=100);var d=Dc(u,c,h);i(d.h,d.s,d.v)}},e.prototype.compare=function(e){return Math.abs(e._hue-this._hue)<2&&Math.abs(e._saturation-this._saturation)<1&&Math.abs(e._value-this._value)<1&&Math.abs(e._alpha-this._alpha)<1},e.prototype.doOnChange=function(){var e=this._hue,t=this._saturation,i=this._value,n=this._alpha,r=this.format;if(this.enableAlpha)switch(r){case\"hsl\":var s=_c(e,t/100,i/100);this.value=\"hsla(\"+e+\", \"+Math.round(100*s[1])+\"%, \"+Math.round(100*s[2])+\"%, \"+n/100+\")\";break;case\"hsv\":this.value=\"hsva(\"+e+\", \"+Math.round(t)+\"%, \"+Math.round(i)+\"%, \"+n/100+\")\";break;default:var a=$c(e,t,i),o=a.r,l=a.g,u=a.b;this.value=\"rgba(\"+o+\", \"+l+\", \"+u+\", \"+n/100+\")\"}else switch(r){case\"hsl\":var c=_c(e,t/100,i/100);this.value=\"hsl(\"+e+\", \"+Math.round(100*c[1])+\"%, \"+Math.round(100*c[2])+\"%)\";break;case\"hsv\":this.value=\"hsv(\"+e+\", \"+Math.round(t)+\"%, \"+Math.round(i)+\"%)\";break;case\"rgb\":var h=$c(e,t,i),d=h.r,p=h.g,f=h.b;this.value=\"rgb(\"+d+\", \"+p+\", \"+f+\")\";break;default:this.value=function(e){var t=e.r,i=e.g,n=e.b,r=function(e){e=Math.min(Math.round(e),255);var t=Math.floor(e/16),i=e%16;return\"\"+(Cc[t]||t)+(Cc[i]||i)};return isNaN(t)||isNaN(i)||isNaN(n)?\"\":\"#\"+r(t)+r(i)+r(n)}($c(e,t,i))}},e}(),Tc=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition\",{attrs:{name:\"el-zoom-in-top\"},on:{\"after-leave\":e.doDestroy}},[i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.showPopper,expression:\"showPopper\"}],staticClass:\"el-color-dropdown\"},[i(\"div\",{staticClass:\"el-color-dropdown__main-wrapper\"},[i(\"hue-slider\",{ref:\"hue\",staticStyle:{float:\"right\"},attrs:{color:e.color,vertical:\"\"}}),i(\"sv-panel\",{ref:\"sl\",attrs:{color:e.color}})],1),e.showAlpha?i(\"alpha-slider\",{ref:\"alpha\",attrs:{color:e.color}}):e._e(),e.predefine?i(\"predefine\",{attrs:{color:e.color,colors:e.predefine}}):e._e(),i(\"div\",{staticClass:\"el-color-dropdown__btns\"},[i(\"span\",{staticClass:\"el-color-dropdown__value\"},[i(\"el-input\",{attrs:{\"validate-event\":!1,size:\"mini\"},on:{blur:e.handleConfirm},nativeOn:{keyup:function(t){return\"button\"in t||!e._k(t.keyCode,\"enter\",13,t.key,\"Enter\")?e.handleConfirm(t):null}},model:{value:e.customInput,callback:function(t){e.customInput=t},expression:\"customInput\"}})],1),i(\"el-button\",{staticClass:\"el-color-dropdown__link-btn\",attrs:{size:\"mini\",type:\"text\"},on:{click:function(t){e.$emit(\"clear\")}}},[e._v(\"\\n \"+e._s(e.t(\"el.colorpicker.clear\"))+\"\\n \")]),i(\"el-button\",{staticClass:\"el-color-dropdown__btn\",attrs:{plain:\"\",size:\"mini\"},on:{click:e.confirmValue}},[e._v(\"\\n \"+e._s(e.t(\"el.colorpicker.confirm\"))+\"\\n \")])],1)],1)])};Tc._withStripped=!0;var Mc=function(){var e=this.$createElement,t=this._self._c||e;return t(\"div\",{staticClass:\"el-color-svpanel\",style:{backgroundColor:this.background}},[t(\"div\",{staticClass:\"el-color-svpanel__white\"}),t(\"div\",{staticClass:\"el-color-svpanel__black\"}),t(\"div\",{staticClass:\"el-color-svpanel__cursor\",style:{top:this.cursorTop+\"px\",left:this.cursorLeft+\"px\"}},[t(\"div\")])])};Mc._withStripped=!0;var Nc=!1,Pc=function(e,t){if(!h.a.prototype.$isServer){var i=function(e){t.drag&&t.drag(e)},n=function e(n){document.removeEventListener(\"mousemove\",i),document.removeEventListener(\"mouseup\",e),document.onselectstart=null,document.ondragstart=null,Nc=!1,t.end&&t.end(n)};e.addEventListener(\"mousedown\",function(e){Nc||(document.onselectstart=function(){return!1},document.ondragstart=function(){return!1},document.addEventListener(\"mousemove\",i),document.addEventListener(\"mouseup\",n),Nc=!0,t.start&&t.start(e))})}},Oc=r({name:\"el-sl-panel\",props:{color:{required:!0}},computed:{colorValue:function(){return{hue:this.color.get(\"hue\"),value:this.color.get(\"value\")}}},watch:{colorValue:function(){this.update()}},methods:{update:function(){var e=this.color.get(\"saturation\"),t=this.color.get(\"value\"),i=this.$el,n=i.clientWidth,r=i.clientHeight;this.cursorLeft=e*n/100,this.cursorTop=(100-t)*r/100,this.background=\"hsl(\"+this.color.get(\"hue\")+\", 100%, 50%)\"},handleDrag:function(e){var t=this.$el.getBoundingClientRect(),i=e.clientX-t.left,n=e.clientY-t.top;i=Math.max(0,i),i=Math.min(i,t.width),n=Math.max(0,n),n=Math.min(n,t.height),this.cursorLeft=i,this.cursorTop=n,this.color.set({saturation:i/t.width*100,value:100-n/t.height*100})}},mounted:function(){var e=this;Pc(this.$el,{drag:function(t){e.handleDrag(t)},end:function(t){e.handleDrag(t)}}),this.update()},data:function(){return{cursorTop:0,cursorLeft:0,background:\"hsl(0, 100%, 50%)\"}}},Mc,[],!1,null,null,null);Oc.options.__file=\"packages/color-picker/src/components/sv-panel.vue\";var Ic=Oc.exports,Ac=function(){var e=this.$createElement,t=this._self._c||e;return t(\"div\",{staticClass:\"el-color-hue-slider\",class:{\"is-vertical\":this.vertical}},[t(\"div\",{ref:\"bar\",staticClass:\"el-color-hue-slider__bar\",on:{click:this.handleClick}}),t(\"div\",{ref:\"thumb\",staticClass:\"el-color-hue-slider__thumb\",style:{left:this.thumbLeft+\"px\",top:this.thumbTop+\"px\"}})])};Ac._withStripped=!0;var Fc=r({name:\"el-color-hue-slider\",props:{color:{required:!0},vertical:Boolean},data:function(){return{thumbLeft:0,thumbTop:0}},computed:{hueValue:function(){return this.color.get(\"hue\")}},watch:{hueValue:function(){this.update()}},methods:{handleClick:function(e){var t=this.$refs.thumb;e.target!==t&&this.handleDrag(e)},handleDrag:function(e){var t=this.$el.getBoundingClientRect(),i=this.$refs.thumb,n=void 0;if(this.vertical){var r=e.clientY-t.top;r=Math.min(r,t.height-i.offsetHeight/2),r=Math.max(i.offsetHeight/2,r),n=Math.round((r-i.offsetHeight/2)/(t.height-i.offsetHeight)*360)}else{var s=e.clientX-t.left;s=Math.min(s,t.width-i.offsetWidth/2),s=Math.max(i.offsetWidth/2,s),n=Math.round((s-i.offsetWidth/2)/(t.width-i.offsetWidth)*360)}this.color.set(\"hue\",n)},getThumbLeft:function(){if(this.vertical)return 0;var e=this.$el,t=this.color.get(\"hue\");if(!e)return 0;var i=this.$refs.thumb;return Math.round(t*(e.offsetWidth-i.offsetWidth/2)/360)},getThumbTop:function(){if(!this.vertical)return 0;var e=this.$el,t=this.color.get(\"hue\");if(!e)return 0;var i=this.$refs.thumb;return Math.round(t*(e.offsetHeight-i.offsetHeight/2)/360)},update:function(){this.thumbLeft=this.getThumbLeft(),this.thumbTop=this.getThumbTop()}},mounted:function(){var e=this,t=this.$refs,i=t.bar,n=t.thumb,r={drag:function(t){e.handleDrag(t)},end:function(t){e.handleDrag(t)}};Pc(i,r),Pc(n,r),this.update()}},Ac,[],!1,null,null,null);Fc.options.__file=\"packages/color-picker/src/components/hue-slider.vue\";var Lc=Fc.exports,Vc=function(){var e=this.$createElement,t=this._self._c||e;return t(\"div\",{staticClass:\"el-color-alpha-slider\",class:{\"is-vertical\":this.vertical}},[t(\"div\",{ref:\"bar\",staticClass:\"el-color-alpha-slider__bar\",style:{background:this.background},on:{click:this.handleClick}}),t(\"div\",{ref:\"thumb\",staticClass:\"el-color-alpha-slider__thumb\",style:{left:this.thumbLeft+\"px\",top:this.thumbTop+\"px\"}})])};Vc._withStripped=!0;var Bc=r({name:\"el-color-alpha-slider\",props:{color:{required:!0},vertical:Boolean},watch:{\"color._alpha\":function(){this.update()},\"color.value\":function(){this.update()}},methods:{handleClick:function(e){var t=this.$refs.thumb;e.target!==t&&this.handleDrag(e)},handleDrag:function(e){var t=this.$el.getBoundingClientRect(),i=this.$refs.thumb;if(this.vertical){var n=e.clientY-t.top;n=Math.max(i.offsetHeight/2,n),n=Math.min(n,t.height-i.offsetHeight/2),this.color.set(\"alpha\",Math.round((n-i.offsetHeight/2)/(t.height-i.offsetHeight)*100))}else{var r=e.clientX-t.left;r=Math.max(i.offsetWidth/2,r),r=Math.min(r,t.width-i.offsetWidth/2),this.color.set(\"alpha\",Math.round((r-i.offsetWidth/2)/(t.width-i.offsetWidth)*100))}},getThumbLeft:function(){if(this.vertical)return 0;var e=this.$el,t=this.color._alpha;if(!e)return 0;var i=this.$refs.thumb;return Math.round(t*(e.offsetWidth-i.offsetWidth/2)/100)},getThumbTop:function(){if(!this.vertical)return 0;var e=this.$el,t=this.color._alpha;if(!e)return 0;var i=this.$refs.thumb;return Math.round(t*(e.offsetHeight-i.offsetHeight/2)/100)},getBackground:function(){if(this.color&&this.color.value){var e=this.color.toRgb(),t=e.r,i=e.g,n=e.b;return\"linear-gradient(to right, rgba(\"+t+\", \"+i+\", \"+n+\", 0) 0%, rgba(\"+t+\", \"+i+\", \"+n+\", 1) 100%)\"}return null},update:function(){this.thumbLeft=this.getThumbLeft(),this.thumbTop=this.getThumbTop(),this.background=this.getBackground()}},data:function(){return{thumbLeft:0,thumbTop:0,background:null}},mounted:function(){var e=this,t=this.$refs,i=t.bar,n=t.thumb,r={drag:function(t){e.handleDrag(t)},end:function(t){e.handleDrag(t)}};Pc(i,r),Pc(n,r),this.update()}},Vc,[],!1,null,null,null);Bc.options.__file=\"packages/color-picker/src/components/alpha-slider.vue\";var zc=Bc.exports,Hc=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-color-predefine\"},[i(\"div\",{staticClass:\"el-color-predefine__colors\"},e._l(e.rgbaColors,function(t,n){return i(\"div\",{key:e.colors[n],staticClass:\"el-color-predefine__color-selector\",class:{selected:t.selected,\"is-alpha\":t._alpha<100},on:{click:function(t){e.handleSelect(n)}}},[i(\"div\",{style:{\"background-color\":t.value}})])}),0)])};Hc._withStripped=!0;var Rc=r({props:{colors:{type:Array,required:!0},color:{required:!0}},data:function(){return{rgbaColors:this.parseColors(this.colors,this.color)}},methods:{handleSelect:function(e){this.color.fromString(this.colors[e])},parseColors:function(e,t){return e.map(function(e){var i=new Ec;return i.enableAlpha=!0,i.format=\"rgba\",i.fromString(e),i.selected=i.value===t.value,i})}},watch:{\"$parent.currentColor\":function(e){var t=new Ec;t.fromString(e),this.rgbaColors.forEach(function(e){e.selected=t.compare(e)})},colors:function(e){this.rgbaColors=this.parseColors(e,this.color)},color:function(e){this.rgbaColors=this.parseColors(this.colors,e)}}},Hc,[],!1,null,null,null);Rc.options.__file=\"packages/color-picker/src/components/predefine.vue\";var Wc=Rc.exports,jc=r({name:\"el-color-picker-dropdown\",mixins:[Oe,q],components:{SvPanel:Ic,HueSlider:Lc,AlphaSlider:zc,ElInput:ne,ElButton:Et,Predefine:Wc},props:{color:{required:!0},showAlpha:Boolean,predefine:Array},data:function(){return{customInput:\"\"}},computed:{currentColor:function(){var e=this.$parent;return e.value||e.showPanelColor?e.color.value:\"\"}},methods:{confirmValue:function(){this.$emit(\"pick\")},handleConfirm:function(){this.color.fromString(this.customInput)}},mounted:function(){this.$parent.popperElm=this.popperElm=this.$el,this.referenceElm=this.$parent.$el},watch:{showPopper:function(e){var t=this;!0===e&&this.$nextTick(function(){var e=t.$refs,i=e.sl,n=e.hue,r=e.alpha;i&&i.update(),n&&n.update(),r&&r.update()})},currentColor:{immediate:!0,handler:function(e){this.customInput=e}}}},Tc,[],!1,null,null,null);jc.options.__file=\"packages/color-picker/src/components/picker-dropdown.vue\";var qc=jc.exports,Yc=r({name:\"ElColorPicker\",mixins:[l],props:{value:String,showAlpha:Boolean,colorFormat:String,disabled:Boolean,size:String,popperClass:String,predefine:Array},inject:{elForm:{default:\"\"},elFormItem:{default:\"\"}},directives:{Clickoutside:at},computed:{displayedColor:function(){return this.value||this.showPanelColor?this.displayedRgb(this.color,this.showAlpha):\"transparent\"},_elFormItemSize:function(){return(this.elFormItem||{}).elFormItemSize},colorSize:function(){return this.size||this._elFormItemSize||(this.$ELEMENT||{}).size},colorDisabled:function(){return this.disabled||(this.elForm||{}).disabled}},watch:{value:function(e){e?e&&e!==this.color.value&&this.color.fromString(e):this.showPanelColor=!1},color:{deep:!0,handler:function(){this.showPanelColor=!0}},displayedColor:function(e){if(this.showPicker){var t=new Ec({enableAlpha:this.showAlpha,format:this.colorFormat});t.fromString(this.value),e!==this.displayedRgb(t,this.showAlpha)&&this.$emit(\"active-change\",e)}}},methods:{handleTrigger:function(){this.colorDisabled||(this.showPicker=!this.showPicker)},confirmValue:function(){var e=this.color.value;this.$emit(\"input\",e),this.$emit(\"change\",e),this.dispatch(\"ElFormItem\",\"el.form.change\",e),this.showPicker=!1},clearValue:function(){this.$emit(\"input\",null),this.$emit(\"change\",null),null!==this.value&&this.dispatch(\"ElFormItem\",\"el.form.change\",null),this.showPanelColor=!1,this.showPicker=!1,this.resetColor()},hide:function(){this.showPicker=!1,this.resetColor()},resetColor:function(){var e=this;this.$nextTick(function(t){e.value?e.color.fromString(e.value):e.showPanelColor=!1})},displayedRgb:function(e,t){if(!(e instanceof Ec))throw Error(\"color should be instance of Color Class\");var i=e.toRgb(),n=i.r,r=i.g,s=i.b;return t?\"rgba(\"+n+\", \"+r+\", \"+s+\", \"+e.get(\"alpha\")/100+\")\":\"rgb(\"+n+\", \"+r+\", \"+s+\")\"}},mounted:function(){var e=this.value;e&&this.color.fromString(e),this.popperElm=this.$refs.dropdown.$el},data:function(){return{color:new Ec({enableAlpha:this.showAlpha,format:this.colorFormat}),showPicker:!1,showPanelColor:!1}},components:{PickerDropdown:qc}},yc,[],!1,null,null,null);Yc.options.__file=\"packages/color-picker/src/main.vue\";var Kc=Yc.exports;Kc.install=function(e){e.component(Kc.name,Kc)};var Gc=Kc,Uc=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-transfer\"},[i(\"transfer-panel\",e._b({ref:\"leftPanel\",attrs:{data:e.sourceData,title:e.titles[0]||e.t(\"el.transfer.titles.0\"),\"default-checked\":e.leftDefaultChecked,placeholder:e.filterPlaceholder||e.t(\"el.transfer.filterPlaceholder\")},on:{\"checked-change\":e.onSourceCheckedChange}},\"transfer-panel\",e.$props,!1),[e._t(\"left-footer\")],2),i(\"div\",{staticClass:\"el-transfer__buttons\"},[i(\"el-button\",{class:[\"el-transfer__button\",e.hasButtonTexts?\"is-with-texts\":\"\"],attrs:{type:\"primary\",disabled:0===e.rightChecked.length},nativeOn:{click:function(t){return e.addToLeft(t)}}},[i(\"i\",{staticClass:\"el-icon-arrow-left\"}),void 0!==e.buttonTexts[0]?i(\"span\",[e._v(e._s(e.buttonTexts[0]))]):e._e()]),i(\"el-button\",{class:[\"el-transfer__button\",e.hasButtonTexts?\"is-with-texts\":\"\"],attrs:{type:\"primary\",disabled:0===e.leftChecked.length},nativeOn:{click:function(t){return e.addToRight(t)}}},[void 0!==e.buttonTexts[1]?i(\"span\",[e._v(e._s(e.buttonTexts[1]))]):e._e(),i(\"i\",{staticClass:\"el-icon-arrow-right\"})])],1),i(\"transfer-panel\",e._b({ref:\"rightPanel\",attrs:{data:e.targetData,title:e.titles[1]||e.t(\"el.transfer.titles.1\"),\"default-checked\":e.rightDefaultChecked,placeholder:e.filterPlaceholder||e.t(\"el.transfer.filterPlaceholder\")},on:{\"checked-change\":e.onTargetCheckedChange}},\"transfer-panel\",e.$props,!1),[e._t(\"right-footer\")],2)],1)};Uc._withStripped=!0;var Xc=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-transfer-panel\"},[i(\"p\",{staticClass:\"el-transfer-panel__header\"},[i(\"el-checkbox\",{attrs:{indeterminate:e.isIndeterminate},on:{change:e.handleAllCheckedChange},model:{value:e.allChecked,callback:function(t){e.allChecked=t},expression:\"allChecked\"}},[e._v(\"\\n \"+e._s(e.title)+\"\\n \"),i(\"span\",[e._v(e._s(e.checkedSummary))])])],1),i(\"div\",{class:[\"el-transfer-panel__body\",e.hasFooter?\"is-with-footer\":\"\"]},[e.filterable?i(\"el-input\",{staticClass:\"el-transfer-panel__filter\",attrs:{size:\"small\",placeholder:e.placeholder},nativeOn:{mouseenter:function(t){e.inputHover=!0},mouseleave:function(t){e.inputHover=!1}},model:{value:e.query,callback:function(t){e.query=t},expression:\"query\"}},[i(\"i\",{class:[\"el-input__icon\",\"el-icon-\"+e.inputIcon],attrs:{slot:\"prefix\"},on:{click:e.clearQuery},slot:\"prefix\"})]):e._e(),i(\"el-checkbox-group\",{directives:[{name:\"show\",rawName:\"v-show\",value:!e.hasNoMatch&&e.data.length>0,expression:\"!hasNoMatch && data.length > 0\"}],staticClass:\"el-transfer-panel__list\",class:{\"is-filterable\":e.filterable},model:{value:e.checked,callback:function(t){e.checked=t},expression:\"checked\"}},e._l(e.filteredData,function(t){return i(\"el-checkbox\",{key:t[e.keyProp],staticClass:\"el-transfer-panel__item\",attrs:{label:t[e.keyProp],disabled:t[e.disabledProp]}},[i(\"option-content\",{attrs:{option:t}})],1)}),1),i(\"p\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.hasNoMatch,expression:\"hasNoMatch\"}],staticClass:\"el-transfer-panel__empty\"},[e._v(e._s(e.t(\"el.transfer.noMatch\")))]),i(\"p\",{directives:[{name:\"show\",rawName:\"v-show\",value:0===e.data.length&&!e.hasNoMatch,expression:\"data.length === 0 && !hasNoMatch\"}],staticClass:\"el-transfer-panel__empty\"},[e._v(e._s(e.t(\"el.transfer.noData\")))])],1),e.hasFooter?i(\"p\",{staticClass:\"el-transfer-panel__footer\"},[e._t(\"default\")],2):e._e()])};Xc._withStripped=!0;var Jc=r({mixins:[q],name:\"ElTransferPanel\",componentName:\"ElTransferPanel\",components:{ElCheckboxGroup:Yi,ElCheckbox:Vi,ElInput:ne,OptionContent:{props:{option:Object},render:function(e){var t=function e(t){return\"ElTransferPanel\"===t.$options.componentName?t:t.$parent?e(t.$parent):t}(this),i=t.$parent||t;return t.renderContent?t.renderContent(e,this.option):i.$scopedSlots.default?i.$scopedSlots.default({option:this.option}):e(\"span\",[this.option[t.labelProp]||this.option[t.keyProp]])}}},props:{data:{type:Array,default:function(){return[]}},renderContent:Function,placeholder:String,title:String,filterable:Boolean,format:Object,filterMethod:Function,defaultChecked:Array,props:Object},data:function(){return{checked:[],allChecked:!1,query:\"\",inputHover:!1,checkChangeByUser:!0}},watch:{checked:function(e,t){if(this.updateAllChecked(),this.checkChangeByUser){var i=e.concat(t).filter(function(i){return-1===e.indexOf(i)||-1===t.indexOf(i)});this.$emit(\"checked-change\",e,i)}else this.$emit(\"checked-change\",e),this.checkChangeByUser=!0},data:function(){var e=this,t=[],i=this.filteredData.map(function(t){return t[e.keyProp]});this.checked.forEach(function(e){i.indexOf(e)>-1&&t.push(e)}),this.checkChangeByUser=!1,this.checked=t},checkableData:function(){this.updateAllChecked()},defaultChecked:{immediate:!0,handler:function(e,t){var i=this;if(!t||e.length!==t.length||!e.every(function(e){return t.indexOf(e)>-1})){var n=[],r=this.checkableData.map(function(e){return e[i.keyProp]});e.forEach(function(e){r.indexOf(e)>-1&&n.push(e)}),this.checkChangeByUser=!1,this.checked=n}}}},computed:{filteredData:function(){var e=this;return this.data.filter(function(t){return\"function\"==typeof e.filterMethod?e.filterMethod(e.query,t):(t[e.labelProp]||t[e.keyProp].toString()).toLowerCase().indexOf(e.query.toLowerCase())>-1})},checkableData:function(){var e=this;return this.filteredData.filter(function(t){return!t[e.disabledProp]})},checkedSummary:function(){var e=this.checked.length,t=this.data.length,i=this.format,n=i.noChecked,r=i.hasChecked;return n&&r?e>0?r.replace(/\\${checked}/g,e).replace(/\\${total}/g,t):n.replace(/\\${total}/g,t):e+\"/\"+t},isIndeterminate:function(){var e=this.checked.length;return e>0&&e0&&0===this.filteredData.length},inputIcon:function(){return this.query.length>0&&this.inputHover?\"circle-close\":\"search\"},labelProp:function(){return this.props.label||\"label\"},keyProp:function(){return this.props.key||\"key\"},disabledProp:function(){return this.props.disabled||\"disabled\"},hasFooter:function(){return!!this.$slots.default}},methods:{updateAllChecked:function(){var e=this,t=this.checkableData.map(function(t){return t[e.keyProp]});this.allChecked=t.length>0&&t.every(function(t){return e.checked.indexOf(t)>-1})},handleAllCheckedChange:function(e){var t=this;this.checked=e?this.checkableData.map(function(e){return e[t.keyProp]}):[]},clearQuery:function(){\"circle-close\"===this.inputIcon&&(this.query=\"\")}}},Xc,[],!1,null,null,null);Jc.options.__file=\"packages/transfer/src/transfer-panel.vue\";var Zc=r({name:\"ElTransfer\",mixins:[l,q,K],components:{TransferPanel:Jc.exports,ElButton:Et},props:{data:{type:Array,default:function(){return[]}},titles:{type:Array,default:function(){return[]}},buttonTexts:{type:Array,default:function(){return[]}},filterPlaceholder:{type:String,default:\"\"},filterMethod:Function,leftDefaultChecked:{type:Array,default:function(){return[]}},rightDefaultChecked:{type:Array,default:function(){return[]}},renderContent:Function,value:{type:Array,default:function(){return[]}},format:{type:Object,default:function(){return{}}},filterable:Boolean,props:{type:Object,default:function(){return{label:\"label\",key:\"key\",disabled:\"disabled\"}}},targetOrder:{type:String,default:\"original\"}},data:function(){return{leftChecked:[],rightChecked:[]}},computed:{dataObj:function(){var e=this.props.key;return this.data.reduce(function(t,i){return(t[i[e]]=i)&&t},{})},sourceData:function(){var e=this;return this.data.filter(function(t){return-1===e.value.indexOf(t[e.props.key])})},targetData:function(){var e=this;return\"original\"===this.targetOrder?this.data.filter(function(t){return e.value.indexOf(t[e.props.key])>-1}):this.value.reduce(function(t,i){var n=e.dataObj[i];return n&&t.push(n),t},[])},hasButtonTexts:function(){return 2===this.buttonTexts.length}},watch:{value:function(e){this.dispatch(\"ElFormItem\",\"el.form.change\",e)}},methods:{getMigratingConfig:function(){return{props:{\"footer-format\":\"footer-format is renamed to format.\"}}},onSourceCheckedChange:function(e,t){this.leftChecked=e,void 0!==t&&this.$emit(\"left-check-change\",e,t)},onTargetCheckedChange:function(e,t){this.rightChecked=e,void 0!==t&&this.$emit(\"right-check-change\",e,t)},addToLeft:function(){var e=this.value.slice();this.rightChecked.forEach(function(t){var i=e.indexOf(t);i>-1&&e.splice(i,1)}),this.$emit(\"input\",e),this.$emit(\"change\",e,\"left\",this.rightChecked)},addToRight:function(){var e=this,t=this.value.slice(),i=[],n=this.props.key;this.data.forEach(function(t){var r=t[n];e.leftChecked.indexOf(r)>-1&&-1===e.value.indexOf(r)&&i.push(r)}),t=\"unshift\"===this.targetOrder?i.concat(t):t.concat(i),this.$emit(\"input\",t),this.$emit(\"change\",t,\"right\",this.leftChecked)},clearQuery:function(e){\"left\"===e?this.$refs.leftPanel.query=\"\":\"right\"===e&&(this.$refs.rightPanel.query=\"\")}}},Uc,[],!1,null,null,null);Zc.options.__file=\"packages/transfer/src/main.vue\";var Qc=Zc.exports;Qc.install=function(e){e.component(Qc.name,Qc)};var eh=Qc,th=function(){var e=this.$createElement;return(this._self._c||e)(\"section\",{staticClass:\"el-container\",class:{\"is-vertical\":this.isVertical}},[this._t(\"default\")],2)};th._withStripped=!0;var ih=r({name:\"ElContainer\",componentName:\"ElContainer\",props:{direction:String},computed:{isVertical:function(){return\"vertical\"===this.direction||\"horizontal\"!==this.direction&&(!(!this.$slots||!this.$slots.default)&&this.$slots.default.some(function(e){var t=e.componentOptions&&e.componentOptions.tag;return\"el-header\"===t||\"el-footer\"===t}))}}},th,[],!1,null,null,null);ih.options.__file=\"packages/container/src/main.vue\";var nh=ih.exports;nh.install=function(e){e.component(nh.name,nh)};var rh=nh,sh=function(){var e=this.$createElement;return(this._self._c||e)(\"header\",{staticClass:\"el-header\",style:{height:this.height}},[this._t(\"default\")],2)};sh._withStripped=!0;var ah=r({name:\"ElHeader\",componentName:\"ElHeader\",props:{height:{type:String,default:\"60px\"}}},sh,[],!1,null,null,null);ah.options.__file=\"packages/header/src/main.vue\";var oh=ah.exports;oh.install=function(e){e.component(oh.name,oh)};var lh=oh,uh=function(){var e=this.$createElement;return(this._self._c||e)(\"aside\",{staticClass:\"el-aside\",style:{width:this.width}},[this._t(\"default\")],2)};uh._withStripped=!0;var ch=r({name:\"ElAside\",componentName:\"ElAside\",props:{width:{type:String,default:\"300px\"}}},uh,[],!1,null,null,null);ch.options.__file=\"packages/aside/src/main.vue\";var hh=ch.exports;hh.install=function(e){e.component(hh.name,hh)};var dh=hh,ph=function(){var e=this.$createElement;return(this._self._c||e)(\"main\",{staticClass:\"el-main\"},[this._t(\"default\")],2)};ph._withStripped=!0;var fh=r({name:\"ElMain\",componentName:\"ElMain\"},ph,[],!1,null,null,null);fh.options.__file=\"packages/main/src/main.vue\";var mh=fh.exports;mh.install=function(e){e.component(mh.name,mh)};var vh=mh,gh=function(){var e=this.$createElement;return(this._self._c||e)(\"footer\",{staticClass:\"el-footer\",style:{height:this.height}},[this._t(\"default\")],2)};gh._withStripped=!0;var bh=r({name:\"ElFooter\",componentName:\"ElFooter\",props:{height:{type:String,default:\"60px\"}}},gh,[],!1,null,null,null);bh.options.__file=\"packages/footer/src/main.vue\";var yh=bh.exports;yh.install=function(e){e.component(yh.name,yh)};var wh=yh,_h=r({name:\"ElTimeline\",props:{reverse:{type:Boolean,default:!1}},provide:function(){return{timeline:this}},render:function(){var e=arguments[0],t=this.reverse,i={\"el-timeline\":!0,\"is-reverse\":t},n=this.$slots.default||[];return t&&(n=n.reverse()),e(\"ul\",{class:i},[n])}},void 0,void 0,!1,null,null,null);_h.options.__file=\"packages/timeline/src/main.vue\";var xh=_h.exports;xh.install=function(e){e.component(xh.name,xh)};var Ch=xh,kh=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"li\",{staticClass:\"el-timeline-item\"},[i(\"div\",{staticClass:\"el-timeline-item__tail\"}),e.$slots.dot?e._e():i(\"div\",{staticClass:\"el-timeline-item__node\",class:[\"el-timeline-item__node--\"+(e.size||\"\"),\"el-timeline-item__node--\"+(e.type||\"\")],style:{backgroundColor:e.color}},[e.icon?i(\"i\",{staticClass:\"el-timeline-item__icon\",class:e.icon}):e._e()]),e.$slots.dot?i(\"div\",{staticClass:\"el-timeline-item__dot\"},[e._t(\"dot\")],2):e._e(),i(\"div\",{staticClass:\"el-timeline-item__wrapper\"},[e.hideTimestamp||\"top\"!==e.placement?e._e():i(\"div\",{staticClass:\"el-timeline-item__timestamp is-top\"},[e._v(\"\\n \"+e._s(e.timestamp)+\"\\n \")]),i(\"div\",{staticClass:\"el-timeline-item__content\"},[e._t(\"default\")],2),e.hideTimestamp||\"bottom\"!==e.placement?e._e():i(\"div\",{staticClass:\"el-timeline-item__timestamp is-bottom\"},[e._v(\"\\n \"+e._s(e.timestamp)+\"\\n \")])])])};kh._withStripped=!0;var Sh=r({name:\"ElTimelineItem\",inject:[\"timeline\"],props:{timestamp:String,hideTimestamp:{type:Boolean,default:!1},placement:{type:String,default:\"bottom\"},type:String,color:String,size:{type:String,default:\"normal\"},icon:String}},kh,[],!1,null,null,null);Sh.options.__file=\"packages/timeline/src/item.vue\";var Dh=Sh.exports;Dh.install=function(e){e.component(Dh.name,Dh)};var $h=Dh,Eh=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"a\",e._b({class:[\"el-link\",e.type?\"el-link--\"+e.type:\"\",e.disabled&&\"is-disabled\",e.underline&&!e.disabled&&\"is-underline\"],attrs:{href:e.disabled?null:e.href},on:{click:e.handleClick}},\"a\",e.$attrs,!1),[e.icon?i(\"i\",{class:e.icon}):e._e(),e.$slots.default?i(\"span\",{staticClass:\"el-link--inner\"},[e._t(\"default\")],2):e._e(),e.$slots.icon?[e.$slots.icon?e._t(\"icon\"):e._e()]:e._e()],2)};Eh._withStripped=!0;var Th=r({name:\"ElLink\",props:{type:{type:String,default:\"default\"},underline:{type:Boolean,default:!0},disabled:Boolean,href:String,icon:String},methods:{handleClick:function(e){this.disabled||this.href||this.$emit(\"click\",e)}}},Eh,[],!1,null,null,null);Th.options.__file=\"packages/link/src/main.vue\";var Mh=Th.exports;Mh.install=function(e){e.component(Mh.name,Mh)};var Nh=Mh,Ph=function(e,t){var i=t._c;return i(\"div\",t._g(t._b({class:[t.data.staticClass,\"el-divider\",\"el-divider--\"+t.props.direction]},\"div\",t.data.attrs,!1),t.listeners),[t.slots().default&&\"vertical\"!==t.props.direction?i(\"div\",{class:[\"el-divider__text\",\"is-\"+t.props.contentPosition]},[t._t(\"default\")],2):t._e()])};Ph._withStripped=!0;var Oh=r({name:\"ElDivider\",props:{direction:{type:String,default:\"horizontal\",validator:function(e){return-1!==[\"horizontal\",\"vertical\"].indexOf(e)}},contentPosition:{type:String,default:\"center\",validator:function(e){return-1!==[\"left\",\"center\",\"right\"].indexOf(e)}}}},Ph,[],!0,null,null,null);Oh.options.__file=\"packages/divider/src/main.vue\";var Ih=Oh.exports;Ih.install=function(e){e.component(Ih.name,Ih)};var Ah=Ih,Fh=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-image\"},[e.loading?e._t(\"placeholder\",[i(\"div\",{staticClass:\"el-image__placeholder\"})]):e.error?e._t(\"error\",[i(\"div\",{staticClass:\"el-image__error\"},[e._v(e._s(e.t(\"el.image.error\")))])]):i(\"img\",e._g(e._b({staticClass:\"el-image__inner\",class:{\"el-image__inner--center\":e.alignCenter,\"el-image__preview\":e.preview},style:e.imageStyle,attrs:{src:e.src},on:{click:e.clickHandler}},\"img\",e.$attrs,!1),e.$listeners)),e.preview?[e.showViewer?i(\"image-viewer\",{attrs:{\"z-index\":e.zIndex,\"initial-index\":e.imageIndex,\"on-close\":e.closeViewer,\"url-list\":e.previewSrcList}}):e._e()]:e._e()],2)};Fh._withStripped=!0;var Lh=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition\",{attrs:{name:\"viewer-fade\"}},[i(\"div\",{ref:\"el-image-viewer__wrapper\",staticClass:\"el-image-viewer__wrapper\",style:{\"z-index\":e.zIndex},attrs:{tabindex:\"-1\"}},[i(\"div\",{staticClass:\"el-image-viewer__mask\"}),i(\"span\",{staticClass:\"el-image-viewer__btn el-image-viewer__close\",on:{click:e.hide}},[i(\"i\",{staticClass:\"el-icon-circle-close\"})]),e.isSingle?e._e():[i(\"span\",{staticClass:\"el-image-viewer__btn el-image-viewer__prev\",class:{\"is-disabled\":!e.infinite&&e.isFirst},on:{click:e.prev}},[i(\"i\",{staticClass:\"el-icon-arrow-left\"})]),i(\"span\",{staticClass:\"el-image-viewer__btn el-image-viewer__next\",class:{\"is-disabled\":!e.infinite&&e.isLast},on:{click:e.next}},[i(\"i\",{staticClass:\"el-icon-arrow-right\"})])],i(\"div\",{staticClass:\"el-image-viewer__btn el-image-viewer__actions\"},[i(\"div\",{staticClass:\"el-image-viewer__actions__inner\"},[i(\"i\",{staticClass:\"el-icon-zoom-out\",on:{click:function(t){e.handleActions(\"zoomOut\")}}}),i(\"i\",{staticClass:\"el-icon-zoom-in\",on:{click:function(t){e.handleActions(\"zoomIn\")}}}),i(\"i\",{staticClass:\"el-image-viewer__actions__divider\"}),i(\"i\",{class:e.mode.icon,on:{click:e.toggleMode}}),i(\"i\",{staticClass:\"el-image-viewer__actions__divider\"}),i(\"i\",{staticClass:\"el-icon-refresh-left\",on:{click:function(t){e.handleActions(\"anticlocelise\")}}}),i(\"i\",{staticClass:\"el-icon-refresh-right\",on:{click:function(t){e.handleActions(\"clocelise\")}}})])]),i(\"div\",{staticClass:\"el-image-viewer__canvas\"},e._l(e.urlList,function(t,n){return n===e.index?i(\"img\",{key:t,ref:\"img\",refInFor:!0,staticClass:\"el-image-viewer__img\",style:e.imgStyle,attrs:{src:e.currentImg},on:{load:e.handleImgLoad,error:e.handleImgError,mousedown:e.handleMouseDown}}):e._e()}),0)],2)])};Lh._withStripped=!0;var Vh=Object.assign||function(e){for(var t=1;t0?e.handleActions(\"zoomIn\",{zoomRate:.015,enableTransition:!1}):e.handleActions(\"zoomOut\",{zoomRate:.015,enableTransition:!1})}),he(document,\"keydown\",this._keyDownHandler),he(document,zh,this._mouseWheelHandler)},deviceSupportUninstall:function(){de(document,\"keydown\",this._keyDownHandler),de(document,zh,this._mouseWheelHandler),this._keyDownHandler=null,this._mouseWheelHandler=null},handleImgLoad:function(e){this.loading=!1},handleImgError:function(e){this.loading=!1,e.target.alt=\"加载失败\"},handleMouseDown:function(e){var t=this;if(!this.loading&&0===e.button){var i=this.transform,n=i.offsetX,r=i.offsetY,s=e.pageX,a=e.pageY;this._dragHandler=F(function(e){t.transform.offsetX=n+e.pageX-s,t.transform.offsetY=r+e.pageY-a}),he(document,\"mousemove\",this._dragHandler),he(document,\"mouseup\",function(e){de(document,\"mousemove\",t._dragHandler)}),e.preventDefault()}},reset:function(){this.transform={scale:1,deg:0,offsetX:0,offsetY:0,enableTransition:!1}},toggleMode:function(){if(!this.loading){var e=Object.keys(Bh),t=(Object.values(Bh).indexOf(this.mode)+1)%e.length;this.mode=Bh[e[t]],this.reset()}},prev:function(){if(!this.isFirst||this.infinite){var e=this.urlList.length;this.index=(this.index-1+e)%e}},next:function(){if(!this.isLast||this.infinite){var e=this.urlList.length;this.index=(this.index+1)%e}},handleActions:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!this.loading){var i=Vh({zoomRate:.2,rotateDeg:90,enableTransition:!0},t),n=i.zoomRate,r=i.rotateDeg,s=i.enableTransition,a=this.transform;switch(e){case\"zoomOut\":a.scale>.2&&(a.scale=parseFloat((a.scale-n).toFixed(3)));break;case\"zoomIn\":a.scale=parseFloat((a.scale+n).toFixed(3));break;case\"clocelise\":a.deg+=r;break;case\"anticlocelise\":a.deg-=r}a.enableTransition=s}}},mounted:function(){this.deviceSupportInstall(),this.$refs[\"el-image-viewer__wrapper\"].focus()}},Lh,[],!1,null,null,null);Hh.options.__file=\"packages/image/src/image-viewer.vue\";var Rh=Hh.exports,Wh=function(){return void 0!==document.documentElement.style.objectFit},jh=\"none\",qh=\"contain\",Yh=\"cover\",Kh=\"fill\",Gh=\"scale-down\",Uh=\"\",Xh=r({name:\"ElImage\",mixins:[q],inheritAttrs:!1,components:{ImageViewer:Rh},props:{src:String,fit:String,lazy:Boolean,scrollContainer:{},previewSrcList:{type:Array,default:function(){return[]}},zIndex:{type:Number,default:2e3}},data:function(){return{loading:!0,error:!1,show:!this.lazy,imageWidth:0,imageHeight:0,showViewer:!1}},computed:{imageStyle:function(){var e=this.fit;return!this.$isServer&&e?Wh()?{\"object-fit\":e}:this.getImageStyle(e):{}},alignCenter:function(){return!this.$isServer&&!Wh()&&this.fit!==Kh},preview:function(){var e=this.previewSrcList;return Array.isArray(e)&&e.length>0},imageIndex:function(){var e=0,t=this.previewSrcList.indexOf(this.src);return t>=0&&(e=t),e}},watch:{src:function(e){this.show&&this.loadImage()},show:function(e){e&&this.loadImage()}},mounted:function(){this.lazy?this.addLazyLoadListener():this.loadImage()},beforeDestroy:function(){this.lazy&&this.removeLazyLoadListener()},methods:{loadImage:function(){var e=this;if(!this.$isServer){this.loading=!0,this.error=!1;var t=new Image;t.onload=function(i){return e.handleLoad(i,t)},t.onerror=this.handleError.bind(this),Object.keys(this.$attrs).forEach(function(i){var n=e.$attrs[i];t.setAttribute(i,n)}),t.src=this.src}},handleLoad:function(e,t){this.imageWidth=t.width,this.imageHeight=t.height,this.loading=!1,this.error=!1},handleError:function(e){this.loading=!1,this.error=!0,this.$emit(\"error\",e)},handleLazyLoad:function(){(function(e,t){if(se||!e||!t)return!1;var i=e.getBoundingClientRect(),n=void 0;return n=[window,document,document.documentElement,null,void 0].includes(t)?{top:0,right:window.innerWidth,bottom:window.innerHeight,left:0}:t.getBoundingClientRect(),i.topn.top&&i.right>n.left&&i.leftr)return console.warn(\"[ElementCalendar]end time should be greater than start time\"),[];if(Ir(n,r))return[[n,r]];var s=[],a=new Date(n.getFullYear(),n.getMonth()+1,1),o=this.toDate(a.getTime()-864e5);if(!Ir(a,r))return console.warn(\"[ElementCalendar]start time and end time interval must not exceed two months\"),[];s.push([n,o]);var l=this.realFirstDayOfWeek,u=a.getDay(),c=0;return u!==l&&(c=0===l?7-u:(c=l-u)>0?c:7+c),(a=this.toDate(a.getTime()+864e5*c)).getDate()6?0:Math.floor(this.firstDayOfWeek)}},data:function(){return{selectedDay:\"\",now:new Date}}},Qh,[],!1,null,null,null);rd.options.__file=\"packages/calendar/src/main.vue\";var sd=rd.exports;sd.install=function(e){e.component(sd.name,sd)};var ad=sd,od=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition\",{attrs:{name:\"el-fade-in\"}},[e.visible?i(\"div\",{staticClass:\"el-backtop\",style:{right:e.styleRight,bottom:e.styleBottom},on:{click:function(t){return t.stopPropagation(),e.handleClick(t)}}},[e._t(\"default\",[i(\"el-icon\",{attrs:{name:\"caret-top\"}})])],2):e._e()])};od._withStripped=!0;var ld=function(e){return Math.pow(e,3)},ud=r({name:\"ElBacktop\",props:{visibilityHeight:{type:Number,default:200},target:[String],right:{type:Number,default:40},bottom:{type:Number,default:40}},data:function(){return{el:null,container:null,visible:!1}},computed:{styleBottom:function(){return this.bottom+\"px\"},styleRight:function(){return this.right+\"px\"}},mounted:function(){this.init(),this.throttledScrollHandler=Mu()(300,this.onScroll),this.container.addEventListener(\"scroll\",this.throttledScrollHandler)},methods:{init:function(){if(this.container=document,this.el=document.documentElement,this.target){if(this.el=document.querySelector(this.target),!this.el)throw new Error(\"target is not existed: \"+this.target);this.container=this.el}},onScroll:function(){var e=this.el.scrollTop;this.visible=e>=this.visibilityHeight},handleClick:function(e){this.scrollToTop(),this.$emit(\"click\",e)},scrollToTop:function(){var e=this.el,t=Date.now(),i=e.scrollTop,n=window.requestAnimationFrame||function(e){return setTimeout(e,16)};n(function r(){var s,a=(Date.now()-t)/500;a<1?(e.scrollTop=i*(1-((s=a)<.5?ld(2*s)/2:1-ld(2*(1-s))/2)),n(r)):e.scrollTop=0})}},beforeDestroy:function(){this.container.removeEventListener(\"scroll\",this.throttledScrollHandler)}},od,[],!1,null,null,null);ud.options.__file=\"packages/backtop/src/main.vue\";var cd=ud.exports;cd.install=function(e){e.component(cd.name,cd)};var hd=cd,dd=function(e,t){return e===window||e===document?document.documentElement[t]:e[t]},pd=function(e){return dd(e,\"offsetHeight\")},fd=\"ElInfiniteScroll\",md={delay:{type:Number,default:200},distance:{type:Number,default:0},disabled:{type:Boolean,default:!1},immediate:{type:Boolean,default:!0}},vd=function(e,t){return v(e)?(i=md,Object.keys(i||{}).map(function(e){return[e,i[e]]})).reduce(function(i,n){var r=n[0],s=n[1],a=s.type,o=s.default,l=e.getAttribute(\"infinite-scroll-\"+r);switch(l=b(t[l])?l:t[l],a){case Number:l=Number(l),l=Number.isNaN(l)?o:l;break;case Boolean:l=null!=l?\"false\"!==l&&Boolean(l):o;break;default:l=a(l)}return i[r]=l,i},{}):{};var i},gd=function(e){return e.getBoundingClientRect().top},bd=function(e){var t=this[fd],i=t.el,n=t.vm,r=t.container,s=t.observer,a=vd(i,n),o=a.distance;if(!a.disabled){var l=r.getBoundingClientRect();if(l.width||l.height){var u=!1;if(r===i){var c=r.scrollTop+function(e){return dd(e,\"clientHeight\")}(r);u=r.scrollHeight-c<=o}else{u=pd(i)+gd(i)-gd(r)-pd(r)+Number.parseFloat(function(e,t){if(e===window&&(e=document.documentElement),1!==e.nodeType)return[];var i=window.getComputedStyle(e,null);return t?i[t]:i}(r,\"borderBottomWidth\"))<=o}u&&g(e)?e.call(n):s&&(s.disconnect(),this[fd].observer=null)}}},yd={name:\"InfiniteScroll\",inserted:function(e,t,i){var n=t.value,r=i.context,s=be(e,!0),a=vd(e,r),o=a.delay,l=a.immediate,u=et()(o,bd.bind(e,n));(e[fd]={el:e,vm:r,container:s,onScroll:u},s)&&(s.addEventListener(\"scroll\",u),l&&((e[fd].observer=new MutationObserver(u)).observe(s,{childList:!0,subtree:!0}),u()))},unbind:function(e){var t=e[fd],i=t.container,n=t.onScroll;i&&i.removeEventListener(\"scroll\",n)},install:function(e){e.directive(yd.name,yd)}},wd=yd,_d=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"div\",{staticClass:\"el-page-header\"},[i(\"div\",{staticClass:\"el-page-header__left\",on:{click:function(t){e.$emit(\"back\")}}},[i(\"i\",{staticClass:\"el-icon-back\"}),i(\"div\",{staticClass:\"el-page-header__title\"},[e._t(\"title\",[e._v(e._s(e.title))])],2)]),i(\"div\",{staticClass:\"el-page-header__content\"},[e._t(\"content\",[e._v(e._s(e.content))])],2)])};_d._withStripped=!0;var xd=r({name:\"ElPageHeader\",props:{title:{type:String,default:function(){return W(\"el.pageHeader.title\")}},content:String}},_d,[],!1,null,null,null);xd.options.__file=\"packages/page-header/src/main.vue\";var Cd=xd.exports;Cd.install=function(e){e.component(Cd.name,Cd)};var kd=Cd,Sd=r({name:\"ElAvatar\",props:{size:{type:[Number,String],validator:function(e){return\"string\"==typeof e?[\"large\",\"medium\",\"small\"].includes(e):\"number\"==typeof e}},shape:{type:String,default:\"circle\",validator:function(e){return[\"circle\",\"square\"].includes(e)}},icon:String,src:String,alt:String,srcSet:String,error:Function,fit:{type:String,default:\"cover\"}},data:function(){return{isImageExist:!0}},computed:{avatarClass:function(){var e=this.size,t=this.icon,i=this.shape,n=[\"el-avatar\"];return e&&\"string\"==typeof e&&n.push(\"el-avatar--\"+e),t&&n.push(\"el-avatar--icon\"),i&&n.push(\"el-avatar--\"+i),n.join(\" \")}},methods:{handleError:function(){var e=this.error;!1!==(e?e():void 0)&&(this.isImageExist=!1)},renderAvatar:function(){var e=this.$createElement,t=this.icon,i=this.src,n=this.alt,r=this.isImageExist,s=this.srcSet,a=this.fit;return r&&i?e(\"img\",{attrs:{src:i,alt:n,srcSet:s},on:{error:this.handleError},style:{\"object-fit\":a}}):t?e(\"i\",{class:t}):this.$slots.default}},render:function(){var e=arguments[0],t=this.avatarClass,i=this.size;return e(\"span\",{class:t,style:\"number\"==typeof i?{height:i+\"px\",width:i+\"px\",lineHeight:i+\"px\"}:{}},[this.renderAvatar()])}},void 0,void 0,!1,null,null,null);Sd.options.__file=\"packages/avatar/src/main.vue\";var Dd=Sd.exports;Dd.install=function(e){e.component(Dd.name,Dd)};var $d=Dd,Ed=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"transition\",{attrs:{name:\"el-drawer-fade\"},on:{\"after-enter\":e.afterEnter,\"after-leave\":e.afterLeave}},[i(\"div\",{directives:[{name:\"show\",rawName:\"v-show\",value:e.visible,expression:\"visible\"}],staticClass:\"el-drawer__wrapper\",attrs:{tabindex:\"-1\"}},[i(\"div\",{staticClass:\"el-drawer__container\",class:e.visible&&\"el-drawer__open\",attrs:{role:\"document\",tabindex:\"-1\"},on:{click:function(t){return t.target!==t.currentTarget?null:e.handleWrapperClick(t)}}},[i(\"div\",{ref:\"drawer\",staticClass:\"el-drawer\",class:[e.direction,e.customClass],style:e.isHorizontal?\"width: \"+e.size:\"height: \"+e.size,attrs:{\"aria-modal\":\"true\",\"aria-labelledby\":\"el-drawer__title\",\"aria-label\":e.title,role:\"dialog\",tabindex:\"-1\"}},[e.withHeader?i(\"header\",{staticClass:\"el-drawer__header\",attrs:{id:\"el-drawer__title\"}},[e._t(\"title\",[i(\"span\",{attrs:{role:\"heading\",tabindex:\"0\",title:e.title}},[e._v(e._s(e.title))])]),e.showClose?i(\"button\",{staticClass:\"el-drawer__close-btn\",attrs:{\"aria-label\":\"close \"+(e.title||\"drawer\"),type:\"button\"},on:{click:e.closeDrawer}},[i(\"i\",{staticClass:\"el-dialog__close el-icon el-icon-close\"})]):e._e()],2):e._e(),e.rendered?i(\"section\",{staticClass:\"el-drawer__body\"},[e._t(\"default\")],2):e._e()])])])])};Ed._withStripped=!0;var Td=r({name:\"ElDrawer\",mixins:[Me,l],props:{appendToBody:{type:Boolean,default:!1},beforeClose:{type:Function},customClass:{type:String,default:\"\"},closeOnPressEscape:{type:Boolean,default:!0},destroyOnClose:{type:Boolean,default:!1},modal:{type:Boolean,default:!0},direction:{type:String,default:\"rtl\",validator:function(e){return-1!==[\"ltr\",\"rtl\",\"ttb\",\"btt\"].indexOf(e)}},modalAppendToBody:{type:Boolean,default:!0},showClose:{type:Boolean,default:!0},size:{type:String,default:\"30%\"},title:{type:String,default:\"\"},visible:{type:Boolean},wrapperClosable:{type:Boolean,default:!0},withHeader:{type:Boolean,default:!0}},computed:{isHorizontal:function(){return\"rtl\"===this.direction||\"ltr\"===this.direction}},data:function(){return{closed:!1,prevActiveElement:null}},watch:{visible:function(e){var t=this;e?(this.closed=!1,this.$emit(\"open\"),this.appendToBody&&document.body.appendChild(this.$el),this.prevActiveElement=document.activeElement,this.$nextTick(function(){qt.focusFirstDescendant(t.$refs.drawer)})):(this.closed||this.$emit(\"close\"),this.$nextTick(function(){t.prevActiveElement&&t.prevActiveElement.focus()}))}},methods:{afterEnter:function(){this.$emit(\"opened\")},afterLeave:function(){this.$emit(\"closed\")},hide:function(e){!1!==e&&(this.$emit(\"update:visible\",!1),this.$emit(\"close\"),!0===this.destroyOnClose&&(this.rendered=!1),this.closed=!0)},handleWrapperClick:function(){this.wrapperClosable&&this.closeDrawer()},closeDrawer:function(){\"function\"==typeof this.beforeClose?this.beforeClose(this.hide):this.hide()},handleClose:function(){this.closeDrawer()}},mounted:function(){this.visible&&(this.rendered=!0,this.open())},destroyed:function(){this.appendToBody&&this.$el&&this.$el.parentNode&&this.$el.parentNode.removeChild(this.$el)}},Ed,[],!1,null,null,null);Td.options.__file=\"packages/drawer/src/main.vue\";var Md=Td.exports;Md.install=function(e){e.component(Md.name,Md)};var Nd=Md,Pd=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i(\"el-popover\",e._b({attrs:{trigger:\"click\"},model:{value:e.visible,callback:function(t){e.visible=t},expression:\"visible\"}},\"el-popover\",e.$attrs,!1),[i(\"div\",{staticClass:\"el-popconfirm\"},[i(\"p\",{staticClass:\"el-popconfirm__main\"},[e.hideIcon?e._e():i(\"i\",{staticClass:\"el-popconfirm__icon\",class:e.icon,style:{color:e.iconColor}}),e._v(\"\\n \"+e._s(e.title)+\"\\n \")]),i(\"div\",{staticClass:\"el-popconfirm__action\"},[i(\"el-button\",{attrs:{size:\"mini\",type:e.cancelButtonType},on:{click:e.cancel}},[e._v(\"\\n \"+e._s(e.cancelButtonText)+\"\\n \")]),i(\"el-button\",{attrs:{size:\"mini\",type:e.confirmButtonType},on:{click:e.confirm}},[e._v(\"\\n \"+e._s(e.confirmButtonText)+\"\\n \")])],1)]),e._t(\"reference\",null,{slot:\"reference\"})],2)};Pd._withStripped=!0;var Od=r({name:\"ElPopconfirm\",props:{title:{type:String},confirmButtonText:{type:String,default:W(\"el.popconfirm.confirmButtonText\")},cancelButtonText:{type:String,default:W(\"el.popconfirm.cancelButtonText\")},confirmButtonType:{type:String,default:\"primary\"},cancelButtonType:{type:String,default:\"text\"},icon:{type:String,default:\"el-icon-question\"},iconColor:{type:String,default:\"#f90\"},hideIcon:{type:Boolean,default:!1}},components:{ElPopover:Zs,ElButton:Et},data:function(){return{visible:!1}},methods:{confirm:function(){ this.visible=!1,this.$emit(\"on-confirm\")},cancel:function(){this.visible=!1,this.$emit(\"on-cancel\")}}},Pd,[],!1,null,null,null);Od.options.__file=\"packages/popconfirm/src/main.vue\";var Id=Od.exports;Id.install=function(e){e.component(Id.name,Id)};var Ad=Id,Fd=[pt,gt,kt,At,Bt,Wt,ei,ai,di,vi,ne,_i,Si,Mi,Ii,Vi,Ri,Yi,Xi,ct,ht,en,Et,Pt,Un,ir,Ts,Ls,Ys,Zs,ui,Ca,$a,Na,uo,yo,Co,Re,zo,qo,ul,Sl,$l,Ml,Kl,Al,Jl,hu,mu,yu,Cu,$u,Ou,Ze,Lu,Hu,qu,bc,Gc,eh,rh,lh,dh,vh,wh,Ch,$h,Nh,Ah,Zh,ad,hd,kd,hc,$d,Nd,Ad,ii],Ld=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};j.use(t.locale),j.i18n(t.i18n),Fd.forEach(function(t){e.component(t.name,t)}),e.use(wd),e.use(_l.directive),e.prototype.$ELEMENT={size:t.size||\"\",zIndex:t.zIndex||2e3},e.prototype.$loading=_l.service,e.prototype.$msgbox=ya,e.prototype.$alert=ya.alert,e.prototype.$confirm=ya.confirm,e.prototype.$prompt=ya.prompt,e.prototype.$notify=tl,e.prototype.$message=ou};\"undefined\"!=typeof window&&window.Vue&&Ld(window.Vue);t.default={version:\"2.13.2\",locale:j.use,i18n:j.i18n,install:Ld,CollapseTransition:ii,Loading:_l,Pagination:pt,Dialog:gt,Autocomplete:kt,Dropdown:At,DropdownMenu:Bt,DropdownItem:Wt,Menu:ei,Submenu:ai,MenuItem:di,MenuItemGroup:vi,Input:ne,InputNumber:_i,Radio:Si,RadioGroup:Mi,RadioButton:Ii,Checkbox:Vi,CheckboxButton:Ri,CheckboxGroup:Yi,Switch:Xi,Select:ct,Option:ht,OptionGroup:en,Button:Et,ButtonGroup:Pt,Table:Un,TableColumn:ir,DatePicker:Ts,TimeSelect:Ls,TimePicker:Ys,Popover:Zs,Tooltip:ui,MessageBox:ya,Breadcrumb:Ca,BreadcrumbItem:$a,Form:Na,FormItem:uo,Tabs:yo,TabPane:Co,Tag:Re,Tree:zo,Alert:qo,Notification:tl,Slider:ul,Icon:Sl,Row:$l,Col:Ml,Upload:Kl,Progress:Al,Spinner:Jl,Message:ou,Badge:hu,Card:mu,Rate:yu,Steps:Cu,Step:$u,Carousel:Ou,Scrollbar:Ze,CarouselItem:Lu,Collapse:Hu,CollapseItem:qu,Cascader:bc,ColorPicker:Gc,Transfer:eh,Container:rh,Header:lh,Aside:dh,Main:vh,Footer:wh,Timeline:Ch,TimelineItem:$h,Link:Nh,Divider:Ah,Image:Zh,Calendar:ad,Backtop:hd,InfiniteScroll:wd,PageHeader:kd,CascaderPanel:hc,Avatar:$d,Drawer:Nd,Popconfirm:Ad}}]).default});", "/ 管理员模块": "/admin module", "一级导航": "First level navigation", "点击系统管理": "Click System Management", "/ 添加管理员": "/Add administrator", "保存按钮": "save button", "/ 设置角色": "/set role", "选中的数量,至少选择一个": "Selected quantity, select at least one", "重复点击问题": "Repeated click issue", "/ 填充管理人员表单": "/ Populate the admin form", "/ UserId和UserName的映射关系": "/ Mapping relationship between UserId and UserName", "/ 根据判断条件获取User": "/ Get User based on judgment conditions", "TODO:需要使用分布式缓存": "TODO: Need to use distributed cache", "需要更新对象到Redis": "Need to update objects to Redis", "未命中,查找数据库": "Missed, search database", "/ 是否被锁定(无法登陆) TODO:暂未添加到DTO中": "/ Whether it is locked (unable to log in) TODO: Not added to DTO yet", "/ 用户信息": "/user information", "/ 用户支付日志": "/User payment log", "/ 用户积分日志": "/User Points Log", "使用 [XncfAutoConfigurationMapping] 可自动执行": "Use [XncfAutoConfigurationMapping] to automate", "/ 用户所关联的角色": "/ role associated with user", "/// 最近一次活动的客户端设备信息": "/// Client device information for the most recent activity", "/ 强制退出登录": "/ Force logout", "/ 未读消息数量": "/Number of unread messages", "/ 已经输入过验证码(如果需要加强验证,可以加上次输入验证码的时间以及token)": "/ The verification code has been entered (if you need to strengthen verification, you can add the time when the verification code was entered last time and the token)", "/ 在线图标": "/ online icon", "/ 账户显示名称": "/Account display name", "/ 2、运行:PM> add-migration [更新名称] -c AccountSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ": "/ 2. Run: PM> add-migration [update name] -c AccountSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ", "/ 2、运行:PM> add-migration [更新名称] -c AccountSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ": "/ 2. Run: PM> add-migration [update name] -c AccountSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ", "/ 2、运行:PM> add-migration [更新名称] -c AccountSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ": "/ 2. Run: PM> add-migration [update name] -c AccountSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ", "/ 2、运行:PM> add-migration [更新名称] -c AccountSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ": "/ 2. Run: PM> add-migration [update name] -c AccountSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ", "/ 2、运行:PM> add-migration [更新名称] -c AccountSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ": "/ 2. Run: PM> add-migration [update name] -c AccountSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ", "/ 2、运行:PM> add-migration [更新名称] -c AccountSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ": "/ 2. Run: PM> add-migration [update name] -c AccountSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ", "/ 列队数据集合": "/Queue data collection", "/ 同步执行锁": "/ Synchronous execution lock", "/ 立即同步所有缓存执行锁(给OperateQueue()使用)": "/ Immediately synchronize all cache execution locks (used by OperateQueue())", "/ 生成Key": "/ Generate Key", "/ 列队应用名称,如“ContainerBag”": "/ Queue application name, such as \"ContainerBag\"", "/ 操作对象类型": "/ Operation object type", "/ 对象唯一标识Key": "/ Object unique identification Key", "/ 操作名称,如“UpdateContainerBag”": "/ Action name, such as \"UpdateContainerBag\"", "/ 操作列队": "/Operation queue", "获取最新的Key": "Get the latest Key", "获取任务项": "Get task items", "识别类型": "Identification type", "清除": "Clear", "/ 获取当前等待执行的Key": "/ Get the Key currently waiting for execution", "/ 获取OperationQueueItem": "/ Get OperationQueueItem", "/ 添加列队成员": "/Add queue members", "MessageQueueList.Add(key);//移动到末尾": "MessageQueueList.Add(key);//Move to the end", "/ 移除列队成员": "/Remove queue members", "/ 获得当前列队数量": "/ Get the current queue number", "/ 操作列队项": "/Operation queue item", "/ 列队项唯一标识": "/ Queue item unique identifier", "/ 列队项目命中触发时执行的委托": "/ Delegation executed when the queue item is hit and triggered", "/ 此实例对象的创建时间": "/ The creation time of this instance object", "/ 储存数据": "/save data", "/ 项目说明(主要用于调试)": "/Project description (mainly used for debugging)", "/ 初始化SenparcMessageQueue消息列队项": "/ Initialize SenparcMessageQueue message queue items", "/ 下载图片到指定文件": "/ Download the image to the specified file", "/ 更新用户头像": "/ Update user avatar", "下载图片": "Download pictures", "测试耗时:4226.5869ms": "Test time: 4226.5869ms", "不使用七牛或其他云储存,就使用本地路径": "Do not use Qiniu or other cloud storage, just use local path", "Add的时候会自动生成": "It will be automatically generated when adding", "throw new Exception($\"支付回调失败【{ex.Message}】\");": "throw new Exception($\"Payment callback failed [{ex.Message}]\");", "注意:": "Notice:", "* 为了兼容emoji表情,使用NickName字段前需要进行一次UrlDecode(Encoding.UTF8),": "* In order to be compatible with emoji expressions, UrlDecode (Encoding.UTF8) needs to be performed before using the NickName field.", "* 存入NickName之前,对原始字符串(如直接从接口获得)进行一次UrlEncode(Encoding.UTF8)": "* Before storing NickName, perform UrlEncode (Encoding.UTF8) on the original string (such as obtained directly from the interface)", "/ 检验用户名是否存在": "/ Check if username exists", "/ 检测密码是否正确": "/ Check if the password is correct", "/ 修改密码": "/ change password", "/ 改变用户基本信息": "/ Change basic user information", "/ 检查手机号是否存在": "/ Check if the mobile phone number exists", "/ 根据用户名获取账号信息": "/ Get account information based on username", "FormsAuthentication.SignOut(); //退出网站登录": "FormsAuthentication.SignOut(); //Log out of website login", "继续删除其他登陆信息": "Continue to delete other login information", "FormsAuthentication.SetAuthCookie(userName, rememberMe);//.net core 的方法": "FormsAuthentication.SetAuthCookie(userName, rememberMe);//.net core method", "保存User信息,同时会清除FullAccount信息,顺便保证“强制退出”等参数失效。": "Saving the User information will also clear the FullAccount information and ensure that parameters such as \"Force Exit\" are invalid.", "/ 自动生成新的用户名": "/ Automatically generate new username", "/ 登陆": "/login", "微信方法,分离到 XNCF": "WeChat method, separated into XNCF", "/// 从weixin.senparc.com获取到的用户信息,对应添加Account": "/// User information obtained from weixin.senparc.com, corresponding to add Account", "LogUtility.Account.Error($\"userInfo.P2PData 中的 P2PData可能为Null,userInfo:{userInfo?.ToJson()}\");": "LogUtility.Account.Error($\"P2PData in userInfo.P2PData may be Null, userInfo: {userInfo?.ToJson()}\");", "if (fullAccount.UserName == \"JeffreySu\")//TODO:这种情况只会存在于老用户,UnionId未同步过来,如accountId=31,name=zhensherlock": "if (fullAccount.UserName == \"JeffreySu\")//TODO: This situation will only exist for old users, and the UnionId has not been synchronized, such as accountId=31, name=zhensherlock", "account.WeixinUnionId = userInfo.unionid;//更新UnionId": "account.WeixinUnionId = userInfo.unionid;//Update UnionId", "account.WeixinOpenId = userInfo.openid;//更新OpenId": "account.WeixinOpenId = userInfo.openid;//Update OpenId", "//下载图片": "//Download pictures", "/// 下载图片到指定文件": "/// Download the image to the specified file", "LogUtility.SystemLogger.Debug($\"开始创建用户(Account),OAuthUserInfo:\\r\\n{userInfo.ToJson()}\");": "LogUtility.SystemLogger.Debug($\"Start creating user (Account), OAuthUserInfo:\\r\\n{userInfo.ToJson()}\");", "account.PicUrl = url; //暂时存为远程地址,让线程异步更新和下载": "account.PicUrl = url; //Temporarily save as a remote address to allow the thread to update and download asynchronously", "LogUtility.SystemLogger.Debug($\"进行异步头像更新:{userInfo.headimgurl}\");": "LogUtility.SystemLogger.Debug($\"Perform asynchronous avatar update: {userInfo.headimgurl}\");", "//异步下载图片": "//Download pictures asynchronously", "operationQueue.Add($\"{account.Id}-{DateTime.Now.Ticks.ToString()}\", OperationQueueType.更新用户头像, new List() { account.Id, userInfo.headimgurl });": "operationQueue.Add($\"{account.Id}-{DateTime.Now.Ticks.ToString()}\", OperationQueueType.Update user avatar, new List() { account.Id, userInfo.headimgurl });", "//删除图片": "//delete picture", "TODO:提供异步方法": "TODO: Provide asynchronous methods", "积分为0则不更改": "If the points are 0, no changes will be made.", "TODO 抛出异常待定": "TODO throws an exception to be determined", "删除Account缓存": "Delete Account cache", "如果不需要启用 Areas,可以只使用 services.StartEngine() 方法": "If you don't need to enable Areas, you can just use the services.StartEngine() method", "指定数据库(必须)": "Specify database (required)", "不可修改": "Cannot be modified", "注册 User 登录策略": "Register User Login Policy", "安装启动时": "When installation starts", "展开高级选项时": "When expanding advanced options", "安装成功时": "When the installation is successful", "不使用基类,因为无法通过已安装程序自动检测": "No base class is used because it cannot be automatically detected by installed programs", "/ 系统名称": "/system name", "/ 管理员用户名": "/admin username", "/ 管理员密码": "/admin password", "/ 数据库连接字符串": "/ database connection string", "/ 需要修改的命名空间": "/ Namespace that needs to be modified", "/ 需要安装的模块": "/ Modules that need to be installed", "/ 新创建的 RequestTenantInfo": "/ Newly created RequestTenantInfo", "强制本地安装": "Force local installation", "//判断是不是从新的域名进入": "//Determine whether to enter from a new domain name", "var created = await database.EnsureCreatedAsync();//尝试创建数据库": "var created = await database.EnsureCreatedAsync();//Try to create a database", "await Console.Out.WriteLineAsync(\"尝试创建数据库:\" + (created ? \"成功创建\" : \"已存在,无需创建\"));": "await Console.Out.WriteLineAsync(\"Attempt to create database:\" + (created ? \"Successfully created\" : \"Already exists, no need to create\"));", "初始化页面显示的配置项的默认值": "Default values ​​of configuration items displayed on the initialization page", "已经安装完毕,且存在管理员则不进行安装": "If the installation has been completed and there is an administrator, the installation will not proceed.", "开始安装": "Start installation", "添加初始化多租户信息": "Add initial multi-tenant information", "撤销安装状态": "Undo installation status", "/ 2、运行:PM> add-migration [更新名称] -c InstallerSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ": "/ 2. Run: PM> add-migration [update name] -c InstallerSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ", "/ 2、运行:PM> add-migration [更新名称] -c InstallerSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ": "/ 2. Run: PM> add-migration [update name] -c InstallerSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ", "/ 2、运行:PM> add-migration [更新名称] -c InstallerSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ": "/ 2. Run: PM> add-migration [update name] -c InstallerSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ", "/ 2、运行:PM> add-migration [更新名称] -c InstallerSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ": "/ 2. Run: PM> add-migration [update name] -c InstallerSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ", "/ 2、运行:PM> add-migration [更新名称] -c InstallerSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ": "/ 2. Run: PM> add-migration [update name] -c InstallerSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ", "初始化Options的默认值": "Initialize the default value of Options", "/ 获取自动生成的管理员用户名称": "/ Get the automatically generated administrator user name", "/ 获取默认的系统名称": "/ Get the default system name", "/ 读取配置中目标数据库连接字符串": "/ Read the target database connection string in the configuration", "/ 读取模块名称": "/Read module name", "/ 修改配置及缓存中目标数据库连接字符串": "/ Modify the target database connection string in the configuration and cache", "清空数据库配置缓存": "Clear database configuration cache", "/ 初始化安装系统": "/Initialize the installation system", "TODO:目前实际已无效,不会构建任何数据库代码,此处仅作占位": "TODO: It is currently invalid and no database code will be built. This is just a placeholder.", "开始安装 Installer 模块": "Start installing the Installer module", "((SenparcEntities)_accountInfoService.BaseData.BaseDB.BaseDataContext).ResetMigrate();//重置合并状态": "((SenparcEntities)_accountInfoService.BaseData.BaseDB.BaseDataContext).ResetMigrate();//Reset merge status", "((SenparcEntities)_accountInfoService.BaseData.BaseDB.BaseDataContext).Migrate();//进行合并": "((SenparcEntities)_accountInfoService.BaseData.BaseDB.BaseDataContext).Migrate();//Merging", "初始化数据库": "Initialize database", "/ 检验安装请求内各项配置的值是否合法": "/ Check whether the values ​​of various configurations in the installation request are legal.", "/ 安装请求": "/ Installation request", "读取现有配置的默认值": "Read the default value of an existing configuration", "/ 执行默认包的安装命令": "/ Execute the installation command of the default package", "比对传入的和原有的数据库连接字符串": "Compare the incoming and original database connection strings", "重新建立 Service": "Re-establish the Service", "原 Get 请求": "Original Get request", "安装多租户": "Install multi-tenancy", "如果已经安装过,则不处理": "If it has already been installed, it will not be processed.", "TODO:特定的Exception": "TODO:Specific Exception", "原Post后执行": "Executed after the original Post", "初始化管理员信息": "Initialize administrator information", "进行系统初始化安装": "Perform system initialization installation", "await _xncfModuleService.InstallMenuAsync(systemRegister, Ncf.Core.Enums.InstallOrUpdate.Install);//安装菜单": "await _xncfModuleService.InstallMenuAsync(systemRegister, Ncf.Core.Enums.InstallOrUpdate.Install);//Install menu", "这里不可以使用 adminUserInfo.Password,因为此参数已经是加密信息": "adminUserInfo.Password cannot be used here because this parameter is already encrypted information", "重置租户状态": "Reset tenant status", "模型名称列表": "Model name list", "需要安装的模块列表": "List of modules that need to be installed", "模块信息数据传输对象": "Module information data transfer object", "调试信息显示": "Debugging information display", "简化数据显示测试": "Simplify data display testing", "/ 获取颜色列表(分页)": "/ Get color list (pagination)", "/ 关键词": "/ Keyword", "/ 排序字段": "/ Order field", "/ 页大小": "/ Page size", "调试信息": "debugging information", "可以根据需要添加搜索条件": "You can add search conditions as needed", "/ 创建新颜色": "/Create new color", "/ 创建颜色请求": "/ Create color request", "/ 更新颜色": "/update color", "/ 更新颜色请求": "/ Update color request", "/ 删除颜色": "/delete color", "/ 删除颜色请求": "/ Delete color request", "/ 随机化指定颜色": "/ Randomize the specified color", "/ 随机化颜色请求": "/ Randomize color request", "/ 获取颜色详情": "/ Get color details", "/ 颜色ID": "/ Color ID", "/ 创建颜色请求DTO": "/Create color request DTO", "/ 附加备注": "/Additional notes", "/ 更新颜色请求DTO": "/ Update color request DTO", "/ 删除颜色请求DTO": "/ Delete color request DTO", "/ 随机化颜色请求DTO": "/ Randomize color request DTO", "/ 2、运行:PM> add-migration [更新名称] -c Template_XncfNameSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ": "/ 2. Run: PM> add-migration [update name] -c Template_XncfNameSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm ", "/ 2、运行:PM> add-migration [更新名称] -c Template_XncfNameSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ": "/ 2. Run: PM> add-migration [update name] -c Template_XncfNameSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql ", "/ 2、运行:PM> add-migration [更新名称] -c Template_XncfNameSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ": "/ 2. Run: PM> add-migration [update name] -c Template_XncfNameSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle ", "/ 2、运行:PM> add-migration [更新名称] -c Template_XncfNameSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ": "/ 2. Run: PM> add-migration [update name] -c Template_XncfNameSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL ", "/ 2、运行:PM> add-migration [更新名称] -c Template_XncfNameSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ": "/ 2. Run: PM> add-migration [update name] -c Template_XncfNameSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite ", "/ 2、运行:PM> add-migration [更新名称] -c Template_XncfNameSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ": "/ 2. Run: PM> add-migration [update name] -c Template_XncfNameSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer ", "通用样式": "Universal style", "过滤器容器样式": "filter container style", "颜色预览样式": "Color preview style", "分页容器样式": "Pagination container style", "表格样式增强": "Table style enhancement", "颜色标签样式": "color label style", "对话框样式": "Dialog style", "滑块样式": "Slider style", "按钮样式增强": "Button style enhancement", "表单项样式": "Form item style", "加载动画样式": "Load animation style", "响应式设计": "Responsive design", "动画效果": "Animation effects", "鼠标悬停效果": "mouseover effect", "尝试多种可能的数据结构": "Try many possible data structures", "NCF框架标准格式 + 新的API格式: {data: {data: {success, message, data: {list, totalCount}}}}": "NCF framework standard format + new API format: {data: {data: {success, message, data: {list, totalCount}}}}", "简单格式: {data: {list, totalCount}}": "Simple format: {data: {list, totalCount}}", "如果data直接是数组": "If data is directly an array", "如果list在顶层": "If the list is at the top level", "数据赋值前的状态": "The state before data assignment", "数据赋值后的状态": "The state after data assignment", "强制Vue更新": "Force Vue to update", "延迟检查数据是否正确绑定": "Lazy check if data is bound correctly", "兼容NCF框架的嵌套响应格式": "Nested response format compatible with NCF framework", "使用原生JavaScript格式化日期": "Format dates using native JavaScript", "如果无法解析,返回原始值": "If it cannot be parsed, return the original value", "格式化为 YYYY-MM-DD HH:mm:ss": "Formatted as YYYY-MM-DD HH:mm:ss", "如果格式化失败,返回原始值": "If formatting fails, the original value is returned", "测试Vue响应性": "Test Vue responsiveness", "使用 PasswordSaltToken": "Use PasswordSaltToken", "不使用 PasswordSaltToken": "Not using PasswordSaltToken", "使用 PasswordSaltToken,但是未设置": "PasswordSaltToken is used, but is not set", "默认打开折叠面板": "Open accordion by default", "获取图表数据": "Get chart data", "生成图表": "Generate chart", "指定图表的配置项和数据": "Specify chart configuration items and data", "确认删除此用户吗?": "Are you sure to delete this user?", "的": "of", "所有": "all", "用户": "user", "改变颜色": "change color", "将Dapr客户端注册到服务": "Register the Dapr client to the service", "客户端配置选项": "Client configuration options", "序列化器接口的实现类型": "Implementation type of serializer interface", "监听端口": "listening port", "状态存储组件名称": "State storage component name", "发布订阅组件名称": "Publish and subscribe component name", "重连最大尝试次数": "Maximum number of reconnect attempts", "服务调用": "service call", "返回的数据类型": "Returned data type", "请求类型": "Request type", "服务名称 (App-Id)": "Service name (App-Id)", "方法路径": "method path", "请求数据": "Request data", "发布事件": "publish event", "主题名称": "Topic name", "发布内容": "Post content", "发布数据": "publish data", "获取状态": "Get status", "返回状态类型": "Return status type", "缓存键": "cache key", "保存状态": "save state", "状态类型": "status type", "缓存数据": "cache data", "保存多个状态": "Save multiple states", "删除状态": "delete status", "确认Dapr Sidecar是否正常运行": "Confirm whether Dapr Sidecar is running normally", "使用Http客户端发布消息": "Post messages using Http client", "请求消息": "request message", "返回消息": "return message", "构建请求消息": "Build request message", "调用的Dapr API类型": "Dapr API type called", "资源路径": "Resource path", "承载数据": "Carrying data", "构建请求URL": "Build request URL", "构建请求体": "Build request body", "序列化接口": "serialization interface", "序列化T为JSON字符串": "Serialize T to JSON string", "反序列化JSON字符串为T": "Deserialize JSON string to T", "反序列化JSON字符串为object": "Deserialize JSON string to object", "忽略大小写": "Ignore case", "基础类型处理通过客户端自定义重载": "Basic type handling through client-side custom overloading", "响应驼峰命名": "Respond to camelCase naming", "中文乱码": "Chinese garbled characters", "允许数组末尾多余的逗号": "Allow extra commas at end of array", "序列化JSON字符串为object": "Serialize JSON string to object", "参考如:https": "Reference such as: https", "注册当前模块需要支持的功能模块": "Register the function modules that the current module needs to support", "使用正则记录,并全局修改": "Use regular records and modify them globally", "扫描所有规则": "Scan all rules", "扫描所有文件,将满足这一条规则替换条件的对象记录下来": "Scan all files and record the objects that meet the replacement conditions of this rule.", "不能使用Split,中间可能还有空格": "Split cannot be used, there may be spaces in the middle", "也可以用IndexOf": "You can also use IndexOf", "来做": "Come and do it", "遍历所有文件,替换已经解锁出来的旧命名空间": "Traverse all files and replace the old namespace that has been unlocked", "替换旧的NameSpace": "Replace old NameSpace", "此参数不设置为属性,不需要在前端显示": "This parameter is not set as an attribute and does not need to be displayed on the front end", "提供选项": "Provide options", "注意:string": "Note: string", "类型的默认值为选项的备选值,如果没有提供备选值,此参数将别忽略": "The default value of the type is the alternative value of the option. If no alternative value is provided, this parameter will not be ignored.", "查询请求事件": "Query request event", "发送此事件来请求 MCP 模块返回可用的 Endpoints": "Send this event to request the MCP module to return available Endpoints", "事件 ID(用于追踪响应)": "Event ID (used to track responses)", "是否仅返回已启用的端点": "Whether to return only enabled endpoints", "更新事件": "Update event", "模块发送此事件通知端点的更新": "The module sends this event to notify the endpoint of updates", "端点列表 JSON(包含所有有效端点的信息)": "Endpoint list JSON (contains information about all valid endpoints)", "格式:": "Format:", "更新的端点数量": "Number of updated endpoints", "触发更新的原因": "What triggered the update", "例如": "For example", "创建事件": "Create event", "启用": "enable", "禁用事件": "Disable event", "表": "surface", "添加一些数据": "add some data", "插入第二条数据 TODO": "Insert the second piece of data TODO", "根据实体自动创建表和列": "Automatically create tables and columns based on entities", "正在进行中": "in progress", "可以等待完成": "can wait for completion", "验证初始状态": "Verify initial state", "验证存在错误信息的结果": "Verify results with error messages", "模板": "template", "聊天组": "chat group", "聊天组成员": "Chat group members", "验证 AIModel": "Verify AIModel", "验证 PromptRange": "Validate PromptRange", "验证 PromptItem": "Validate PromptItem", "验证 PromptResult": "Validate PromptResult", "验证 AgentTemplate": "Validate AgentTemplate", "验证 ChatGroup": "Verify ChatGroup", "验证 ChatGroupMember": "Verify ChatGroupMember", "测试启动聊天组": "Test launch chat group", "验证聊天组状态已更新为运行中": "Verify that the chat group status has been updated to Running", "说明:此处使用 CreateAsyncScope() 方法是由于还有其他线程在读写 ChatTask,此处缓存可能读取不到最新的更新": "Note: The CreateAsyncScope() method is used here because there are other threads reading and writing ChatTask, and the cache here may not be able to read the latest updates.", "外框颜色": "Frame color", "发光阴影颜色": "glow shadow color", "右侧图表区域卡片样式覆盖": "Right chart area card style override", "模块卡片样式优化": "Module card style optimization", "模块图标样式优化": "Module icon style optimization", "版本号样式优化": "Version number style optimization", "可升级版本的样式": "Upgradeable version of the style", "模块名称样式": "Module name style", "响应式布局优化": "Responsive layout optimization", "图表容器样式": "Chart container style", "针对包含charts-container的卡片内容区域移除内边距": "Remove padding from the card content area containing charts-container", "抵消父元素的padding,底部有足够留白": "Offset the padding of the parent element, leaving enough space at the bottom", "添加更强烈的抖动动画关键帧": "Add stronger jitter animation keyframes", "添加发光动画关键帧": "Add glow animation keyframes", "添加淡化效果动画": "Add fade effect animation", "动画类": "Animation", "数据库前缀": "Database prefix", "动态获取数据库上下文": "Dynamically obtain database context", "实现": "accomplish", "特性之后,可以自动执行,无需手动添加": "After the feature is added, it can be executed automatically without adding it manually.", "请勿移除或修改本行 - Entities Point": "Do not remove or modify this bank - Entities Point", "异步方法需要添加排序功能": "Asynchronous methods need to add sorting functionality", "更多业务方法可以写到这里": "More business methods can be written here", "的公共配置": "public configuration", "用于寻找 App": "Used to find apps", "文件夹,从而找到数据库连接字符串配置信息": "folder to find the database connection string configuration information", "设计时 DbContext 创建(仅在开发时创建 Code-First 的数据库 Migration 使用,在生产环境不会执行)": "DbContext creation at design time (Code-First database migration is only used during development and will not be executed in the production environment)", "切换至 Debug 模式": "Switch to Debug mode", "运行:PM": "Run: PM", "更新名称": "Update name", "实体类": "Entity class", "颜色码,0-255": "Color code, 0-255", "附加列,测试多次数据库 Migrate": "Additional columns, test database multiple times Migrate", "花费时间": "spend time", "获取或初始化一个 ColorDto 对象": "Get or initialize a ColorDto object", "使用": "use", "可将任意方法或类快速创建动态 WebApi。": "Any method or class can be quickly created into a dynamic WebApi.", "在 DDD 系统中,出于安全和防腐考虑,建议只在 AppService 上使用。": "In DDD systems, for security and anti-corrosion considerations, it is recommended to only use AppService.", "当 AppService 上添加": "When added on AppService", "标签满足不了需求时,仍然可以手动创建 ApiController。": "When the label cannot meet the needs, you can still create an ApiController manually.", "将 AppService 暴露为 WebApi": "Expose AppService as WebApi", "自定义 Post 类型和复杂参数,同时测试异常抛出和自定义状态码": "Customize Post types and complex parameters, while testing exception throwing and custom status codes", "是 AppResponseBase": "is AppResponseBase", "的快捷写法": "shortcut for writing", "参数说明:": "Parameter description:", "已经初始化后的返回结果": "Return result after initialization", "日志": "log", "如果直接对 response 的属性修改,则最终 return null,": "If the properties of response are modified directly, null will eventually be returned.", "否则可以返回一个新的 response 对象,系统将自动覆盖原有对象": "Otherwise, a new response object can be returned, and the system will automatically overwrite the original object.", "测试保存单个对象": "Test saving a single object", "设置保存对象后的行为": "Set the behavior after saving the object", "测试批量保存对象": "Test batch saving objects", "测试保存所有变化": "Test save all changes", "附带备份) 数据库配置,处于等待官方更新中,目前无效": "Backup included) Database configuration, waiting for official update, currently invalid", "需要等 Pomelo": "Need to wait Pomelo", "才能支持:https": "To support: https", "关闭连接(长时间保持一个连接操作会导致数据库操作时间逐渐变长)": "Close the connection (maintaining a connection operation for a long time will cause the database operation time to gradually become longer)", "升级到": "upgrade to", "的过程中注释掉": "Comment out the process", "当前采用注入可以保证HttpContext单例,如果要全局单例,可采用单件模式(需要先解决释放的问题)": "Currently, injection can be used to ensure the HttpContext singleton. If you want a global singleton, you can use the singleton mode (you need to solve the release problem first)", "测试同一Request只有一个SenparcEntities实例": "Test that there is only one SenparcEntities instance for the same Request", "模拟 SenparcEntities 或各个 XNCF 模块中的 xxxSenparcEntities。": "Emulate SenparcEntities or xxxSenparcEntities in various XNCF modules.", "在单元测试过程中,这个类将取代 NCF 运行时的 SenparcEntities,集成所有模块的 DbSet 对象": "During unit testing, this class will replace the NCF runtime's SenparcEntities, integrating all modules' DbSet objects", "获取强类型的列表": "Get a strongly typed list", "快速添加实体类型的列表数据": "Quickly add list data of entity types", "快速添加 个对象,兼备自动根据类型分类": "Quickly add objects, with automatic classification by type", "创建 DataList": "Create DataList", "唯一编号,相同编号的 DataList 数据在同一次单元门测试中不会被反复写入": "Unique number. DataList data with the same number will not be written repeatedly in the same unit gate test.", "设置 RepositoryBase": "Set RepositoryBase", "获取指定类型的仓储实例,带有预设的 Mock 行为": "Get a repository instance of the specified type with preset Mock behavior", "实体类型": "Entity type", "仓储实例": "Warehousing example", "方法": "method", "泛型版本的Mock设置": "Generic version of Mock settings", "方法 (带 includesNavigationPropertyPathFunc)": "Method (with includesNavigationPropertyPathFunc)", "待完善": "To be improved", "使用Mock DbSet": "Using Mock DbSet", "实体类型(OrderBy 默认为 int 类型,include 默认为 object 类型)": "Entity type (OrderBy defaults to int type, include defaults to object type)", "获取指定类型的仓储实例": "Get the warehousing instance of the specified type", "根目录地址": "Root directory address", "检查关键字存在": "Check keyword exists", "构造函数,用于初始化服务提供者和种子数据": "Constructor to initialize the service provider and seed data", "在启动时注册 ServiceCollection 的委托": "Register the ServiceCollection's delegate at startup", "初始化种子数据的委托": "Initialize the delegate for seed data", "执行前准备": "Preparation before execution", "自动填充": "autofill", "填充后": "After filling", "填充所有数据": "Populate all data", "设置 DbSet": "SetupDbSet", "转换 data 类型": "Convert data type", "添加到 DbSet": "Add to DbSet", "尝试初始化指定类型的种子数据": "Attempts to initialize seed data of the specified type", "注册 IServiceCollection 和 MemoryCache": "Register IServiceCollection and MemoryCache", "设置单元测试默认 DbContext(需要在 StartNcfEngine 之前)": "Set the unit test default DbContext (needs before StartNcfEngine)", "覆盖 NCF 基础设置": "Override NCF basic settings", "初始化对象": "initialize object", "不支持事务,如果不希望抛错,则忽略警告": "Transactions are not supported, ignore warnings if you do not want to throw errors", "注册 RegisterService": "Register RegisterService", "注册": "register", "的连接字符串信息会从 Config": "The connection string information will be retrieved from Config", "自动获取并注册,如不需要修改,下方方法可以忽略": "Automatically obtain and register. If no modification is required, the following method can be ignored.", "如需手动修改,可以通过下方 SetConfigurationOption 方法手动设置 Redis 链接信息(仅修改配置,不立即启用)": "If you need to modify it manually, you can manually set the Redis link information through the SetConfigurationOption method below (only modify the configuration, not enable it immediately)", "储存格式的缓存策略": "Storage format caching strategy", "键值对": "key value pair", "创建种子数据": "Create seed data", "填充种子数据前的操作": "Operations before filling in seed data", "填充种子数据后的操作": "What to do after filling in seed data", "从 ExecuteAsync 中返回的 dataList": "dataList returned from ExecuteAsync", "绝对路径": "absolute path", "对话入口样式": "Conversation entry style", "拖拽时的鼠标样式": "Mouse style when dragging", "角色权限列表": "Role permission list", "模块页面的模板页的菜单部分": "The menu section of the template page of the module page", "判断当前页面是否为 XNCF 子页面": "Determine whether the current page is an XNCF subpage", "菜单": "menu", "判断当前页面是否为 XNCF 模块入口": "Determine whether the current page is the XNCF module entry", "判断是否隐藏模块管理单元": "Determine whether to hide the module management unit", "显示扩展模块,建立一级目录": "Display extension modules and create a first-level directory", "显示扩展模块,建立二级目录": "Display extension modules and create secondary directories", "隐藏“模块管理”菜单": "Hide \"Module Management\" menu", "这是一个 XNCF UI 模块菜单,并且包含多个菜单配置": "This is an XNCF UI module menu and contains multiple menu configurations", "这是一个普通的 XNCF(可能只包含 Functions)菜单": "This is a normal XNCF (maybe just Functions) menu", "其他菜单,如系统或自定义菜单": "Other menus, such as system or custom menus", "显示扩展模块,二级目录结束": "Extension modules are displayed, and the secondary directory ends", "显示扩展模块,一级目录结束": "Display extension modules, end of first-level directory", "自定义路径": "Custom path", "文件名": "file name", "应用程序": "app", "启动所有的程序": "Start all programs", "开始运行 LaunchApp": "Start running LaunchApp", "操作成功!": "Operation successful!", "启动程序\n private bool StartApp(string appName)": "Start the program\n private bool StartApp(string appName)", "应用程序模块": "application module", "此模块提供给开发者一个可以启动任何程序!": "This module provides developers with a way to launch any program!", "新增公众号": "Add a new official account", "编辑公众号": "Edit public account", "新增公众号成功": "Added public account successfully", "编辑公众号成功": "Edit public account successfully", "成功": "success", "警告": "warn", "请勾选公众号": "Please check the official account", "是否删除所都选的项目?": "Delete all selected items?", "已成功删除": "Deleted successfully", "条": "strip", "凭证状态": "Voucher status", "扩展模块": "Extension modules", "微信管理": "WeChat management", "追加新增关注": "Add new attention", "同步所有关注": "Sync all following", "注意:如果用户数量众多,可能会消耗非常长的时间!": "Note: If there are a large number of users, it may take a very long time!", "昵称": "Nick name", "性别": "gender", "男": "male", "女": "female", "未知": "unknown", "地区": "area", "分组": "Group", "关注时间": "attention time", "关注状态": "Follow status", "已关注": "Already following", "未关注": "Not following", "关注场景": "Pay attention to the scene", "标签": "Label", "无": "none", "备注": "Remark", "添加时间": "Add time", "的用户": "of users", "所有用户": "all users", "无效的端口号": "Invalid port number", "未提供的方法名称": "Unsupplied method name", "访问控制禁止调用": "Access control prohibits calls", "请求失败": "Request failed", "没有配置全局PubSubName": "No global PubSubName configured", "没有配置全局StateStoreName": "Global StateStoreName is not configured", "获取状态成功": "Get status successfully", "状态键不存在": "Status key does not exist", "状态存贮不存在或者配置错误": "The state store does not exist or is misconfigured.", "状态获取失败": "Status acquisition failed", "状态保存成功": "Status saved successfully", "状态存储丢失或配置错误或请求格式错误": "The state store is missing or misconfigured or the request is malformed", "状态保存失败": "State saving failed", "状态删除成功": "Status deleted successfully", "状态存储丢失或配置错误": "The state store is missing or misconfigured", "状态删除失败": "Status deletion failed", "尚未准备就绪": "Not ready yet", "序列化对象失败:": "Failed to serialize object:", "反序化对象失败:": "Deserializing object failed:", "消息体:": "Message body:", "修改命名空间": "Modify namespace", "此模块提供给开发者在安装完 NCF、发布产品之前,全局修改命名空间,请在生产环境中谨慎使用,此操作不可逆!必须做好提前备份!不建议在已经部署至生产环境并开始运行后使用此功能!": "This module allows developers to globally modify the namespace before installing NCF and releasing the product. Please use it with caution in a production environment. This operation is irreversible! You must make a backup in advance! It is not recommended to use this feature after it has been deployed to a production environment and is running!", "扫描文件类型": "Scan file types", "数量": "quantity", "文件命中": "file hit", "更新成功!您还可以使用【还原命名空间】功能进行还原!": "Update successful! You can also use the [Restore Namespace] function to restore!", "还原命名空间": "Restore namespace", "还原所有源码在": "Restore all source code in", "中的命名空间为 NCF 默认(建议在断崖式更新之前进行此操作)。": "The namespace in is NCF by default (recommended before cliff updates).", "还原命名空间成功!": "Namespace restoration successful!", "下载官方 NCF 源码": "Download official NCF source code", "修改所有源码在": "Modify all source code in", "中的命名空间。": "namespace in .", "未知的下载地址": "Unknown download address", "未知的下载参数": "Unknown download parameters", "源码来源": "Source code source", "目前更新最快的是 GitHub,Gitee(码云)在国内下载速度更快,但是不能确定是最新代码,下载前请注意核对最新 GitHub 上的版本。": "Currently, GitHub is the fastest updated, and Gitee (code cloud) has faster download speed in China, but it cannot be sure that it is the latest code. Please check the latest version on GitHub before downloading.", "本地物理路径,如:E": "Local physical path, such as: E", "当前自定义的命名空间": "Current customized namespace", "命名空间根,一般以": "The namespace root is usually", "结尾,如:": "ending, such as:", "最终将替换为例如": "will eventually be replaced by e.g.", "或": "or", "开始生成,任务类型:": "Start generation, task type:", "信息:": "information:", "创建文件": "Create file", "创建文件 createFileResult": "Create file createFileResult", "获取单词复数\n\n var pluginDir": "Get word plural\n\n var pluginDir", "更新 SenparcEntities": "Update SenparcEntities", "已存在,需要删除": "Already exists and needs to be deleted", "已删除": "Deleted", "删除过程中发生异常:": "An exception occurred during deletion:", "删除失败": "Delete failed", "开始初始化 XncfBuilderPlugin 靶场信息": "Start initializing XncfBuilderPlugin range information", "背景": "background", "这是一个代码生成任务。\n2": "This is a code generation task.\n2", "生成后的 Entity 代码将填充在 JSON 结果中的“EntityCode”参数中返回。\n3": "The generated Entity code will be returned in the \"EntityCode\" parameter in the JSON result.\n3", "生成代码的要求见": "For requirements for generating code, see", "要求": "Require", "规则": "rule", "生成任务的规则如下:\n1": "The rules for generating tasks are as follows:\n1", "根据下方": "According to the following", "中的提示,生成 Entity 类的内容。\n2": "prompt in, generate the contents of the Entity class.\n2", "将生成的类,整理成 JSON 格式并返回,JSON 示例代码如": "Organize the generated classes into JSON format and return it. The JSON sample code is as follows", "和": "and", "之间代码所示(示例中假设当前 Entity 名称为 Color,生成内容中请根据 3": "As shown in the code (in the example, it is assumed that the current Entity name is Color, please generate content according to 3", "中的要求生成)。\n 2": "request generation).\n 2", "第一条数据 FileName 用于设置 Entity 对应的文件名;EntityCode 用于设值此 Entity 类的代码(代码生成规则见 4": "The first piece of data, FileName, is used to set the file name corresponding to Entity; EntityCode is used to set the code of this Entity class (see 4 for code generation rules)", "的文件名命名规则:\n 3": "File name naming rules:\n 3", "类名": "Class name", "相等,当": "equal, when", "参数中的": "in parameters", "参数不为空时,使用className;否则,根据": "When the parameter is not empty, use className; otherwise, use", "中的内容自动生成此名称。\n4": "This name is automatically generated from the content in .\n4", "生成规则:\n 4": "Generate rules:\n 4", "返回数据的 EntityCode:分析": "Return the EntityCode of the data: analysis", "需求": "need", "中的代码生成要求,参考下方“": "For code generation requirements, refer to the \"", "代码": "code", "模板,生成新的代码。生成的代码必须根据要求对其类名、属性、方法进行重构。最后将生成的代码插入到 EntityCode 的值中。\n6": "Template to generate new code. The generated code must have its class names, properties, and methods refactored as required. Finally insert the generated code into the value of EntityCode.\n6", "新 Entity 代码": "New Entity code", "的生成需要进行如下的约束:\n 6": "The generation of requires the following constraints:\n 6", "所有的属性都为只读,即设置 set 属性为 private。\n 6": "All properties are read-only, that is, set the set property to private.\n 6", "提供一个不带参数的 private 构造函数。\n 6": "Provide a private constructor that takes no parameters.\n 6", "所有的数据默认在一个 public 的构造函数中进行初始化。\n 6": "All data is initialized in a public constructor by default.\n 6", "实体内部包含了必要的方法,形成一个“充血实体”。\n 6": "The necessary methods are included inside the entity to form a \"congested entity\".\n 6", "所有的类、参数、方法,都必须添加注释,注释内容必须结合生成要求,能明确描述当前对象的作用。\n 6": "All classes, parameters, and methods must be annotated. The annotation content must be combined with the generation requirements and can clearly describe the role of the current object.\n 6", "如果代码中出现了使用了新的枚举类型,请按为该枚举创建对应的代码(附加在": "If a new enumeration type appears in the code, please click to create the corresponding code for the enumeration (attached in", "定义之后)。如果没有明确指定枚举的值,请按照上下文理解需求后自动生成枚举代码。枚举值同样需要加入注释。\n 6": "after definition). If the value of the enumeration is not explicitly specified, automatically generate the enumeration code after understanding the requirements in context. Enumeration values ​​also need to be commented.\n 6", "生成的同时请检查返回的 JSON 是否符合标准的 JSON 格式,尤其注意标签的闭合情况。\n\n-----------------": "While generating, please check whether the returned JSON conforms to the standard JSON format, paying special attention to the closing of the tag.\n\n------------------", "返回结果模板": "Return result template", "数据库实体": "database entity", "红色,取值范围:0": "Red, value range: 0", "参数": "parameter", "结果": "result", "生成 Entity 类的内容": "Generate the contents of the Entity class", "完成": "Finish", "添加": "Add to", "生成后的 DTO Entity 代码将填充在 JSON 结果中的“EntityCode”参数中返回。\n3": "The generated DTO Entity code will be returned in the \"EntityCode\" parameter in the JSON result.\n3", "源码": "Source code", "中的代码,生成对应的 Dto Entity 类的内容。\n2": "The code in generates the contents of the corresponding Dto Entity class.\n2", "之间代码所示(示例中假设当前 Entity 名称为": "As shown in the code (in the example, it is assumed that the current Entity name is", "用于设置 DTO Entity 对应的文件名,文件名的命名规则见 3": "Used to set the file name corresponding to the DTO Entity. For the naming rules of the file name, see 3", "用于设值此 DTO Entity 类的代码(代码生成规则见 4": "Code used to set the value of this DTO Entity class (see 4 for code generation rules", "数据的 EntityCode:分析": "EntityCode of data: analysis", "模板,生成新的代码,生成的代码必须根据要求对其类名、属性、方法进行重构。最后将生成的代码插入到 EntityCode 的值中。\n6": "Templates generate new code, and the generated code must reconstruct its class names, attributes, and methods according to requirements. Finally insert the generated code into the value of EntityCode.\n6", "新 DTO Entity 代码": "New DTO Entity code", "中如果有枚举,在 DTO Entity 中不需要再次生成。\n 6": "If there is an enumeration in the DTO Entity, it does not need to be generated again.\n 6", "所有的类、参数、方法的注释都和": "All class, parameter, and method annotations are the same as", "中的属性保持一致。\n 6": "properties remain consistent.\n 6", "生成的同时请检查返回的 JSON 是否符合标准的 JSON 格式,尤其注意标签的闭合情况。\n 6": "While generating, please check whether the returned JSON conforms to the standard JSON format, paying special attention to the closing of the tag.\n 6", "生成代码不需要包含 Markdown 的代码块标签(": "Generating code does not require code block tags containing Markdown (", "注释请完全复制,不要对": "Please copy the comments exactly and do not modify the", "等符号进行转义。\n\n-----------------": "Escape symbols.\n\n------------------", "代码转换示例": "Code conversion example", "源码:": "Source code:", "更多属性和构造函数": "More properties and constructors", "新 DTO Entity 代码:": "New DTO Entity code:", "数据库实体 DTO": "Database Entity DTO", "此处添加": "add here", "类中的所有属性及构造函数,删除所有额外的方法,保留完整注释内容": "All attributes and constructors in the class, delete all extra methods, and retain complete annotation content", "生成 DTO Entity 类的内容": "Generate the contents of the DTO Entity class", "请将 INPUT": "Please enter INPUT", "中提供的单词转换为复数,在": "Convert the words provided in to the plural, in", "输出": "output", "跟着输出结果。": "Follow the output results.", "示例": "Example", "将单词转换为复数": "Convert word to plural", "全部初始化完成!": "All initialization completed!", "未知的 PromptRangeName:": "Unknown PromptRangeName:", "记录已存在,未覆盖": "The record already exists and has not been overwritten", "自定义目标框架版本": "Custom target framework version", "其他目标框架版本,如果填写,将覆盖【目标框架版本】的选择": "Other target framework versions, if filled in, will override the selection of [Target Framework Version]", "组织名称": "Organization name", "用于作为模块命名空间(及名称)的前缀": "Used as a prefix for module namespaces (and names)", "模块名称": "module name", "同时将作为类名(请注意类名规范),支持连续英文大小写和数字,不能以数字开头,不能带有空格和": "At the same time, it will be used as a class name (please pay attention to the class name specification). It supports continuous English upper and lower case and numbers. It cannot start with a number, and cannot contain spaces and", "等特殊符号": "and other special symbols", "版本号": "version number", "如:1": "Such as: 1", "菜单名称": "Menu name", "如“NCF 生成器”": "Like \"NCF Generator\"", "图标": "icon", "支持 Font Awesome 图标集,如:fa fa-star": "Support Font Awesome icon set, such as: fa fa-star", "模块的说明": "Description of the module", "功能配置": "Function configuration", "配置“函数”功能": "Configure the \"Function\" function", "是否需要使用函数模块(Function)": "Do you need to use function module?", "配置“数据库”功能": "Configure the \"database\" function", "是否需要使用数据库模块(Database),将配置空数据库": "Whether you need to use the database module (Database), an empty database will be configured", "配置“WebApi”功能": "Configure the \"WebApi\" function", "是否需要使用WebApi模块(WebApi)": "Do you need to use the WebApi module (WebApi)", "配置“Web(Area) 页面”功能": "Configure the \"Web (Area) Page\" function", "是否需要使用 Web 页面模块(Web)": "Do you need to use the Web page module (Web)", "安装 Sample": "Install Sample", "是": "yes", "是否安装数据库示例,由于展示需要,将自动安装上述“数据库”、“Web(Area) 页面”功能": "Whether to install the database example. Due to display needs, the above \"Database\" and \"Web (Area) Page\" functions will be automatically installed.", "自动载入上次配置": "Automatically load the last configuration", "自动查找当前项目的解决方案路径\n\n SlnFilePath": "Automatically find the solution path for the current project\n\n SlnFilePath", "用户名": "username", "产品表": "product list", "订单表": "order form", "文档测试": "Document testing", "测试-": "test-", "产品经理机器人": "Product Manager Robot", "你是一名产品经理,负责管理和协调软件开发项目,当需要获取外部资源时,你可以向其他人寻求帮助。": "You are a product manager responsible for managing and coordinating software development projects, and you can call on others for help when you need to obtain external resources.", "爬虫机器人": "Reptile robot", "你是一个爬虫,你负责从互联网上获取信息,并返回给用户。": "You are a crawler and you are responsible for getting information from the Internet and returning it to the user.", "测试项目": "test items", "验证 PromptRange 初始化数据\n var aiModelService": "Verify PromptRange initialization data\n var aiModelService", "项目经理": "project manager", "爬虫": "reptile", "验证 AgentsManager 初始化数据\n var agentTemplateService": "Verify AgentsManager initialization data\n var agentTemplateService", "爬虫 FunctionCall:": "Crawler FunctionCall:", "请对 https": "Please use https", "进行分析,告诉我这个站点的概要信息,以及所表述的产品的特点。": "Do an analysis and tell me an overview of the site and the features of the product being represented.", "测试项目聊天组": "Test project chat group", "接口": "interface", "接口\n\n public string HomeUrl": "interface\n\n public string HomeUrl", "首页": "front page", "数据库操作示例": "Database operation example", "完成 Area": "Complete Area", "接口\n public string LibraryPath": "interface\n public string LibraryPath", "人工智能代理枢纽模块": "Artificial Intelligence Agent Hub Module", "初始化数据库数据\n var colorService": "Initialize database data\n var colorService", "删除数据库(演示)\n\n var mySenparcEntitiesType": "Delete database (demo)\n\n var mySenparcEntitiesType", "抛出异常测试,传输参数:": "Throw an exception test and transfer parameters:", "正在处理异常,信息:": "Handling exception, message:", "建议格式: NCF公司ID-项目类型-保留字段(可随机)-内部类别1-内部类别2": "Recommended format: NCF company ID-project type-reserved field (can be random)-internal category 1-internal category 2", "以下参数放到SiteConfig": "Put the following parameters into SiteConfig", "中": "middle", "填充种子数据": "Populate seed data", "完成 Redis 设置": "Complete Redis setup", "启用 Redis UseKeyValue 策略": "Enable Redis UseKeyValue policy", "系统管理": "System management", "角色管理": "role management", "角色权限管理": "Role permission management", "权限设置": "Permission settings", "设置": "set up", "添加模型": "Add model", "显示": "show", "编辑模型": "Edit model", "页面中,点击【显示 ApiKey】按钮查看到 DeveloperId 及 ApiKey,如果您有多项,可分批次导入。": "On the page, click the [Show ApiKey] button to view the DeveloperId and ApiKey. If you have multiple items, you can import them in batches.", "添加向量数据库": "Add vector database", "编辑向量数据库": "Edit vector database", "侧边": "side", "组详情": "Group details", "任务详情": "Mission details", "详情 type": "Details type", "编辑 type": "edit type", "组全部": "group all", "全部任务": "All tasks", "抽屉 新增": "Drawer New", "编辑 智能体 540px": "Edit Agent 540px", "内容中的头(包括标题和关闭图标)": "Header within content (including title and close icon)", "内容中的主体内容": "body of content", "内容中固定的footer": "Fixed footer in content", "编辑 组": "Edit group", "抽屉 启动 组": "drawer start group", "智能体 新增": "Agent New", "智能体 参数列表": "Agent parameter list", "请填写": "Please fill in", "工具列表": "Tool list", "详情内容": "Details", "共用按钮操作区域": "Shared button operation area", "编辑 数据表单区域": "Edit data form area", "数据表新增 空状态 提示": "Added empty status prompt to data table", "数据表字段 table": "data table fields table", "新增字段抽屉": "Add new field drawer", "主体操作区域": "Main operating area", "全局按钮": "global button", "组件区域": "component area", "自定义列 按钮": "Custom column button", "开关": "switch", "下拉选择": "drop down selection", "固定时间点": "fixed time point", "任意时间点": "any point in time", "日期 时间": "date time", "复选框": "checkbox", "单选框": "radio button", "重置": "reset", "组件占位": "Component placeholder", "上下移动": "Move up and down", "添加组件提示": "Add component prompt", "表格设置管理": "Form settings management", "属性设置": "Property settings", "表格": "sheet", "表单 表和列属性 设置": "Form table and column properties settings", "表格提示": "Table prompts", "请选择列宽": "Please select column width", "数据表文本": "data table text", "数据表文本1": "Datasheet text 1", "选择项": "Options", "请输入列宽": "Please enter column width", "表格 数据 设置": "table data settings", "表格 普通列属性 设置": "Table common column properties settings", "表格 自定义列属性 设置": "Table custom column properties settings", "请选择": "Please select", "普通列": "Ordinary column", "操作列": "Operation column", "显示列": "show columns", "固定列": "fixed column", "表格 自定义列按钮属性 设置": "Table Custom Column Button Properties Settings", "自定义列按钮事件": "Custom column button event", "表单 单个组件属性 设置": "Form individual component properties settings", "日期": "date", "链接": "Link", "选择组件": "Select components", "日期选择器": "date picker", "单选组件": "Radio component", "多选组件": "Multiple choice component", "字段名称": "Field name", "请输入内容": "Please enter content", "提示文字": "Prompt text", "显示方式": "Display mode", "下拉菜单": "drop down menu", "平铺": "Tile", "详情": "Details", "选项": "Options", "自定义": "Customize", "数据集": "Dataset", "选项设置": "Option settings", "选项字段": "option field", "请添加选项": "Please add options", "添加选项": "Add options", "默认值": "default value", "默认值1": "Default value 1", "类型": "type", "单行": "single line", "多行": "multiple lines", "验证": "verify", "必填": "Required", "不允许重复输入": "Duplicate entries are not allowed", "限定字数": "Limited word count", "最小": "smallest", "最大": "maximum", "限定输入格式": "Limit input format", "宽度占比": "width ratio", "日期类型": "date type", "日期时间": "date time", "时间类型": "time type", "年-月-日 时": "year-month-day hour", "分": "point", "日期格式": "date format", "字段属性": "Field properties", "只读": "read only", "隐藏": "hide", "表单 提交按钮属性 设置": "Form submit button properties settings", "样式 设置": "style settings", "表格 整体样式 设置": "Table overall style settings", "表单 整体样式 设置": "Form overall style settings", "字体大小": "font size", "请输入字体大小": "Please enter font size", "宽度": "width", "请输入lable宽度": "Please enter label width", "表格 普通列样式 设置": "Table common column style settings", "表格 自定义列样式 设置": "Table custom column style settings", "表单 单个组件样式 设置": "Form single component style settings", "表单 按钮样式 设置": "Form button style settings", "全局 按钮设置管理": "Global button settings management", "编辑 知识库关联": "Edit Knowledge Base Association", "描述": "describe", "配置页内": "Configuration page", "新建文件": "Create new file", "上传弹框:选择文件后调用上传接口写入 FileManage,保存时与知识库关联": "Upload pop-up box: After selecting the file, call the upload interface to write to FileManage, and associate it with the knowledge base when saving.", "检索设置抽屉:点击倒排索引打开,可设置 Top K 并传给召回接口": "Search settings drawer: Click on the inverted index to open it. Top K can be set and passed to the recall interface.", "段落详情弹框:点击召回片段": "Paragraph details pop-up box: click to recall the fragment", "打开": "Open", "模型使用次数": "Model usage times", "至": "to", "开始日期": "start date", "结束日期": "end date", "维护": "maintain", "左侧 输入测试": "Left input test", "配置": "Configuration", "输入": "enter", "请新建或选择靶场 mask": "Please create a new or select a range mask", "连发 mask": "continuous mask", "模型参数设置": "Model parameter settings", "设置ai评分标准": "Set AI scoring criteria", "左侧和中间区域之间的拖动分隔条": "Drag divider between left and middle areas", "中间 输入Prompt": "Middle input prompt", "输入Prompt": "EnterPrompt", "输入Prompt内容": "Enter prompt content", "编辑器,支持内联高亮": "Editor, supports inline highlighting", "选择连发次数": "Select the number of bursts", "打靶按钮 v-loading": "Target button v-loading", "请求参数 - 移到leftItemBox内部,作为下方区域": "Request parameters - moved inside the leftItemBox as the lower area", "变量名 变量值": "variable name variable value", "请输入变量名": "Please enter variable name", "请输入变量值": "Please enter variable value", "取 消": "Cancel", "确 定": "Sure", "中间和右侧区域之间的拖动分隔条": "Drag divider between middle and right areas", "右侧 评分": "Rating on the right", "分析": "analyze", "请输入": "Please enter", "风格数据条背景": "style data bar background", "内容容器": "content container", "分数趋势图": "Score Trend Chart", "版本记录-抽屉 drawer": "Version record-drawer drawer", "查看备注": "View notes", "战术选择 dialog": "tactical options dialog", "继续聊天模式下不显示战术选择,第一次打靶时(没有promptid)也不显示战术选择": "Tactical options are not displayed in continued chat mode, and tactical options are not displayed when shooting for the first time (without promptid).", "继续聊天模式下的历史记录显示": "Continue history display in chat mode", "显示在对话历史顶部": "Show at top of conversation history", "只有助手消息才显示反馈按钮,并且必须有有效的 ID": "The feedback button is only shown for assistant messages and must have a valid ID", "如果没有有效 ID,显示提示": "If there is no valid ID, show a prompt", "对话模式下的对话输入区域": "Conversation input area in conversation mode", "注意:不使用 prop": "Note: do not use prop", "因为这是条件显示的字段,resetFields 可能会出错": "Because this is a field that is conditionally displayed, resetFields may error", "导图 dialog": "map dialog", "新增模型 dialog": "New model dialog", "新增靶场 dialog": "New shooting range dialog", "评分标准 dialog": "Grading criteria dialog", "请输入期望结果": "Please enter the desired result", "保 存": "Save", "导入plugin": "Import plugin", "拖拽区域 class": "drag area class", "选择的文件夹中的文件列表": "List of files in selected folder", "导出plugin": "Export plugin", "请选择靶场": "Please select a shooting range", "操作按钮组": "Action button group", "树形选择器": "tree selector", "对比对话框": "Compare dialog box", "选择对比的Prompt": "Select Prompt for comparison", "同一Prompt警告": "Same Prompt warning", "基本信息对比(紧凑型表格)": "Basic information comparison (compact table)", "内容对比(左右同步滚动,Git风格差异显示)": "Content comparison (synchronous scrolling left and right, Git style difference display)", "请求参数对比(前缀、后缀、变量配置)": "Request parameter comparison (prefix, suffix, variable configuration)", "前缀和后缀对比(表格形式)": "Prefix and suffix comparison (table format)", "变量列表对比(Git diff风格)": "Variable list comparison (Git diff style)", "优化Prompt弹窗": "Optimize Prompt pop-up window", "新增选项:自动打靶和 AI 评分": "New options: automatic target shooting and AI scoring", "初始化对话框": "Initialization dialog", "工具类库 - 必须在 prompt": "Tool library - must be in prompt", "之前加载": "Loaded before", "暂不使用,项目已有 servicePR (axios)": "Not used yet, the project already has servicePR (axios)", "南海诸岛": "South China Sea Islands", "广东": "Guangdong", "香港": "Hongkong", "澳门": "Macao", "天津": "Tianjin", "辅助函数": "Helper function", "测试 HTML Helper": "Test HTML Helper", "测试转义": "Test escaping", "测试 UUID 生成": "Test UUID generation", "测试文件大小格式化": "Test file size formatting", "测试正则转义": "Test regular escape", "测试空值判断": "Test null value judgment", "测试 Date Helper": "Test Date Helper", "测试日期格式化": "Test date formatting", "测试相对时间": "test relative time", "分钟前": "minutes ago", "测试时间戳": "test timestamp", "测试是否为今天": "Test if it is today", "测试持续时间格式化": "Test duration formatting", "小时1分钟5秒": "hour 1 minute 5 seconds", "测试 Name Helper": "Test Name Helper", "测试 getName": "Test getName", "测试找不到的情况": "The test cannot be found", "测试 getId": "Test getId", "测试 hasId": "Test hasId", "测试批量获取": "Test batch acquisition", "测试 Storage Helper": "Test Storage Helper", "测试是否可用": "Test if available", "测试保存和读取": "Test save and read", "测试 has": "test has", "测试删除": "Test deletion", "测试存储信息": "Test storage information", "测试 Copy Helper": "Test Copy Helper", "测试是否支持": "Test whether it is supported", "测试复制文本": "Test copy text", "测试复制对象": "Test copy object", "测试复制数组": "Test copy array", "检查全局命名空间": "Check global namespace", "页面加载完成提示": "Page loading completion prompt", "技术支持": "Technical support", "切换菜单栏": "Toggle menu bar", "面包屑": "bread crumbs", "模块页面的模板页": "Template page for module page", "编辑管理员": "Edit Admin", "不建议修改用户名!": "It is not recommended to change your username!", "模块信息 xncfModule": "Module information xncfModule", "网页": "Web page", "数据库": "database", "中间件": "middleware", "更新记录": "Update record", "运行结果": "Running results", "一用于构套造基础项目的": "One for constructing basic projects", "框架": "frame", "待填写文字待填写文字待填写文字待填写文字待填写文字待填写文字待填写文字": "Text to be filled in Text to be filled in Text to be filled in Text to be filled in Text to be filled in Text to be filled in Text to be filled in", "极差": "Very poor", "失望": "disappointment", "一般": "generally", "满意": "satisfy", "惊喜": "surprise", "加载失败": "Loading failed", "添加颜色": "add color", "编辑颜色": "Edit colors"} \ No newline at end of file diff --git a/.translation_state.json b/.translation_state.json new file mode 100644 index 000000000..27d10b356 --- /dev/null +++ b/.translation_state.json @@ -0,0 +1,2194 @@ +{ + "index": 2188, + "done": [ + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.AreaBase/Admin/AdminPageModelBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.AreaBase/Admin/AdminXscfModulePageModelBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/AdminAuthorizeAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/AuthenticationAsyncPageFilterAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/AuthenticationResultFilterAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/CustomerResourceAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.AreaBase/Conventions/AutoValidateAntiForgeryTokenModelConvention.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/AjaxReturnModel.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/AppServices/AppServiceBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/AppServices/AppServiceLogger.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/AppServices/Exceptions/BaseAppServiceException.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/AppServices/FunctionRenderAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/AppServices/FunctionRenderSymbolHelper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/AppServices/Helpers/AppServiceHelper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/AppServices/Models/AppRequestBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/AppServices/Models/AppResponseBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/AppServices/Models/StringAppResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Area/AreaData.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Areas/AreaPageMenuItem.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Areas/IAreaRegister.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/AssembleScan/AssembleScanHelper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/AssembleScan/AssembleScanItem.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Authorization/ICheckPermission.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Authorization/PermissionAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Authorization/PermissionFilterAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Authorization/PermissionHandler.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Authorization/PermissionRequirement.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/AutoMapper/SystemProfile.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/AreaDataCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseDictionary/BaseDictionaryCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseDictionary/BaseStringDictionary/BaseStringDictionaryCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/ICacheData.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/BaseCacheBindable.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/CacheDataMessageQueue.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/CacheDataMessageQueueItem.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/CommonCacheAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/CommonDataCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/EntityCache/FullSystemConfigCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/EntityCache/FullXncfModuleCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/Extensions/BaseCacheExtensions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/Extensions/ObjectCacheStrategyExtensions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/Interface/IBaseCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/Interface/IBaseCacheStrategy.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/Interface/ICommonDataCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/MethodCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/MobileLoginCodeCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/DemoLoginKeyCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/OAuthCodeCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/PhoneCheckCodeCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeBaseData.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeGroupCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeLoginCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeRegCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QueueCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QueueCacheData.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/WeixinCheckCodeCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Config/AIPluginHub.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Config/NcfCoreState.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Config/SiteConfig.Core.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Config/XmlConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/DI/AutoDITypeAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/DI/IAutoDI.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Enums.Core.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/EventBus/EventBusExtensions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/EventBus/EventBusHostedService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/EventBus/InMemoryEventBus.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Exceptions/LoginLockException.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Exceptions/NcfDatabaseException.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Exceptions/NcfExceptionBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Exceptions/NcfModuleException.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Exceptions/NcfTenantException.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Exceptions/NcfUninstallException.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Extensions/AuthorizeAttributeExtensions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Extensions/EntityFrameworkExtensions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Extensions/EntityTypeProvider.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Extensions/ObjectExtensions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Extensions/ObjectQueryExtensions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/AppServices/FileResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/AdminUserInfoDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/Base/DtoBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/Base/IDtoBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/Base/QueryDtoBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysButtonDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysMenuDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysPermissionDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysRoleAdminUserInfoDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysRoleDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/XscfModuleDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Mapping/Base/BlankEntityTypeConfiguration.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Mapping/Base/ConfigurationMappingBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysButton.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysMenu.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysRole.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysRoleAdminUserInfo.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysRolePermission.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SystemConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/XncfModule.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/EntityBase/EntityBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/EntityBase/IAggregateRoot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/EntityBase/IEntityBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/EntityBase/IMultiTenancy.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/EntityBase/ISoftDelete.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/EntitySetKeys.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/ExtensionEntity.Core.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/ExtensionEntity.Extension.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/IValidatorEnvironment.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/DatabaseConfigurationFactory.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/Helpers/NcfDatabaseMigrationHelper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/IDatabaseConfiguration.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/MultipleDatabaseType.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/XncfModules/IXncfDatabase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/XncfModules/XncfDatabaseData.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/NcfDbData/NcfDbData.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/SenparcCoreSetting.Core.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/SenparcEntities/ISenparcEntitiesDbContext.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/SenparcEntities/SenparcEntitiesBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/SenparcEntities/SenparcEntitiesDbContextBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/SmsTemplateEntity/SmsTemplate_Base.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/SmsTemplateEntity/SmsTemplate_Custom.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/VD/BaseVD.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/VD/CommonVD.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Models/VD/Razor/PageModelBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/MultiTenant/IIgnoreMulitTenant.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/MultiTenant/MultiTenantHelper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/MultiTenant/RequestTenantInfo.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/MultiTenant/TenantRule.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Properties/Annotations.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Properties/BindableBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Utility/CacheEntity/AutoSetCacheAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Utility/CacheEntity/FullEntityCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Utility/CheckCodeHandle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Utility/CommonWebParts.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Utility/ReflectorUtility.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Utility/XmlDataContext.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/Validator/Validator.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/VersionManager.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/WebApi/IXncfInterfaceConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/WebApi/NcfWebApiHelper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/WorkContext/AdminWorkContext.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/WorkContext/Provider/AdminWorkContextProvider.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Core/WorkContext/Provider/IAdminWorkContextProvider.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database/DatabaseConfiguration/BySettingDatabaseConfiguration.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database/DatabaseConfiguration/DatabaseConfigurationBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database/DatabaseConfiguration/UnitTestDatabaseConfiguration.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database/DbContextPools/MultipleDatabasePool.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database/DbContextPools/XncfDatabaseDbContextPool.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database/Helpers/NcfDatabaseHelper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database/MultipleMigrationDbContext/IMultipleMigrationDbContext.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database/MultipleMigrationDbContext/MultipleMigrationDbContextAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database/MySqlIdentityColumWorkaround/Workaround.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database/SenparcDatabaseConnectionConfigs.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database.Dm/DmDatabaseConfiguration.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database.InMemory/InMemoryDatabaseConfiguration.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database.InMemory/InMemoryDbContextOptionsBuilderForNcf.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database.InMemory/InMemoryOptionsExtensionForNcf.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database.MySql/MySqlDatabaseConfiguration.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database.Oracle/OracleDatabaseConfiguration.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database.Oracle/OracleDatabaseConfigurationForV11.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database.PostgreSQL/PostgreSQLDatabaseConfiguration.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database.SqlServer/SqlServerDatabaseConfiguration.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database.Sqlite/SqliteDatabaseConfiguration.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Database.Sqlite/SqliteMemoryDatabaseConfiguration.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Log/LogUtility.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Log/NLogExtension.Core.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Mvc.UI/CustomBindingHelperExtentions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Mvc.UI/HtmlExtension.Core.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/CurrentBsMenuExtensions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/CurrentMenuExtensions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/GridViewActionExtension.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/GridViewExtension.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/HtmlExtensionUtility.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/OnClickSpanExtension.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/PagerBarExtension.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/RepeaterExtension.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Mvc.UI/backend/BackendTemplateExtension.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Mvc.UI/backend/ContentBox.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Mvc.UI/backend/ShowMessageBox.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Repository/BaseRepoisitory/ClientRepositoryBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Repository/BaseRepoisitory/DataBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Repository/BaseRepoisitory/IRepositoryBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Repository/BaseRepoisitory/RepositoryBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Repository/System/SysButtonRespository.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Repository/System/SysRolePermissionRepository.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Repository/XncfModuleRepository.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.SMS/Enums.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.SMS/ReplyMessage.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.SMS/ReplyMessageCollection.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.SMS/SmsPlatform/SmsPlatform.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.SMS/SmsPlatform/SmsPlatformFactory.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.SMS/SmsPlatform/SmsPlatform_Fissoft.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.SMS/SmsPlatform/SmsPlatform_JunMei.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.SMS/SmsPlatform/SmsSetting.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/Common/EncryptionService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/Common/EncryptionServiceBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/Config.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/Extensions/ServieExtension.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/ServiceBase/AutoDetectChangeContext/AutoDetectChangeContext.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/ServiceBase/AutoDetectChangeContext/AutoDetectChangeContextExtension.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/ServiceBase/ClientServiceBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/ServiceBase/DtoServiceBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/ServiceBase/IServiceBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/ServiceBase/ResilientTransaction.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/ServiceBase/ServiceBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/ServiceBase/ServiceDataBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/SignalrHubs/SignalrTicker.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/System/SysButtonService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/System/SysMenuService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/System/SysRoleAdminUserInfoService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/System/SysRolePermissionService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/System/SysRoleService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Service/System/XncfModuleService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Shared.Abstractions/Events/IEventBus.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Shared.Abstractions/Events/IIntegrationEvent.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Shared.Abstractions/Events/IIntegrationEventHandler.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/DIExtension/Extensions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/ExpressionExtension/SenparcExpressionHelper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/ExpressionExtension/SenparcExpressionModifier.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/ExpressionExtension/SenparcIQueryableExtension.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/Extensions/DateTimeExtensions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/Extensions/IntegerExtensions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/Extensions/StringExtensions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/FileSaveUtility.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/FileUtility.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/Helpers/GlobalCulture.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/Helpers/ReflectionHelper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/IDCardValid.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/IPData.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/ModelStateDictionaryExtension.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/ReflectorUtility.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/RequestExtension.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/DateTimeUtility.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/DesUtility.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/Extensions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/IoUtility.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/MD5.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/Server.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/W3wp.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/SenparcHttpContext.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.Utility/StreamExtensions/WriteOnlyStreamWrapper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfAutoConfigurationMappingAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfMethodAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfOrderAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfRegisterAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/AutoMapper/XncfModuleProfile.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Database/SenparcDesignTimeDbContextFactoryBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Database/XncfDatabaseDbContext.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Enums.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Exceptions/NcfModuleException.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Exceptions/XncfFunctionException.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/FunctionRenders/FunctionAppRequestBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/FunctionRenders/FunctionRenderBag.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/FunctionRenders/FunctionRenderCollection.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Functions/EnumType.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Functions/FunctionHelper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Functions/Parameters/FunctionParameterInfo.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Functions/Parameters/PasswordAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Functions/Parameters/SelectionList.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Helpers/StartupHelper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfMiddleware.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfRazorRuntimeCompilation.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfRegister.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfThread.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IxncfMcp.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/MCP/McpServerInfo.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Threads/ThreadInfo.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/Threads/XncfThreadBuilder.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/VersionManager/VersionHelper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/VersionManager/VersionInfo.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/XncfRegisterBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Basic/Senparc.Ncf.XncfBase/XncfRegisterManager.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIKernel/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIKernel/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIVector/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIVector/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/Dashboard/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/Shared/_SideMenu.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/Shared/_ViewImports.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/Shared/_ViewStart.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/AutoMpperProfiles/AIKernelAutoMapperProfile.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Dm/20250402132108_Add_VectorDB.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Dm/20250402132108_Add_VectorDB.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Dm/AIKernelSenparcEntities_DmModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Migrations/MySql/AIKernelSenparcEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Migrations/Oracle/AIKernelSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Migrations/PostgreSQL/AIKernelSenparcEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Migrations/SqlServer/AIKernelSenparcEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Migrations/Sqlite/AIKernelSenparcEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Migrations.MySql/20240208132110_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Migrations.MySql/20240208132110_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Migrations.Oracle/20240208132057_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Migrations.Oracle/20240208132057_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Migrations.PostgreSQL/20240208132044_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Migrations.PostgreSQL/20240208132044_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Migrations.SqlServer/20240208132032_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Migrations.SqlServer/20240208132032_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Migrations.Sqlite/20240208132016_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Migrations.Sqlite/20240208132016_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/MySql/20250402132744_Add_VectorDB.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/MySql/20250402132744_Add_VectorDB.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Oracle/20250402132048_Add_VectorDB.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Oracle/20250402132048_Add_VectorDB.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/PostgreSQL/20250402132029_Add_VectorDB.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/PostgreSQL/20250402132029_Add_VectorDB.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/SqlServer/20250402131807_Add_VectorDB.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/SqlServer/20250402131807_Add_VectorDB.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Sqlite/20250402131732_Add_VectorDB.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Migrations/Sqlite/20250402131732_Add_VectorDB.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Models/DatabaseModel/AIKernelSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Models/DatabaseModel/AIModel.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Models/DatabaseModel/AIVector.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Models/DatabaseModel/Dto/AIModelDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Models/DatabaseModel/Dto/AIVectorDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Models/Enums.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Models/Extensions/NeuCharModel.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Models/MultipleDatabase/AIKernelSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Models/MultipleDatabase/AIKernelSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Models/MultipleDatabase/AIKernelSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Models/MultipleDatabase/AIKernelSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Models/MultipleDatabase/AIKernelSenparcEntities_SQLite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Models/MultipleDatabase/AIKernelSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Services/AIModelService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Services/AIVectorService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Domain/Services/ColorService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/OHS/Local/AppService/AIModelAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/OHS/Local/AppService/AIModelStudioAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/OHS/Local/AppService/AIVectorAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/OHS/Local/AppService/ColorAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/OHS/Local/PL/AIModelStudioRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/OHS/Local/PL/AIModel_Request.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/OHS/Local/PL/AIModel_Response.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/OHS/Local/PL/AIVector_Request.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/OHS/Local/PL/ApiRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/OHS/Local/PL/ColorResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/OHS/Local/PL/MyFunctionRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Register.Area.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/css/Admin/AIKernel/index.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/css/Admin/AIVector/index.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/js/Admin/AIKernel/index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/js/Admin/AIVector/index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AIKernel/wwwroot/js/Admin/Dashboard/Index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/ACL/AgentTemplatePrintMessageMiddleware.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/ACL/PrintWechatMessageMiddlewareExtension.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Application/AppService/AgentTemplateAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Application/AppService/ChatGroupAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Application/AppService/ChatGroupHistoryAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Application/AppService/ChatTaskAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Application/AppService/PromptOptimizationAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Application/DTOs/AgentTemplateRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Application/DTOs/AgentTemplateResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Application/DTOs/ChatGroupHistoryResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Application/DTOs/ChatGroupRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Application/DTOs/ChatGroupResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Application/DTOs/ChatTaskRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Application/DTOs/ChatTaskResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Application/EventHandlers/PromptInitResponseHandler.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Application/EventHandlers/PromptOptimizationChatTaskHandler.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Application/EventHandlers/PromptOptimizationHandler.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Application/EventHandlers/PromptOptimizationResponseHandler.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Areas/Admin/Pages/AgentsManager/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Areas/Admin/Pages/AgentsManager/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Areas/Admin/Pages/Shared/_SideMenu.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Areas/Admin/Pages/Shared/_ViewImports.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Areas/Admin/Pages/Shared/_ViewStart.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Exceptions/AgentsManagerException.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Dm/20250120143944_Add_AgentTemplate_FunctionCallNames.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Dm/20250120143944_Add_AgentTemplate_FunctionCallNames.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Dm/20250515133639_Add_AgentTemplate_McpEndpoints.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Dm/20250515133639_Add_AgentTemplate_McpEndpoints.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Dm/AgentsManagerSenparcEntities_DmModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/MySql/20241015173557_Add_ChatGroupHistory_Status.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/MySql/20241015173557_Add_ChatGroupHistory_Status.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/MySql/20241016160305_Add_ChatTask.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/MySql/20241016160305_Add_ChatTask.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/MySql/20250120143755_Add_AgentTemplate_FunctionCallNames.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/MySql/20250120143755_Add_AgentTemplate_FunctionCallNames.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/MySql/20250515133508_Add_AgentTemplate_McpEndpoints.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/MySql/20250515133508_Add_AgentTemplate_McpEndpoints.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/MySql/AgentsManagerSenparcEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Oracle/20241015173541_Add_ChatGroupHistory_Status.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Oracle/20241015173541_Add_ChatGroupHistory_Status.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Oracle/20241016160350_Add_ChatTask.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Oracle/20241016160350_Add_ChatTask.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Oracle/20250120143905_Add_AgentTemplate_FunctionCallNames.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Oracle/20250120143905_Add_AgentTemplate_FunctionCallNames.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Oracle/20250515133604_Add_AgentTemplate_McpEndpoints.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Oracle/20250515133604_Add_AgentTemplate_McpEndpoints.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Oracle/AgentsManagerSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/PostgreSQL/20241015173549_Add_ChatGroupHistory_Status.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/PostgreSQL/20241015173549_Add_ChatGroupHistory_Status.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/PostgreSQL/20241016160328_Add_ChatTask.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/PostgreSQL/20241016160328_Add_ChatTask.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/PostgreSQL/20250120143833_Add_AgentTemplate_FunctionCallNames.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/PostgreSQL/20250120143833_Add_AgentTemplate_FunctionCallNames.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/PostgreSQL/20250515133537_Add_AgentTemplate_McpEndpoints.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/PostgreSQL/20250515133537_Add_AgentTemplate_McpEndpoints.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/PostgreSQL/AgentsManagerSenparcEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/SqlServer/20240521052701_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/SqlServer/20240521052701_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/SqlServer/20241015173532_Add_ChatGroupHistory_Status.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/SqlServer/20241015173532_Add_ChatGroupHistory_Status.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/SqlServer/20241016160241_Add_ChatTask.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/SqlServer/20241016160241_Add_ChatTask.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/SqlServer/20250120143642_Add_AgentTemplate_FunctionCallNames.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/SqlServer/20250120143642_Add_AgentTemplate_FunctionCallNames.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/SqlServer/20250515133435_Add_AgentTemplate_McpEndpoints.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/SqlServer/20250515133435_Add_AgentTemplate_McpEndpoints.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/SqlServer/AgentsManagerSenparcEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Sqlite/20241015173607_Add_ChatGroupHistory_Status.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Sqlite/20241015173607_Add_ChatGroupHistory_Status.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Sqlite/20241016160210_Add_ChatTask.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Sqlite/20241016160210_Add_ChatTask.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Sqlite/20250120143717_Add_AgentTemplate_FunctionCallNames.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Sqlite/20250120143717_Add_AgentTemplate_FunctionCallNames.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Sqlite/20250515133150_Add_AgentTemplate_McpEndpoints.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Sqlite/20250515133150_Add_AgentTemplate_McpEndpoints.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Migrations/Sqlite/AgentsManagerSenparcEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/DatabaseModel/AgentTemplate.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/DatabaseModel/AgentsManagerSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/DatabaseModel/ChatGroup.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/DatabaseModel/ChatGroupHistory.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/DatabaseModel/ChatGroupMember.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/DatabaseModel/ChatTask.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/DatabaseModel/Dto/AgentTemplateDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/DatabaseModel/Dto/ChatGroupDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/DatabaseModel/Dto/ChatGroupHistoryDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/DatabaseModel/Dto/ChatGroupMemberDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/DatabaseModel/Dto/ChatTaskDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/DatabaseModel/Mapping/AgentTemplateConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/DatabaseModel/Mapping/ChatGroupConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/DatabaseModel/Mapping/ChatGroupMemberConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/MultipleDatabase/AgentsManagerSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/MultipleDatabase/AgentsManagerSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/MultipleDatabase/AgentsManagerSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/MultipleDatabase/AgentsManagerSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/MultipleDatabase/AgentsManagerSenparcEntities_SQLite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/MultipleDatabase/AgentsManagerSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Services/AIPlugins/CrawlPlugin.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Services/AIPlugins/FormatorPlugin.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Services/AIPlugins/PromptCatalyzerPlugin.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Services/AIPlugins/PromptOptimizationPlugin.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Services/AgentsTemplateService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Services/ChatGroupHistoryService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Services/ChatGroupMemberService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Services/ChatGroupService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Services/ChatTaskService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Services/MyRolePlayOrchestrator.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Services/PromptOptimizationAgentBridge.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Services/PromptOptimizationKernelFallbackService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Domain/Services/PromptOptimizationService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/OHS/Local/AppService/ApiAuthorizeAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/OHS/Remote/Controllers/PromptCatalyzerInitController.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/OHS/Remote/Controllers/PromptOptimizationController.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Register.Area.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Register.Thread.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/wwwroot/css/AgentsManager/fontawesome/all.min.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/wwwroot/css/AgentsManager/index.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/wwwroot/js/AgentsManager/axios.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/wwwroot/js/AgentsManager/index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/wwwroot/js/AgentsManager/lib/axios.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.AgentsManager/wwwroot/js/AgentsManager/lib/marked.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Exceptions/UnSetBackupException.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/Dm/20250108042739_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/Dm/20250108042739_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/Dm/DatabaseToolkitSenparcEntities_DmModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/MySql/20201022044824_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/MySql/20201022044824_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/MySql/20210105155851_Add_TenantId.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/MySql/20210105155851_Add_TenantId.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/MySql/DatabaseToolkitEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/Oracle/20220818152841_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/Oracle/20220818152841_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/Oracle/DatabaseToolkitSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/PostgreSQL/20211114134027_InitLastVersion.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/PostgreSQL/20211114134027_InitLastVersion.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/PostgreSQL/DatabaseToolkitEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/SqlServer/20201022044812_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/SqlServer/20201022044812_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/SqlServer/20210105155838_Add_TenantId.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/SqlServer/20210105155838_Add_TenantId.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/SqlServer/DatabaseToolkitEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/Sqlite/20201022044836_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/Sqlite/20201022044836_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/Sqlite/20210105155826_Add_TenantId.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/Sqlite/20210105155826_Add_TenantId.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Migrations/Sqlite/DatabaseToolkitEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Models/DataBaseModel/DatabaseToolkitSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Models/DataBaseModel/DbConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Models/MultipleDatabase/DatabaseToolkitSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Models/MultipleDatabase/DatabaseToolkitSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Models/MultipleDatabase/DatabaseToolkitSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Models/MultipleDatabase/DatabaseToolkitSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Models/MultipleDatabase/DatabaseToolkitSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Models/MultipleDatabase/DatabaseToolkitSenparcEntities_Sqlite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Domain/Services/DbConfigQueryService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/OHS/Local/AppService/AIAgentIntegrationAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/OHS/Local/AppService/DatabaseBackupAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/OHS/Local/AppService/DatabaseConfigAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/OHS/Local/AppService/DatabaseOperationAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/OHS/Local/AppService/DatabaseSchemaQueryAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/OHS/Local/AppService/DatabaseUpdateAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/OHS/Local/Models/DatabaseSchemaMetadata.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/OHS/Local/PL/DatabaseAutoBackup_IsAutoBackupResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/OHS/Local/PL/DatabaseBackupRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/OHS/Local/Services/DatabaseExecutor.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/OHS/Local/Services/DatabaseSchemaMetadataProvider.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Register.RazorRuntime.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Register.Thread.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DatabaseToolkit/Senparc.Xncf.DatabaseToolkit/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Areas/Admin/Pages/DynamicData/DataSheetSet.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Areas/Admin/Pages/DynamicData/DataSheetSet.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Areas/Admin/Pages/DynamicData/DatabaseSample.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Areas/Admin/Pages/DynamicData/DatabaseSample.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Areas/Admin/Pages/DynamicData/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Areas/Admin/Pages/DynamicData/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Areas/Admin/Pages/DynamicData/LayoutSet.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Areas/Admin/Pages/DynamicData/LayoutSet.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Areas/Admin/Pages/DynamicData/PageSet.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Areas/Admin/Pages/DynamicData/PageSet.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Areas/Admin/Pages/DynamicData/renderLayoutPage.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Areas/Admin/Pages/DynamicData/renderLayoutPage.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Areas/Admin/Pages/Shared/_SideMenu.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Areas/Admin/Pages/Shared/_ViewImports.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Areas/Admin/Pages/Shared/_ViewStart.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/Dm/20250108043102_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/Dm/20250108043102_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/Dm/DynamicDataSenparcEntities_DmModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/MySql/20240423143658_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/MySql/20240423143658_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/MySql/DynamicDataSenparcEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/Oracle/20240423143234_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/Oracle/20240423143234_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/Oracle/DynamicDataSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/PostgreSQL/20240423143225_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/PostgreSQL/20240423143225_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/PostgreSQL/DynamicDataSenparcEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/SqlServer/20240423143154_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/SqlServer/20240423143154_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/SqlServer/20240718100757_Add_Tables.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/SqlServer/20240718100757_Add_Tables.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/SqlServer/DynamicDataSenparcEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/Sqlite/20240423143206_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/Sqlite/20240423143206_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Migrations/Sqlite/DynamicDataSenparcEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/DatabaseModel/Color.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/DatabaseModel/ColumnMetadata.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/DatabaseModel/Dto/ColorDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/DatabaseModel/Dto/ColumnMetadataDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/DatabaseModel/Dto/TableDataDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/DatabaseModel/Dto/TableMetadataDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/DatabaseModel/DynamicDataSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/DatabaseModel/Mapping/DynamicData_ColorConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/DatabaseModel/Mapping/DynamicData_Mapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/DatabaseModel/TableData.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/DatabaseModel/TableMetadata.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/Extensions/ColumnTemplate.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/Extensions/DataTemplate.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/MultipleDatabase/DynamicDataSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/MultipleDatabase/DynamicDataSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/MultipleDatabase/DynamicDataSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/MultipleDatabase/DynamicDataSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/MultipleDatabase/DynamicDataSenparcEntities_SQLite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/MultipleDatabase/DynamicDataSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Services/ColorService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Services/ColumnMetadataService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Services/TableDataService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Domain/Services/TableMetadataService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/OHS/Local/AppService/ApiAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/OHS/Local/AppService/ColorAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/OHS/Local/AppService/MyFuctionAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/OHS/Local/PL/ApiRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/OHS/Local/PL/ColorResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/OHS/Local/PL/MyFunctionRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Register.Area.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/wwwroot/css/DynamicData/dataSheetSet.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/wwwroot/css/DynamicData/interfaceSet.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/wwwroot/css/DynamicData/layoutSet.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/wwwroot/css/DynamicData/renderLayoutPage.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/wwwroot/js/DynamicData/axios.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/wwwroot/js/DynamicData/dataSheetSet.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/wwwroot/js/DynamicData/interfaceSet.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/wwwroot/js/DynamicData/layoutSet.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/wwwroot/js/DynamicData/lib/axios.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.DynamicData/wwwroot/js/DynamicData/renderLayoutPage.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Areas/Admin/Pages/FileManager/DatabaseSample.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Areas/Admin/Pages/FileManager/DatabaseSample.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Areas/Admin/Pages/FileManager/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Areas/Admin/Pages/FileManager/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Areas/Admin/Pages/Shared/_SideMenu.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Areas/Admin/Pages/Shared/_ViewImports.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Areas/Admin/Pages/Shared/_ViewStart.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/Dm/20260107132639_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/Dm/20260107132639_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/Dm/FileManagerSenparcEntities_DmModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/MySql/20260107132600_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/MySql/20260107132600_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/MySql/FileManagerSenparcEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/Oracle/20260107132626_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/Oracle/20260107132626_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/Oracle/FileManagerSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/PostgreSQL/20260107132613_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/PostgreSQL/20260107132613_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/PostgreSQL/FileManagerSenparcEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/SqlServer/20260107132546_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/SqlServer/20260107132546_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/SqlServer/FileManagerSenparcEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/Sqlite/20260107132533_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/Sqlite/20260107132533_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Migrations/Sqlite/FileManagerSenparcEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Models/DatabaseModel/Color.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Models/DatabaseModel/Dto/ColorDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Models/DatabaseModel/Dto/NcfFileDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Models/DatabaseModel/FileManagerSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Models/DatabaseModel/Mapping/FileManager_ColorConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Models/DatabaseModel/NcfFile.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Models/DatabaseModel/NcfFolder.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Models/MultipleDatabase/FileManagerSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Models/MultipleDatabase/FileManagerSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Models/MultipleDatabase/FileManagerSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Models/MultipleDatabase/FileManagerSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Models/MultipleDatabase/FileManagerSenparcEntities_SQLite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Models/MultipleDatabase/FileManagerSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Services/ColorService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Services/NcfFileService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Domain/Services/NcfFolderService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/OHS/Local/AppService/ApiAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/OHS/Local/AppService/ColorAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/OHS/Local/AppService/FileTemplateAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/OHS/Local/AppService/MyFuctionAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/OHS/Local/PL/ApiRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/OHS/Local/PL/ColorResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/OHS/Local/PL/DeleteFileRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/OHS/Local/PL/MyFunctionRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/OHS/Local/PL/Response/FileTemplate_GetListResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Register.Area.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/wwwroot/css/Admin/FileManager/index.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.FileManager/wwwroot/js/Admin/FileManager/index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Areas/Admin/Pages/KnowledgeBase/Edit.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Areas/Admin/Pages/KnowledgeBase/Edit.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Areas/Admin/Pages/KnowledgeBase/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Areas/Admin/Pages/KnowledgeBase/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Areas/Admin/Pages/KnowledgeBase/RecallTest.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Areas/Admin/Pages/KnowledgeBase/RecallTest.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Areas/Admin/Pages/Shared/_SideMenu.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Areas/Admin/Pages/Shared/_ViewImports.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Areas/Admin/Pages/Shared/_ViewStart.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/Dm/20260107134040_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/Dm/20260107134040_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/Dm/KnowledgeBaseSenparcEntities_DmModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/MySql/20260107133958_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/MySql/20260107133958_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/MySql/KnowledgeBaseSenparcEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/Oracle/20260107134025_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/Oracle/20260107134025_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/Oracle/KnowledgeBaseSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/PostgreSQL/20260107134011_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/PostgreSQL/20260107134011_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/PostgreSQL/KnowledgeBaseSenparcEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/SqlServer/20260107133944_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/SqlServer/20260107133944_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/SqlServer/KnowledgeBaseSenparcEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/Sqlite/20260107133930_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/Sqlite/20260107133930_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Migrations/Sqlite/KnowledgeBaseSenparcEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/DatabaseModel/Color.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/DatabaseModel/Config/AllowFileExtension.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/DatabaseModel/Config/StaticResourceSetting.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/DatabaseModel/Dto/ColorDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/DatabaseModel/Dto/KnowledgeBaseDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/DatabaseModel/Dto/KnowledgeBaseItemDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/DatabaseModel/KnowledgeBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/DatabaseModel/KnowledgeBaseItem.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/DatabaseModel/KnowledgeBaseSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/DatabaseModel/Mapping/Admin_KnowledgeBaseConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/DatabaseModel/Mapping/Admin_KnowledgeBaseItemConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/DatabaseModel/Mapping/KnowledgeBase_ColorConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/DatabaseModel/Request/KnowledgeBaseItemRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/DatabaseModel/Request/KnowledgeBaseRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/MultipleDatabase/KnowledgeBaseSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/MultipleDatabase/KnowledgeBaseSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/MultipleDatabase/KnowledgeBaseSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/MultipleDatabase/KnowledgeBaseSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/MultipleDatabase/KnowledgeBaseSenparcEntities_SQLite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/MultipleDatabase/KnowledgeBaseSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Services/ColorService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Services/KnowledgeBaseService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Domain/Services/KnowledgeBasesItemService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/OHS/Local/AppService/ApiAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/OHS/Local/AppService/ColorAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/OHS/Local/AppService/CommonService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/OHS/Local/AppService/KnowledgeBaseAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/OHS/Local/AppService/KnowledgeBaseItemAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/OHS/Local/AppService/MyFuctionAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/OHS/Local/AppService/RecallTestAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/OHS/Local/PL/ApiRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/OHS/Local/PL/ColorResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/OHS/Local/PL/KnowledgeBaseRequests.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/OHS/Local/PL/MyFunctionRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/OHS/Local/PL/Request/ImportFilesRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/OHS/Local/PL/Request/KnowledgeBasesDetailRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/OHS/Local/PL/Request/KnowledgeBasesRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/OHS/Local/PL/Request/RecallTestRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/OHS/Local/PL/Response/RecallTestResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Register.Area.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/wwwroot/css/KnowledgeBase/KnowledgeBase/KnowledgeBase.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/wwwroot/css/KnowledgeBase/KnowledgeBasesDetail/KnowledgeBasesDetail.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/wwwroot/css/KnowledgeBase/RecallTest/RecallTest.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/wwwroot/js/KnowledgeBase/Pages/KnowledgeBase/knowledgeBase.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/wwwroot/js/KnowledgeBase/Pages/KnowledgeBasesDetail/knowledgeBasesDetail.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.KnowledgeBase/wwwroot/js/KnowledgeBase/Pages/RecallTest/recallTest.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Areas/Admin/Pages/MCP/DatabaseSample.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Areas/Admin/Pages/MCP/DatabaseSample.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Areas/Admin/Pages/MCP/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Areas/Admin/Pages/MCP/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Areas/Admin/Pages/Shared/_SideMenu.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Areas/Admin/Pages/Shared/_ViewImports.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Areas/Admin/Pages/Shared/_ViewStart.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Migrations/MySql/20240423143658_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Migrations/MySql/20240423143658_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Migrations/MySql/MCPSenparcEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Migrations/Oracle/20240423143234_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Migrations/Oracle/20240423143234_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Migrations/Oracle/MCPSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Migrations/PostgreSQL/20240423143225_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Migrations/PostgreSQL/20240423143225_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Migrations/PostgreSQL/MCPSenparcEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Migrations/SqlServer/20240423143154_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Migrations/SqlServer/20240423143154_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Migrations/SqlServer/MCPSenparcEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Migrations/Sqlite/20240423143206_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Migrations/Sqlite/20240423143206_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Migrations/Sqlite/MCPSenparcEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Models/DatabaseModel/Color.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Models/DatabaseModel/Dto/ColorDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Models/DatabaseModel/MCPEndpoint.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Models/DatabaseModel/MCPSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Models/DatabaseModel/Mapping/MCP_ColorConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Models/MultipleDatabase/MCPSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Models/MultipleDatabase/MCPSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Models/MultipleDatabase/MCPSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Models/MultipleDatabase/MCPSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Models/MultipleDatabase/MCPSenparcEntities_SQLite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Models/MultipleDatabase/MCPSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Services/ColorService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Services/MCPEndpointService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Domain/Services/McpServerService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/OHS/Local/AppService/ApiAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/OHS/Local/AppService/ColorAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/OHS/Local/AppService/MCPEndpointAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/OHS/Local/AppService/MyFuctionAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/OHS/Local/PL/ApiRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/OHS/Local/PL/ColorResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/OHS/Local/PL/MyFunctionRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Register.Area.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.MCP/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/AppServices/ApiAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/AppServices/LlmModelAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/AppServices/PromptItemAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/AppServices/PromptRangeAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/AppServices/PromptResultAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/AppServices/StatisticAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Extensions/PromptRangeItemHelper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Request/ApiRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Request/LlModel_AddRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Request/LlmModel_ModifyRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Request/PromptItem_AddRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Request/PromptItem_ExportRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Request/PromptItem_ModifyRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Request/PromptRange_AddRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Request/PromptResult_ContinueChatRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Request/PromptResult_HumanScoreRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Request/PromptResult_RobotScoreRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Request/PromptResult_UpdateChatFeedbackRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Response/BaseResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Response/LlModel_GetIdAndNameResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Response/LlModel_GetPageResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Response/PromptItem_AddResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Response/PromptItem_GetIdAndNameResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Response/PromptItem_GetRangeNameListResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Response/PromptItem_GetResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Response/PromptItem_HistoryScoreResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Response/PromptResult_ChatHistoryWithPromptResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Response/PromptResult_ListResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Response/SenparcAI_GetByVersionResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Response/Statistic_LineChartDataResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Response/Statistics_TodayTacticResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/DTOs/Response/TacticTree_GetResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Application/EventHandlers/PromptInitRequestHandler.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Areas/Admin/Pages/PromptRange/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Areas/Admin/Pages/PromptRange/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Areas/Admin/Pages/PromptRange/Model.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Areas/Admin/Pages/PromptRange/Model.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Areas/Admin/Pages/PromptRange/Prompt.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Areas/Admin/Pages/PromptRange/Prompt.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Areas/Admin/Pages/Shared/_SideMenu.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Areas/Admin/Pages/Shared/_ViewImports.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Areas/Admin/Pages/Shared/_ViewStart.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Dm/20251213122556_Add_PromptResultChat.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Dm/20251213122556_Add_PromptResultChat.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Dm/20251216182159_Add_PromptResult_SystemMessage.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Dm/20251216182159_Add_PromptResult_SystemMessage.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Dm/PromptRangeSenparcEntities_DmModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Migrations.SqlServer/20240105152737_Init2024.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Migrations.SqlServer/20240105152737_Init2024.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Migrations.SqlServer/20240112023246_UseDecimal.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Migrations.SqlServer/20240112023246_UseDecimal.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Migrations.SqlServer/20250816100529_Add_IsAIGrade.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Migrations.SqlServer/20250816100529_Add_IsAIGrade.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/MySql/20240514093951_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/MySql/20240514093951_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/MySql/20251213122406_Add_PromptResultChat.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/MySql/20251213122406_Add_PromptResultChat.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/MySql/20251216182034_Add_PromptResult_SystemMessage.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/MySql/20251216182034_Add_PromptResult_SystemMessage.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/MySql/PromptRangeSenparcEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Oracle/20240514091920_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Oracle/20240514091920_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Oracle/20251213122517_Add_PromptResultChat.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Oracle/20251213122517_Add_PromptResultChat.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Oracle/20251216182132_Add_PromptResult_SystemMessage.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Oracle/20251216182132_Add_PromptResult_SystemMessage.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Oracle/PromptRangeSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/PostgreSQL/20240514091906_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/PostgreSQL/20240514091906_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/PostgreSQL/20251213122436_Add_PromptResultChat.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/PostgreSQL/20251213122436_Add_PromptResultChat.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/PostgreSQL/20251216182102_Add_PromptResult_SystemMessage.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/PostgreSQL/20251216182102_Add_PromptResult_SystemMessage.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/PostgreSQL/PromptRangeSenparcEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/SqlServer/20251213113348_Add_PromotResultChat.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/SqlServer/20251213113348_Add_PromotResultChat.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/SqlServer/20251216182007_Add_PromptResult_SystemMessage.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/SqlServer/20251216182007_Add_PromptResult_SystemMessage.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/SqlServer/PromptRangeSenparcEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Sqlite/20240515040341_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Sqlite/20240515040341_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Sqlite/20250816100504_Add_IsAIGrade.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Sqlite/20250816100504_Add_IsAIGrade.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Sqlite/20251213122131_Add_PromptResultChat.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Sqlite/20251213122131_Add_PromptResultChat.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Sqlite/20251213133522_Add_PromptResultChat_AddTime.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Sqlite/20251213133522_Add_PromptResultChat_AddTime.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Sqlite/20251216181941_Add_PromptResult_SystemMessage.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Sqlite/20251216181941_Add_PromptResult_SystemMessage.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Migrations/Sqlite/PromptRangeSenparcEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/DatabaseModel/Dto/Constants.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/DatabaseModel/Dto/LlModelDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/DatabaseModel/Dto/PromptItemDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/DatabaseModel/Dto/PromptRangeDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/DatabaseModel/Dto/PromptResultChatDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/DatabaseModel/Dto/PromptResultDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/DatabaseModel/LlModel.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/DatabaseModel/Mapping/PromptItemConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/DatabaseModel/Mapping/PromptResultChatConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/DatabaseModel/PromptItem.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/DatabaseModel/PromptRange.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/DatabaseModel/PromptRangeSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/DatabaseModel/PromptResult.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/DatabaseModel/PromptResultChat.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/Entities/PromptItemTreeList.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/Entities/PromptItemTreeNode.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/Entities/PromptItemVersion.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/MultipleDatabase/PromptRangeSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/MultipleDatabase/PromptRangeSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/MultipleDatabase/PromptRangeSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/MultipleDatabase/PromptRangeSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/MultipleDatabase/PromptRangeSenparcEntities_SQLite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/MultipleDatabase/PromptRangeSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Services/LlModelService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Services/PromptItemService.Plugins.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Services/PromptItemService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Services/PromptRangeService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Services/PromptResultChatService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Services/PromptResultService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Domain/Services/PromptService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/OHS/Local/AppService/ApiAuthorizeAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Register.Area.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/css/PromptRange/index.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/css/PromptRange/model.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/css/PromptRange/prompt.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/axios.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/echarts/echarts-gl.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/echarts/echarts.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/lib/OrbitControls.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/lib/axios.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/lib/diff.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/lib/fileSave.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/lib/jszip.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/lib/marked.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/lib/three.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/model.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/prompt.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/utils/copyHelper.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/utils/dateHelper.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/utils/htmlHelper.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/utils/nameHelper.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/utils/storageHelper.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange/wwwroot/js/PromptRange/utils/test-utils.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange.Abstractions/Events/PromptInitEvents.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange.Abstractions/Events/PromptOptimizationEvents.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.PromptRange.Abstractions/Events/PromptTestFinishedEvent.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Areas/Admin/Pages/SenMapic/DatabaseSample.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Areas/Admin/Pages/SenMapic/DatabaseSample.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Areas/Admin/Pages/SenMapic/Task/Detail.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Areas/Admin/Pages/SenMapic/Task/Detail.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Areas/Admin/Pages/SenMapic/Task/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Areas/Admin/Pages/SenMapic/Task/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Areas/Admin/Pages/Shared/_SideMenu.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Areas/Admin/Pages/Shared/_ViewImports.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Areas/Admin/Pages/Shared/_ViewStart.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Migrations/MySql/20240423143658_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Migrations/MySql/20240423143658_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Migrations/MySql/KnowledgeBaseSenparcEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Migrations/Oracle/20240423143234_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Migrations/Oracle/20240423143234_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Migrations/Oracle/KnowledgeBaseSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Migrations/PostgreSQL/20240423143225_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Migrations/PostgreSQL/20240423143225_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Migrations/PostgreSQL/KnowledgeBaseSenparcEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Migrations/SqlServer/20240423143154_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Migrations/SqlServer/20240423143154_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Migrations/SqlServer/KnowledgeBaseSenparcEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Migrations/Sqlite/20240423143206_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Migrations/Sqlite/20240423143206_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Migrations/Sqlite/KnowledgeBaseSenparcEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Models/DatabaseModel/Color.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Models/DatabaseModel/Dto/ColorDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Models/DatabaseModel/Dto/SenMapicTaskDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Models/DatabaseModel/Dto/SenMapicTaskItemDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Models/DatabaseModel/Mapping/SenMapic_ColorConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Models/DatabaseModel/Mapping/SenMapic_TaskConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Models/DatabaseModel/SenMapicSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Models/DatabaseModel/SenMapicTask.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Models/DatabaseModel/SenMapicTaskItem.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Models/MultipleDatabase/FileManagerSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Models/MultipleDatabase/FileManagerSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Models/MultipleDatabase/FileManagerSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Models/MultipleDatabase/FileManagerSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Models/MultipleDatabase/FileManagerSenparcEntities_SQLite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Models/MultipleDatabase/FileManagerSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/SenMapic/Entities/BaiduResult.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/SenMapic/SenMapicClasses.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/SenMapic/SenMapicEngine.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/SenMapic/SenMapicEngineExtension.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/SenMapic/SenMapicEngineExtensionForWeb.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/SenMapic/SenMapicSynData.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/SenMapic/SenMapicUtility.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/SenMapic/SiteMap/AutoAlertSitemapUtility.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/SenMapic/SiteMap/BuildGoogleSitemapWithReport.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/SenMapic/SiteMap/SiteMapHandler.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Services/ColorService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Services/SenMapicTaskItemService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Domain/Services/SenMapicTaskService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/OHS/Local/AppService/ApiAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/OHS/Local/AppService/ColorAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/OHS/Local/AppService/MyFuctionAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/OHS/Local/PL/ApiRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/OHS/Local/PL/ColorResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/OHS/Local/PL/MyFunctionRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Register.Area.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/wwwroot/css/Admin/FileManager/index.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/wwwroot/css/SenMapic/task-detail.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/wwwroot/js/Admin/FileManager/index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/wwwroot/js/SenMapic/task-detail.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.SenMapic/wwwroot/js/SenMapic/task.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/Builder/CustomSwaggerServiceCollectionExtensions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/Builder/SwaggerBuilderExtensions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/Convention/ApiExplorerGroupPerVersionConvention.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/Filters/SwaggerDefaultValueFilter.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/Filters/SwaggerFileUploadFilter.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/Models/CustomSwaggerAuth.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/Models/CustsomSwaggerOptions.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/Models/DataBaseModel/Config.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/Models/SwaggerEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/Register.Area.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/Utils/ConfigurationHelpler.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/Utils/SecurityHelper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/Utils/VersionHelper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/index.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/login.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Swagger/wwwroot/js/swagger.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Terminal/OHS/Local/AppService/TerminalAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Terminal/OHS/PL/TerminalRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.Terminal/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Areas/Admin/Pages/WeixinManager/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Areas/Admin/Pages/WeixinManager/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Areas/Admin/Pages/WeixinManager/MpAccount/Edit.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Areas/Admin/Pages/WeixinManager/MpAccount/Edit.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Areas/Admin/Pages/WeixinManager/MpAccount/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Areas/Admin/Pages/WeixinManager/MpAccount/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Areas/Admin/Pages/WeixinManager/WeixinUser/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Areas/Admin/Pages/WeixinManager/WeixinUser/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Areas/Admin/Pages/WeixinManager/_ViewImports.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Areas/Admin/Pages/WeixinManager/_ViewStart.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Cache/WeixinFaceCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/Dm/20250109164617_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/Dm/20250109164617_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/Dm/20250710145423_UpdateNewColumn.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/Dm/20250710145423_UpdateNewColumn.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/Dm/WeixinSenparcEntities_DmModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/MySql/20240105082108_Add_PromptRangeCode.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/MySql/20240105082108_Add_PromptRangeCode.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/MySql/20250711064824_UpdateNewColumn.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/MySql/20250711064824_UpdateNewColumn.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/MySql/WeixinSenparcEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/Oracle/20250109164729_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/Oracle/20250109164729_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/Oracle/20250710145413_UpdateNewColumn.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/Oracle/20250710145413_UpdateNewColumn.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/Oracle/WeixinSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/PostgreSQL/20240105082117_Add_PromptRangeCode.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/PostgreSQL/20240105082117_Add_PromptRangeCode.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/PostgreSQL/20250710145404_UpdateNewColumn.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/PostgreSQL/20250710145404_UpdateNewColumn.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/PostgreSQL/WeixinSenparcEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/SqlServer/20240105082058_Add_PromptRangeCode.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/SqlServer/20240105082058_Add_PromptRangeCode.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/SqlServer/20250711065513_UpdateNewColumn.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/SqlServer/20250711065513_UpdateNewColumn.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/SqlServer/WeixinSenparcEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/Sqlite/20240105082050_Add_PromptRangeCode.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/Sqlite/20240105082050_Add_PromptRangeCode.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/Sqlite/20250710145338_UpdateNewColumn.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/Sqlite/20250710145338_UpdateNewColumn.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Migrations/Sqlite/WeixinSenparcEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/AutoMapper/WeixinManagerProfile.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/DatabaseModel/Dto/MpAccountDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/DatabaseModel/Dto/UserTagDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/DatabaseModel/Dto/UserTag_WeixinUserDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/DatabaseModel/Dto/WeixinUserDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/DatabaseModel/Mapping/UserTagConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/DatabaseModel/Mapping/UserTag_WeixinUserConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/DatabaseModel/Mapping/WeixinUserConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/DatabaseModel/MpAccount.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/DatabaseModel/UserTag.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/DatabaseModel/UserTag_WeixinUser.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/DatabaseModel/WeixinSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/DatabaseModel/WeixinUser.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/MultipleDatabase/WeixinSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/MultipleDatabase/WeixinSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/MultipleDatabase/WeixinSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/MultipleDatabase/WeixinSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/MultipleDatabase/WeixinSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/MultipleDatabase/WeixinSenparcEntities_Sqlite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/VD/Admin/WeixinManager/BaseAdminWeixinManagerModel.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Models/WeixinFace.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Services/MpAccountService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/Services/WeixinService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/WeixinTemplate/WeixinTemplateBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Domain/WeixinTemplate/WeixinTemplate_AuthNotice.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Enum.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/MessageHandler/AiMessageContext.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/MessageHandler/MpMessageHandlerAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/MessageHandler/XncfMpMessageHandler.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/OHS/Remote/Controllers/FindWeixinApi.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Pages/Shared/_Layout.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Pages/Shared/_ValidationScriptsPartial.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Pages/_ViewImports.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Register.Areas.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Register.Middleware.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Register.RazorRuntime.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.WeixinManager/Senparc.Xncf.WeixinManager/wwwroot/js/swagger.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Enums.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/Dm/20250108042450_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/Dm/20250108042450_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/Dm/XncfBuilderSenparcEntities_DmModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/MySql/20201021150407_AddConfig.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/MySql/20201021150407_AddConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/MySql/20210105160434_AddTenantId.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/MySql/20210105160434_AddTenantId.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/MySql/20211109111700_Add-Description.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/MySql/20211109111700_Add-Description.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/MySql/XncfBuilderEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/Oracle/20220818151558_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/Oracle/20220818151558_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/Oracle/XncfBuilderSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/PostgreSQL/20211114113553_InitLastVersion.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/PostgreSQL/20211114113553_InitLastVersion.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/PostgreSQL/XncfBuilderEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/SqlServer/20201007161811_AddConfig.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/SqlServer/20201007161811_AddConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/SqlServer/20210105160425_AddTenantId.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/SqlServer/20210105160425_AddTenantId.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/SqlServer/20211109111606_Add-Description.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/SqlServer/20211109111606_Add-Description.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/SqlServer/XncfBuilderEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/Sqlite/20201007170647_AddConfig.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/Sqlite/20201007170647_AddConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/Sqlite/20210105160415_AddTenantId.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/Sqlite/20210105160415_AddTenantId.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/Sqlite/20211109111509_Add-Description.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/Sqlite/20211109111509_Add-Description.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Migrations/Sqlite/XncfBuilderEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Models/DatabaseModel/Config.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Models/DatabaseModel/ConfigDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Models/DatabaseModel/XncfBuilderSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Models/MultipleDatabase/XncfBuilderSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Models/MultipleDatabase/XncfBuilderSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Models/MultipleDatabase/XncfBuilderSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Models/MultipleDatabase/XncfBuilderSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Models/MultipleDatabase/XncfBuilderSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Models/MultipleDatabase/XncfBuilderSenparcEntities_Sqlite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Models/Services/ConfigService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Services/Plugins/FileGenerateResult.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Services/Plugins/FilePlugin.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Domain/Services/PromptBuilderService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/OHS/Local/BuildXncfAppService.AI.MCP.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/OHS/Local/BuildXncfAppService.AI.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/OHS/Local/BuildXncfAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/OHS/Local/DatabaseMigrationsAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/OHS/Local/GenerateAppServiceInterface.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/OHS/PL/BuildXncfRequest.AI.MCP.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/OHS/PL/BuildXncfRequest.AI.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/OHS/PL/BuildXncfRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/OHS/PL/DatabaseMigrationRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/OHS/PL/GenerateAppServiceInterfaceRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/Request.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder/ResponseClass.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/Senparc.Xncf.XncfBuilder/Senparc.Xncf.XncfBuilder.DynamicContentGenerator/MultiFileCodeGenerator.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.AreasBase/AreaRegister.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.AreasBase/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/Dm/20250108041346_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/Dm/20250108041346_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/Dm/MenuSenparcEntities_DmModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/MySql/20211128080845_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/MySql/20211128080845_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/MySql/MenuSenparcEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/Oracle/20220818143505_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/Oracle/20220818143505_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/Oracle/MenuSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/PostgreSQL/20211128080857_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/PostgreSQL/20211128080857_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/PostgreSQL/MenuSenparcEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/SqlServer/20211128080402_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/SqlServer/20211128080402_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/SqlServer/MenuSenparcEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/Sqlite/20211128080352_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/Sqlite/20211128080352_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Migrations/Sqlite/MenuSenparcEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Models/DatabaseModel/MenuSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Models/MultipleDatabase/MenuSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Models/MultipleDatabase/MenuSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Models/MultipleDatabase/MenuSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Models/MultipleDatabase/MenuSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Models/MultipleDatabase/MenuSenparcEntities_SQLite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Models/MultipleDatabase/MenuSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Menu/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemCore/ACL/Repository/SystemConfigRepository.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemCore/Domain/Database/BasePoolEntities/BasePoolEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemCore/Domain/Database/BasePoolEntities/MultipleDatabase/BasePoolEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemCore/Domain/Database/BasePoolEntities/MultipleDatabase/BasePoolEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemCore/Domain/Database/BasePoolEntities/MultipleDatabase/BasePoolEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemCore/Domain/Database/BasePoolEntities/MultipleDatabase/BasePoolEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemCore/Domain/Database/BasePoolEntities/MultipleDatabase/BasePoolEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemCore/Domain/Database/BasePoolEntities/MultipleDatabase/BasePoolEntities_Sqlite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemCore/Domain/Database/BasePoolEntities/MultipleDatabase/BasePoolEntities_UnitTest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemCore/Domain/Database/NcfDbData/NcfClientDbData.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemCore/Domain/Database/SenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemCore/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemCore/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/ACL/Repository/FeedBackRepository.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/ACL/Repository/SystemConfigRepository.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/Dm/20250108041720_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/Dm/20250108041720_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/Dm/SystemManagerSenparcEntities_DmModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/MySql/20211211102713_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/MySql/20211211102713_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/MySql/20240927144029_Add_NeuCharAccountInfo_To_SystemConfig.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/MySql/20240927144029_Add_NeuCharAccountInfo_To_SystemConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/MySql/SystemManagerSenparcEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/Oracle/20220818145506_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/Oracle/20220818145506_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/Oracle/20240826155305_Add_NeuCharAccountInfo_To_SystemConfig.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/Oracle/20240826155305_Add_NeuCharAccountInfo_To_SystemConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/Oracle/SystemManagerSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/PostgreSQL/20211211102732_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/PostgreSQL/20211211102732_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/PostgreSQL/20240826155316_Add_NeuCharAccountInfo_To_SystemConfig.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/PostgreSQL/20240826155316_Add_NeuCharAccountInfo_To_SystemConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/PostgreSQL/SystemManagerSenparcEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/SqlServer/20211211102753_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/SqlServer/20211211102753_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/SqlServer/20240826155251_Add_NeuCharAccountInfo_To_SystemConfig.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/SqlServer/20240826155251_Add_NeuCharAccountInfo_To_SystemConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/SqlServer/SystemManagerSenparcEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/Sqlite/20211211102658_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/Sqlite/20211211102658_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/Sqlite/20240826155338_Add_NeuCharAccountInfo_To_SystemConfig.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/Sqlite/20240826155338_Add_NeuCharAccountInfo_To_SystemConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Migrations/Sqlite/SystemManagerSenparcEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Models/DatabaseModel/ExtensionEntity.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Models/DatabaseModel/FeedBack.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Models/DatabaseModel/Mapping/FeedbackConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Models/DatabaseModel/MultipleDatabase/SenparcDbContextFactoryConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Models/DatabaseModel/MultipleDatabase/SystemManagerSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Models/DatabaseModel/MultipleDatabase/SystemManagerSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Models/DatabaseModel/MultipleDatabase/SystemManagerSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Models/DatabaseModel/MultipleDatabase/SystemManagerSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Models/DatabaseModel/MultipleDatabase/SystemManagerSenparcEntities_SQLite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Models/DatabaseModel/MultipleDatabase/SystemManagerSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Models/DatabaseModel/SystemManagerSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Services/FeedBackService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Services/SystemConfigService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Domain/Services/SystemConfigServiceBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/OHS/Local/AppService/SystemConfigAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/OHS/Local/PL/SystemConfig_Request.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemManager/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/Dm/20250108041652_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/Dm/20250108041652_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/Dm/SystemPermissionSenparcEntities_DmModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/Mysql/20211212114408_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/Mysql/20211212114408_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/Mysql/SystemPermissionSenparcEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/Oracle/20220818145153_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/Oracle/20220818145153_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/Oracle/20241016154411_Update_Role_Column_Length.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/Oracle/20241016154411_Update_Role_Column_Length.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/Oracle/SystemPermissionSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/PostgreSQL/20211212114422_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/PostgreSQL/20211212114422_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/PostgreSQL/20241016154358_Update_Role_Column_Length.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/PostgreSQL/20241016154358_Update_Role_Column_Length.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/PostgreSQL/SystemPermissionSenparcEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/SqlServer/20211212114439_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/SqlServer/20211212114439_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/SqlServer/20241016154332_Update_Role_Column_Length.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/SqlServer/20241016154332_Update_Role_Column_Length.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/SqlServer/SystemPermissionSenparcEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/Sqlite/20211212114352_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/Sqlite/20211212114352_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/Sqlite/20241016154316_Update_Role_Column_Length.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/Sqlite/20241016154316_Update_Role_Column_Length.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Migrations/Sqlite/SystemPermissionSenparcEntities_SQLiteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Models/DatabaseModel/SystemPermissionSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Models/MultipleDatabase/SystemPermissionSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Models/MultipleDatabase/SystemPermissionSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Models/MultipleDatabase/SystemPermissionSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Models/MultipleDatabase/SystemPermissionSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Models/MultipleDatabase/SystemPermissionSenparcEntities_SQLite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Domain/Models/MultipleDatabase/SystemPermissionSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.SystemPermission/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/ACL/Repository/TenantInfoRepository.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Cache/FullTenantInfoCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/DatabaseModel/AutoMapper/TenantInfoProfile.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/DatabaseModel/MultipleDatabase/SenparcDbContextFactoryConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/DatabaseModel/MultipleDatabase/TenantSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/DatabaseModel/MultipleDatabase/TenantSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/DatabaseModel/MultipleDatabase/TenantSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/DatabaseModel/MultipleDatabase/TenantSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/DatabaseModel/MultipleDatabase/TenantSenparcEntities_SQLite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/DatabaseModel/MultipleDatabase/TenantSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/DatabaseModel/SenparcEntitiesMultiTenant.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/DatabaseModel/TenantInfo.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/DatabaseModel/TenantInfoDbData.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/DatabaseModel/TenantSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/Dm/20250108042232_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/Dm/20250108042232_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/Dm/TenantSenparcEntities_DmModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/Mysql/20211211105015_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/Mysql/20211211105015_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/Mysql/TenantSenparcEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/Oracle/20220818145836_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/Oracle/20220818145836_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/Oracle/TenantSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/PostgreSQL/20211211105030_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/PostgreSQL/20211211105030_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/PostgreSQL/TenantSenparcEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/SqlServer/20211211105040_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/SqlServer/20211211105040_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/SqlServer/TenantSenparcEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/Sqlite/20211211105002_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/Sqlite/20211211105002_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Migrations/Sqlite/TenantSenparcEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Domain/Services/TenantInfoService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/OHS/Remote/TenantMiddleware.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.Tenant.Interface/Domain/DatabaseModel/Dto/TenantInfoDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/Dm/20250108041617_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/Dm/20250108041617_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/Dm/XncfModuleManagerSenparcEntities_DmModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/Mysql/20211211134009_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/Mysql/20211211134009_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/Mysql/XncfModuleManagerSenparcEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/Oracle/20220818144845_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/Oracle/20220818144845_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/Oracle/XncfModuleManagerSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/PostgreSQL/20211211134020_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/PostgreSQL/20211211134020_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/PostgreSQL/XncfModuleManagerSenparcEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/SqlServer/20211211134031_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/SqlServer/20211211134031_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/SqlServer/XncfModuleManagerSenparcEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/Sqlite/20211211133958_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/Sqlite/20211211133958_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Migrations/Sqlite/XncfModuleManagerSenparcEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/DatabaseModel/Mapping/XscfModuleAccountConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/DatabaseModel/XncfModuleManagerSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_SQLite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Models/MultipleDatabase/XncfModuleManagerSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Domain/Services/XncfModuleServiceExtension.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/OHS/Local/AppService/XncfStateAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/OHS/Local/PL/XncfStateRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/src/Extensions/System/Senparc.Xncf.XncfModuleManager/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/ACL/Repository/AdminChatMessageRepository.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/ACL/Repository/AdminChatSessionModuleRepository.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/ACL/Repository/AdminChatSessionRepository.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/ACL/Repository/AdminUserInfoRepository.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/ACL/Repository/SysMenuRepository.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/ACL/Repository/SysRolePermissionRepository.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/AdminOrJwtAuthorizeAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/AdminChat/Chat.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/AdminChat/Chat.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/AdminUserInfo/AuthorizationPage.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/AdminUserInfo/AuthorizationPage.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/AdminUserInfo/Edit.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/AdminUserInfo/Edit.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/AdminUserInfo/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/AdminUserInfo/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Login.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Login.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Menu/Edit.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Menu/Edit.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Menu/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Menu/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Role/Edit.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Role/Edit.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Role/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Role/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Role/Permission.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Role/Permission.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/SenparcTrace/DateLog.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/SenparcTrace/DateLog.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/SenparcTrace/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/SenparcTrace/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Shared/_ChatModuleSelectorDialog.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Shared/_HeaderPartial.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Shared/_Layout_Vue.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Shared/_MenuPartial.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Shared/_XncfModuleLayout.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/Shared/_XncfModuleLayout_Menu.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/SystemConfig/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/SystemConfig/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/TenantInfo/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/TenantInfo/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/XncfModule/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/XncfModule/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/XncfModule/Start.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/XncfModule/Start.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/_ViewImports.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Areas/Admin/Pages/_ViewStart.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/AutoMpperProfiles/SenparcAreaAdminAutoMapperProfile.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/BackendJwtAuthorizeAttribute.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/BaseAdminPageModel.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Dto/SysRoleDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Dto/SystemConfigDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/Dm/20260325233351_Add_Chat_Tables.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/Dm/20260325233351_Add_Chat_Tables.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/Dm/AdminSenparcEntities_DmModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/MySql/20211225064239_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/MySql/20211225064239_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/MySql/20260325233100_Add_Chat_Tables.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/MySql/20260325233100_Add_Chat_Tables.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/MySql/AdminSenparcEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/Oracle/20220818163958_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/Oracle/20220818163958_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/Oracle/20260325233313_Add_Chat_Tables.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/Oracle/20260325233313_Add_Chat_Tables.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/Oracle/AdminSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/PostgreSQL/20211225064146_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/PostgreSQL/20211225064146_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/PostgreSQL/20260325233136_Add_Chat_Tables.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/PostgreSQL/20260325233136_Add_Chat_Tables.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/PostgreSQL/AdminSenparcEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/SqlServer/20211225064300_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/SqlServer/20211225064300_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/SqlServer/20260325233440_Add_Chat_Tables.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/SqlServer/20260325233440_Add_Chat_Tables.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/SqlServer/AdminSenparcEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/Sqlite/20211225064050_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/Sqlite/20211225064050_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/Sqlite/20260325231127_Add_Chat_Tables.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/Sqlite/20260325231127_Add_Chat_Tables.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Migrations/Sqlite/AdminSenparcEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/DatabaseModel/AdminChatMessage.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/DatabaseModel/AdminChatSession.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/DatabaseModel/AdminChatSessionModule.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/DatabaseModel/AdminSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/DatabaseModel/AdminUserInfo.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/DatabaseModel/Dto/AccountDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/DatabaseModel/Dto/AdminChatMessageDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/DatabaseModel/Dto/AdminChatSessionDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/DatabaseModel/Dto/AdminChatSessionModuleDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/DatabaseModel/Dto/BaseDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/DatabaseModel/Dto/BaseQueryDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/DatabaseModel/Dto/XncfModuleDisplayDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/DatabaseModel/Mapping/AdminUserInfoConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/MultipleDatabase/AdminSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/MultipleDatabase/AdminSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/MultipleDatabase/AdminSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/MultipleDatabase/AdminSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/MultipleDatabase/AdminSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/MultipleDatabase/AdminSenparcEntities_Sqlite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/VD/BasePageModel.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/VD/BaseVD.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/VD/ErrorVD.Core.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/VD/HomeVD.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Models/VD/LoginVD.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/SeparcTraceManager/SenparcTraceHelper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/SeparcTraceManager/SenparcTraceItem.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/SeparcTraceManager/SenparcTraceType.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Services/AIPlugins/ModuleAssistantPlugin.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Services/AdminChatAiService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Services/AdminChatMessageService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Services/AdminChatSessionModuleService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Services/AdminChatSessionService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Services/AdminUserInfoService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Services/BaseClientService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Services/InstallerService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Services/SysMenuService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Services/SysRolePermissionService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/Services/SysRoleService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Domain/TagHelpers/AuthorizationMenuTagHelper.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/JwtSettings.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/MyMessageHandler.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/OHS/Local/AppService/AdminChatAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/OHS/Local/AppService/AdminUserInfoAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/OHS/Local/AppService/LocalAppServiceBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/OHS/Local/AppService/ModuleAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/OHS/Local/AppService/StatAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/OHS/Local/AppService/SysMenuAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/OHS/Local/AppService/SysRoleAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/OHS/Local/AppService/SystemInfoAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/OHS/Local/PL/AdminUserInfo_Request.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/OHS/Local/PL/AdminUserInfo_Response.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/OHS/Local/PL/Module_Response.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/OHS/Local/PL/Stat_Response.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/OHS/Local/PL/SysMenu_Request.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/OHS/Local/PL/SysMenu_Response.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/OHS/Local/PL/SysRole_Request.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/OHS/Local/PL/SysRole_Response.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/css/Admin/AdminUserInfo/AdminUserInfo.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/css/Admin/Index/index.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/css/Admin/Menu/Menu.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/css/Admin/Pages/AdminChat/Chat.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/css/Admin/Pages/AdminChat/ChatLauncher.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/css/Admin/Shared/layout.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/css/Admin/SystemConfig/Index.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/css/Admin/TenantInfo/Index.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/css/Admin/XncfModule/XncfModule.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/css/Admin/login/index.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/Pages/AdminChat/Chat.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/Pages/AdminChat/ChatLauncherMixin.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/Pages/AdminUserInfo/Index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/Pages/Components/components.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/Pages/Index/Index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/Pages/LogIn/Index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/Pages/Menu/Index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/Pages/Role/Index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/Pages/SenparcTrace/Index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/Pages/SenparcTrace/detail.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/Pages/SystemConfig/Index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/Pages/TenantInfo/Index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/Pages/XncfModule/index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/Pages/XncfModule/start.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/Pages/XncfModule/startOld.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/axios.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/dompurify.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/global.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/navMenu.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/store.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Areas.Admin/wwwroot/js/Admin/vuePermission.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/GlobalUsings.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/JeffreyMpMessageHandler.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Models/BasePageModel.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Models/BaseVD.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Models/ErrorViewModel.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Pages/Doc.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Pages/Doc.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Pages/Error.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Pages/Error.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Pages/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Pages/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Pages/Privacy.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Pages/Privacy.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Pages/Shared/_CookieConsentPartial.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Pages/Shared/_Layout.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Pages/Shared/_ValidationScriptsPartial.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Pages/_ViewImports.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Pages/_ViewStart.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Program.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/css/Index/index.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/css/User/base/pagecss.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/css/User/styles.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/css/WX/base/base.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/css/WX/styles.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/css/site.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/css/site.min.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/js/Index/index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/js/site.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/dist/Chart.bundle.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/dist/Chart.bundle.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/dist/Chart.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/dist/Chart.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/gulpfile.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/karma.conf.ci.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/karma.conf.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/karma.coverage.conf.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/AnimationCallbacks/progress-bar.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/bar-horizontal.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/bar-multi-axis.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/bar-stacked.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/bar.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/bubble.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/combo-bar-line.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/data_label_combo-bar-line.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/different-point-sizes.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/doughnut.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/line-customTooltips.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/line-legend.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/line-logarithmic.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/line-multi-axis.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/line-skip-points.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/line-stacked-area.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/line-x-axis-filter.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/line.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/pie-customTooltips.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/pie.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/polar-area.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/radar-skip-points.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/radar.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/scatter-logX.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/scatter-multi-axis.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/scatter.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/timeScale/combo-time-scale.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/timeScale/line-time-point-data.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/timeScale/line-time-scale.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/samples/tooltip-hooks.html", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/chart.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/charts/Chart.Bar.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/charts/Chart.Bubble.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/charts/Chart.Doughnut.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/charts/Chart.Line.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/charts/Chart.PolarArea.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/charts/Chart.Radar.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/charts/Chart.Scatter.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/controllers/controller.bar.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/controllers/controller.bubble.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/controllers/controller.doughnut.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/controllers/controller.line.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/controllers/controller.polarArea.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/controllers/controller.radar.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/core/core.animation.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/core/core.controller.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/core/core.datasetController.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/core/core.element.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/core/core.helpers.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/core/core.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/core/core.layoutService.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/core/core.legend.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/core/core.plugin.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/core/core.scale.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/core/core.scaleService.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/core/core.title.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/core/core.tooltip.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/elements/element.arc.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/elements/element.line.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/elements/element.point.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/elements/element.rectangle.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/scales/scale.category.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/scales/scale.linear.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/scales/scale.logarithmic.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/scales/scale.radialLinear.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/src/scales/scale.time.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/controller.bar.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/controller.bubble.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/controller.doughnut.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/controller.line.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/controller.polarArea.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/controller.radar.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/core.element.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/core.helpers.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/core.layoutService.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/core.legend.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/core.plugin.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/core.scaleService.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/core.title.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/defaultConfig.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/element.arc.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/element.line.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/element.point.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/element.rectangle.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/mockContext.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/scale.category.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/scale.linear.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/scale.logarithmic.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/scale.radialLinear.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/Chart.js/test/scale.time.tests.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/axios/axios.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/dist/echarts.common.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/dist/echarts.common.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/dist/echarts.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/dist/echarts.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/dist/echarts.simple.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/dist/echarts.simple.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/dist/extension/bmap.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/dist/extension/bmap.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/dist/extension/dataTool.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/dist/extension/dataTool.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/extension/bmap/BMapCoordSys.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/extension/bmap/BMapModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/extension/bmap/BMapView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/extension/bmap/bmap.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/extension/dataTool/gexf.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/extension/dataTool/index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/extension/dataTool/prepareBoxplotData.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/extension/dataTool/quantile.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/extension/echarts.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/extension/webpack.config.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/index.common.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/index.simple.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/china-contour.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/china.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/anhui.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/aomen.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/beijing.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/chongqing.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/fujian.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/gansu.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/guangdong.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/guangxi.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/guizhou.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/hainan.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/hebei.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/heilongjiang.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/henan.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/hubei.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/hunan.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/jiangsu.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/jiangxi.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/jilin.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/liaoning.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/neimenggu.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/ningxia.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/qinghai.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/shandong.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/shanghai.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/shanxi.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/shanxi1.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/sichuan.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/tianjin.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/xianggang.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/xinjiang.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/xizang.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/yunnan.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/province/zhejiang.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/map/js/world.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/CoordinateSystem.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/ExtensionAPI.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/action/createDataSelectAction.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/action/geoRoam.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/action/roamHelper.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/bar/BarSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/bar/BarView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/bar/barItemStyle.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/bar.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/boxplot/BoxplotSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/boxplot/BoxplotView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/boxplot/boxplotLayout.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/boxplot/boxplotVisual.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/boxplot.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/candlestick/CandlestickSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/candlestick/CandlestickView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/candlestick/candlestickLayout.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/candlestick/candlestickVisual.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/candlestick/preprocessor.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/candlestick.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/chord/ChordSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/chord/ChordView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/chord/Ribbon.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/chord/chordCircularLayout.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/chord.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/effectScatter/EffectScatterSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/effectScatter/EffectScatterView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/effectScatter.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/funnel/FunnelSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/funnel/FunnelView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/funnel/funnelLayout.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/funnel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/gauge/GaugeSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/gauge/GaugeView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/gauge/PointerPath.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/gauge.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/graph/GraphSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/graph/GraphView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/graph/adjustEdge.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/graph/backwardCompat.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/graph/categoryFilter.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/graph/categoryVisual.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/graph/circularLayout.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/graph/circularLayoutHelper.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/graph/createView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/graph/edgeVisual.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/graph/forceHelper.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/graph/forceLayout.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/graph/roamAction.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/graph/simpleLayout.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/graph/simpleLayoutEdge.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/graph/simpleLayoutHelper.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/graph.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/heatmap/HeatmapLayer.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/heatmap/HeatmapSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/heatmap/HeatmapView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/heatmap.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/helper/EffectLine.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/helper/EffectSymbol.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/helper/LargeSymbolDraw.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/helper/Line.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/helper/LineDraw.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/helper/LinePath.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/helper/Symbol.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/helper/SymbolDraw.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/helper/WhiskerBoxDraw.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/helper/createGraphFromNodeEdge.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/helper/createGraphFromNodeMatrix.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/helper/createListFromArray.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/helper/whiskerBoxCommon.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/line/LineSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/line/LineView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/line/lineAnimationDiff.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/line/poly.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/line.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/lines/LinesSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/lines/LinesView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/lines/linesLayout.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/lines.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/map/MapSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/map/MapView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/map/backwardCompat.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/map/mapDataStatistic.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/map/mapSymbolLayout.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/map/mapVisual.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/map.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/parallel/ParallelSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/parallel/ParallelView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/parallel/parallelVisual.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/parallel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/pie/PieSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/pie/PieView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/pie/labelLayout.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/pie/pieLayout.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/pie.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/radar/RadarSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/radar/RadarView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/radar/backwardCompat.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/radar/radarLayout.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/radar.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/sankey/SankeySeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/sankey/SankeyView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/sankey/sankeyLayout.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/sankey/sankeyVisual.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/sankey.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/scatter/ScatterSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/scatter/ScatterView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/scatter.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/themeRiver/ThemeRiverSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/themeRiver/ThemeRiverView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/themeRiver/themeRiverLayout.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/themeRiver/themeRiverVisual.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/themeRiver.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/treemap/Breadcrumb.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/treemap/TreemapSeries.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/treemap/TreemapView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/treemap/helper.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/treemap/treemapAction.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/treemap/treemapLayout.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/treemap/treemapVisual.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/chart/treemap.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/angleAxis.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/axis/AngleAxisView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/axis/AxisBuilder.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/axis/AxisView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/axis/ParallelAxisView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/axis/RadiusAxisView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/axis/SingleAxisView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/axis/parallelAxisAction.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/axis.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/dataZoom/AxisProxy.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/dataZoom/DataZoomModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/dataZoom/DataZoomView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/dataZoom/InsideZoomModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/dataZoom/InsideZoomView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/dataZoom/SelectZoomModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/dataZoom/SelectZoomView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/dataZoom/SliderZoomModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/dataZoom/SliderZoomView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/dataZoom/dataZoomAction.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/dataZoom/dataZoomProcessor.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/dataZoom/history.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/dataZoom/roams.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/dataZoom/typeDefaulter.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/dataZoom.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/dataZoomInside.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/dataZoomSelect.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/geo/GeoView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/geo.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/grid.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/helper/MapDraw.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/helper/RoamController.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/helper/SelectController.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/helper/interactionMutex.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/helper/listComponent.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/helper/selectableMixin.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/helper/sliderMove.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/legend/LegendModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/legend/LegendView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/legend/legendAction.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/legend/legendFilter.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/legend.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/markLine.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/markPoint.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/marker/MarkLineModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/marker/MarkLineView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/marker/MarkPointModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/marker/MarkPointView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/marker/markerHelper.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/parallel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/parallelAxis.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/polar.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/radar/RadarView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/radar.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/radiusAxis.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/single.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/singleAxis.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/timeline/SliderTimelineModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/timeline/SliderTimelineView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/timeline/TimelineAxis.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/timeline/TimelineModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/timeline/TimelineView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/timeline/preprocessor.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/timeline/timelineAction.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/timeline/typeDefaulter.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/timeline.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/title.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/toolbox/ToolboxModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/toolbox/ToolboxView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/toolbox/feature/DataView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/toolbox/feature/DataZoom.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/toolbox/feature/MagicType.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/toolbox/feature/Restore.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/toolbox/feature/SaveAsImage.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/toolbox/featureManager.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/toolbox.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/tooltip/TooltipContent.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/tooltip/TooltipModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/tooltip/TooltipView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/tooltip.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/visualMap/ContinuousModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/visualMap/ContinuousView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/visualMap/PiecewiseModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/visualMap/PiecewiseView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/visualMap/VisualMapModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/visualMap/VisualMapView.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/visualMap/helper.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/visualMap/preprocessor.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/visualMap/typeDefaulter.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/visualMap/visualCoding.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/visualMap/visualMapAction.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/visualMap.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/visualMapContinuous.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/component/visualMapPiecewise.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/Axis.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/View.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/axisDefault.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/axisHelper.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/axisModelCommonMixin.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/axisModelCreator.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/cartesian/Axis2D.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/cartesian/AxisModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/cartesian/Cartesian.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/cartesian/Cartesian2D.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/cartesian/Grid.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/cartesian/GridModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/cartesian/axisLabelInterval.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/geo/Geo.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/geo/GeoModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/geo/Region.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/geo/fix/geoCoord.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/geo/fix/nanhai.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/geo/fix/textCoord.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/geo/geoCreator.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/geo/parseGeoJson.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/parallel/AxisModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/parallel/Parallel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/parallel/ParallelAxis.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/parallel/ParallelModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/parallel/parallelCreator.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/parallel/parallelPreprocessor.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/polar/AngleAxis.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/polar/AxisModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/polar/Polar.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/polar/PolarModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/polar/RadiusAxis.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/polar/polarCreator.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/radar/IndicatorAxis.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/radar/Radar.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/radar/RadarModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/single/AxisModel.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/single/Single.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/single/SingleAxis.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/coord/single/singleCreator.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/data/DataDiffer.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/data/Graph.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/data/List.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/data/Tree.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/data/helper/completeDimensions.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/data/helper/linkList.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/echarts.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/layout/barGrid.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/layout/points.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/loading/default.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/model/Component.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/model/Global.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/model/Model.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/model/OptionManager.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/model/Series.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/model/globalDefault.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/model/mixin/areaStyle.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/model/mixin/boxLayout.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/model/mixin/itemStyle.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/model/mixin/lineStyle.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/model/mixin/makeStyleMapper.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/model/mixin/textStyle.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/preprocessor/backwardCompat.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/preprocessor/helper/compatStyle.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/processor/dataFilter.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/processor/dataSample.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/scale/Interval.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/scale/Log.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/scale/Ordinal.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/scale/Scale.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/scale/Time.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/util/KDTree.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/util/animation.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/util/array/nest.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/util/clazz.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/util/component.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/util/format.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/util/graphic.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/util/layout.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/util/model.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/util/number.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/util/quickSelect.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/util/symbol.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/util/throttle.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/view/Chart.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/view/Component.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/visual/VisualMapping.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/visual/dataColor.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/visual/seriesColor.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/visual/symbol.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/src/visual/visualDefault.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/theme/dark.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/theme/infographic.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/theme/macarons.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/theme/roma.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/theme/shine.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/theme/vintage.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/echarts/webpack.config.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/element-ui_2.13.2/element.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/element-ui_2.13.2/element.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/font-awesome/css/font-awesome.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/font-awesome/css/font-awesome.min.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/jquery/dist/jquery.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/jquery/dist/jquery.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/jquery-validation/dist/additional-methods.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/jquery-validation/dist/additional-methods.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/jquery-validation/dist/jquery.validate.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/jquery-validation/dist/jquery.validate.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/jquery.form.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/jquery.form.min.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/vue/vue.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/lib/vuex.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web/wwwroot/scripts/app.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web.DatabasePlant/Program.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web.DatabasePlant/Startup.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web.FirefoxDriverTest/AdminUserInfoModuleTest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Web.FirefoxDriverTest/FireFoxBaseDriverTest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/ACL/Repository/AccountPayLogRepository.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/ACL/Repository/AccountRepository.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/ACL/Repository/PointsLogRepository.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Areas/Admin/Pages/Account/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Areas/Admin/Pages/Account/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Areas/Admin/Pages/Shared/_SideMenu.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Areas/Admin/Pages/Shared/_ViewImports.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Areas/Admin/Pages/Shared/_ViewStart.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Cache/FullAccountCache.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Migrations/MySql/20211127070513_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Migrations/MySql/20211127070513_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Migrations/MySql/AccountSenparcEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Migrations/Oracle/20220818164240_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Migrations/Oracle/20220818164240_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Migrations/Oracle/AccountSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Migrations/PostgreSQL/20211127070536_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Migrations/PostgreSQL/20211127070536_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Migrations/PostgreSQL/AccountSenparcEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Migrations/SqlServer/20211127070522_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Migrations/SqlServer/20211127070522_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Migrations/SqlServer/AccountSenparcEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Migrations/Sqlite/20211127070504_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Migrations/Sqlite/20211127070504_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Migrations/Sqlite/AccountSenparcEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/Account.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/AccountPayLog.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/AccountSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/Dto/AccountDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/Dto/BaseDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/Dto/BaseQueryDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/ExtensionEntity.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/Mapping/AccountConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/Mapping/AccountPayLogConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/Mapping/PointsLogConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/MultipleDatabase/AccountSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/MultipleDatabase/AccountSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/MultipleDatabase/AccountSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/MultipleDatabase/AccountSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/MultipleDatabase/AccountSenparcEntities_SQLite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/MultipleDatabase/AccountSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/MultipleDatabase/SenparcDbContextFactoryConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Models/DatabaseModel/PointsLog.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/OperationQueue/OperationQueue.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/OperationQueue/OperationQueueItem.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/OperationQueue/OperationQueueService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Services/AccountPayLogService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Services/AccountService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Services/BaseClientService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Domain/Services/PointsLogService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/OHS/Local/PL/MyFunctionRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/OHS/Local/Remote/AccountAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Program.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Register.Area.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Accounts/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/Areas/Install/Pages/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/Areas/Install/Pages/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/Domain/Models/DatabaseModel/InstallerSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/Domain/Models/MultipleDatabase/InstallerSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/Domain/Models/MultipleDatabase/InstallerSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/Domain/Models/MultipleDatabase/InstallerSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/Domain/Models/MultipleDatabase/InstallerSenparcEntities_SQLite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/Domain/Models/MultipleDatabase/InstallerSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/Domain/Services/InstallOptionsService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/Domain/Services/InstallerService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/OHS/Local/AppService/InstallAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/Pages/Shared/_SideMenu.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/Pages/Shared/_ViewImports.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/Pages/Shared/_ViewStart.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/Program.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/Register.Area.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/wwwroot/css/Installer/Pages/index.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer/wwwroot/js/Installer/Pages/index.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer.Interface/Config.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer.Interface/Domain/Dto/GetDefaultInstallOptionsResponseDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer.Interface/Domain/Dto/InstallRequestDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer.Interface/Domain/Dto/InstallResponseDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer.Interface/Domain/Dto/XncfRegisterDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Senparc.Xncf.Installer.Interface/OHS/WebClient/InstallerApiClient.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Areas/Admin/Pages/Shared/_SideMenu.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Areas/Admin/Pages/Shared/_ViewImports.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Areas/Admin/Pages/Shared/_ViewStart.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Areas/Admin/Pages/Template_XncfName/DatabaseSample.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Areas/Admin/Pages/Template_XncfName/DatabaseSample.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Areas/Admin/Pages/Template_XncfName/DatabaseSampleIndex.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Areas/Admin/Pages/Template_XncfName/DatabaseSampleIndex.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Areas/Admin/Pages/Template_XncfName/Index.cshtml", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Areas/Admin/Pages/Template_XncfName/Index.cshtml.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/Dm/20250109084533_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/Dm/20250109084533_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/Dm/Template_XncfNameSenparcEntities_DmModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/MySql/20240423143658_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/MySql/20240423143658_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/MySql/Template_XncfNameSenparcEntities_MySqlModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/Oracle/20240423143234_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/Oracle/20240423143234_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/Oracle/Template_XncfNameSenparcEntities_OracleModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/PostgreSQL/20240423143225_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/PostgreSQL/20240423143225_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/PostgreSQL/Template_XncfNameSenparcEntities_PostgreSQLModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/SqlServer/20240423143154_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/SqlServer/20240423143154_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/SqlServer/Template_XncfNameSenparcEntities_SqlServerModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/Sqlite/20240423143206_Init.Designer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/Sqlite/20240423143206_Init.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Migrations/Sqlite/Template_XncfNameSenparcEntities_SqliteModelSnapshot.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Models/DatabaseModel/Color.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Models/DatabaseModel/Dto/ColorDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Models/DatabaseModel/Dto/ColorRequestDto.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Models/DatabaseModel/Mapping/Template_XncfName_ColorConfigurationMapping.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Models/DatabaseModel/Template_XncfNameSenparcEntities.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Models/MultipleDatabase/Template_XncfNameSenparcEntities_Dm.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Models/MultipleDatabase/Template_XncfNameSenparcEntities_MySql.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Models/MultipleDatabase/Template_XncfNameSenparcEntities_Oracle.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Models/MultipleDatabase/Template_XncfNameSenparcEntities_PostgreSQL.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Models/MultipleDatabase/Template_XncfNameSenparcEntities_SQLite.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Models/MultipleDatabase/Template_XncfNameSenparcEntities_SqlServer.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Domain/Services/ColorService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/OHS/Local/AppService/ApiAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/OHS/Local/AppService/ColorAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/OHS/Local/AppService/MyFuctionAppService.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/OHS/Local/PL/ApiRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/OHS/Local/PL/ColorResponse.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/OHS/Local/PL/MyFunctionRequest.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Register.Area.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Register.Database.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/Register.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/wwwroot/css/Admin/Template_XncfName/databaseSampleIndex.css", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Template_OrgName.Xncf.Template_XncfName/wwwroot/js/Admin/Template_XncfName/databaseSampleIndex.js", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Tests/Senparc.Areas.Admin.Tests/AdminUserInfoServiceTests.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Tests/Senparc.Areas.Admin.Tests/Domain/Models/AdminUserInfoTests.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Tests/Senparc.Areas.Admin.Tests/Domain/Services/AdminUserInfoServiceTests.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Tests/Senparc.Areas.Admin.Tests/TestBase.cs", + "/home/nvidia/JeffreySu/NeuCharFramework/NcfPackageSources/tools/NcfSimulatedSite/Tests/Senparc.Areas.Admin.Tests/Usings.cs" + ], + "total": 2188 +} \ No newline at end of file diff --git a/.translation_state_v2.json b/.translation_state_v2.json new file mode 100644 index 000000000..1b5498db5 --- /dev/null +++ b/.translation_state_v2.json @@ -0,0 +1,4 @@ +{ + "index": 1816, + "total": 1816 +} \ No newline at end of file diff --git a/EVENTBUS_QUICK_REFERENCE.md b/EVENTBUS_QUICK_REFERENCE.md index ff392e835..7bc96953a 100644 --- a/EVENTBUS_QUICK_REFERENCE.md +++ b/EVENTBUS_QUICK_REFERENCE.md @@ -177,3 +177,4 @@ When implementing handlers, ensure: **Version**: 1.0 **Updated**: 2026-03-24 + \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.AreaBase/Admin/AdminPageModelBase.cs b/src/Basic/Senparc.Ncf.AreaBase/Admin/AdminPageModelBase.cs index cd9af736b..cbceeff83 100644 --- a/src/Basic/Senparc.Ncf.AreaBase/Admin/AdminPageModelBase.cs +++ b/src/Basic/Senparc.Ncf.AreaBase/Admin/AdminPageModelBase.cs @@ -1,57 +1,57 @@ -using Microsoft.AspNetCore.Mvc; -using Senparc.Ncf.AreaBase.Admin.Filters; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Core.Models.VD; -using Senparc.Ncf.Core.WorkContext; -using Senparc.Ncf.Mvc.UI; -using Senparc.Ncf.XncfBase; -using System.Collections.Generic; - -namespace Senparc.Ncf.AreaBase.Admin// Senparc.Areas.Admin -{ - - public interface IAdminPageModelBase : IPageModelBase - { - AdminWorkContext AdminWorkContext { get; set; } - string Uid { get; set; } - List XncfRegisterList { get; } - - FullSystemConfig FullSystemConfig { get; set; } - - IActionResult RenderError(string message); - } - - //暂时取消权限验证 - //[ServiceFilter(typeof(AuthenticationAsyncPageFilterAttribute))] - [AdminAuthorize("AdminOnly")] - public class AdminPageModelBase : PageModelBase, IAdminPageModelBase - { - /// - /// 存储相关用户信息 - /// - public virtual AdminWorkContext AdminWorkContext { get; set; } - - [BindProperty(SupportsGet = true)] - public string Uid { get; set; } - - /// - /// 所有 XncfRegister 列表(包括还未注册的) - /// - public virtual List XncfRegisterList => Senparc.Ncf.XncfBase.XncfRegisterManager.RegisterList; - - - public virtual IActionResult RenderError(string message) - { - //保留原有的controller和action信息 - //ViewData["FakeControllerName"] = RouteData.Values["controller"] as string; - //ViewData["FakeActionName"] = RouteData.Values["action"] as string; - - return Page();//TODO:设定一个特定的错误页面 - - //return View("Error", new Error_ExceptionVD - //{ - // //HandleErrorInfo = new HandleErrorInfo(new Exception(message), Url.RequestContext.RouteData.GetRequiredString("controller"), Url.RequestContext.RouteData.GetRequiredString("action")) - //}); - } - } -} +using Microsoft.AspNetCore.Mvc; +using Senparc.Ncf.AreaBase.Admin.Filters; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Core.Models.VD; +using Senparc.Ncf.Core.WorkContext; +using Senparc.Ncf.Mvc.UI; +using Senparc.Ncf.XncfBase; +using System.Collections.Generic; + +namespace Senparc.Ncf.AreaBase.Admin// Senparc.Areas.Admin +{ + + public interface IAdminPageModelBase : IPageModelBase + { + AdminWorkContext AdminWorkContext { get; set; } + string Uid { get; set; } + List XncfRegisterList { get; } + + FullSystemConfig FullSystemConfig { get; set; } + + IActionResult RenderError(string message); + } + + //Temporarily cancel permission verification + //[ServiceFilter(typeof(AuthenticationAsyncPageFilterAttribute))] + [AdminAuthorize("AdminOnly")] + public class AdminPageModelBase : PageModelBase, IAdminPageModelBase + { + /// + ///Storage related user information + /// + public virtual AdminWorkContext AdminWorkContext { get; set; } + + [BindProperty(SupportsGet = true)] + public string Uid { get; set; } + + /// + /// List of all XncfRegisters (including those not yet registered) + /// + public virtual List XncfRegisterList => Senparc.Ncf.XncfBase.XncfRegisterManager.RegisterList; + + + public virtual IActionResult RenderError(string message) + { + //Keep original controller and action information + //ViewData["FakeControllerName"] = RouteData.Values["controller"] as string; + //ViewData["FakeActionName"] = RouteData.Values["action"] as string; + + return Page();//TODO: Set a specific error page + + //return View("Error", new Error_ExceptionVD + //{ + // //HandleErrorInfo = new HandleErrorInfo(new Exception(message), Url.RequestContext.RouteData.GetRequiredString("controller"), Url.RequestContext.RouteData.GetRequiredString("action")) + //}); + } + } +} diff --git a/src/Basic/Senparc.Ncf.AreaBase/Admin/AdminXscfModulePageModelBase.cs b/src/Basic/Senparc.Ncf.AreaBase/Admin/AdminXscfModulePageModelBase.cs index 9ab583681..301f9b417 100644 --- a/src/Basic/Senparc.Ncf.AreaBase/Admin/AdminXscfModulePageModelBase.cs +++ b/src/Basic/Senparc.Ncf.AreaBase/Admin/AdminXscfModulePageModelBase.cs @@ -1,66 +1,66 @@ -using Microsoft.AspNetCore.Mvc; -using Senparc.CO2NET.Extensions; -using Senparc.Ncf.Core.Models.DataBaseModel; -using Senparc.Ncf.Service; -using Senparc.Ncf.XncfBase; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Senparc.Ncf.AreaBase.Admin -{ - /// - /// XNCF 模块的页面模板 - /// - public abstract class AdminXncfModulePageModelBase : AdminPageModelBase - { - private XncfModuleDto _xncfModuleDto; - /// - /// XncfModuleDto - /// - public XncfModuleDto XncfModuleDto - { - get - { - if (_xncfModuleDto == null) - { - SetXncfModuleDto(); - } - return _xncfModuleDto; - } - } - /// - /// XncfModuleDto.Uid - /// - public string XncfModuleUid => XncfModuleDto?.Uid; - - /// - /// 当前正在操作的 XncfRegister - /// - public virtual IXncfRegister XncfRegister => XncfModuleDto != null ? XncfRegisterList.FirstOrDefault(z => z.Uid == XncfModuleDto.Uid) : null; - - protected readonly Lazy _xncfModuleService; - - protected AdminXncfModulePageModelBase(Lazy xncfModuleService) - { - _xncfModuleService = xncfModuleService; - } - - public virtual void SetXncfModuleDto() - { - if (Uid.IsNullOrEmpty()) - { - throw new XncfPageException(null, "页面未提供UID!"); - } - - var xncfModule = _xncfModuleService.Value.GetObject(z => z.Uid == Uid); - if (xncfModule == null) - { - throw new XncfPageException(null, "尚未注册 XNCF 模块,UID:" + Uid); - } - - _xncfModuleDto = _xncfModuleService.Value.Mapper.Map(xncfModule); - } - } -} +using Microsoft.AspNetCore.Mvc; +using Senparc.CO2NET.Extensions; +using Senparc.Ncf.Core.Models.DataBaseModel; +using Senparc.Ncf.Service; +using Senparc.Ncf.XncfBase; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Senparc.Ncf.AreaBase.Admin +{ + /// + /// Page template for XNCF module + /// + public abstract class AdminXncfModulePageModelBase : AdminPageModelBase + { + private XncfModuleDto _xncfModuleDto; + /// + /// XncfModuleDto + /// + public XncfModuleDto XncfModuleDto + { + get + { + if (_xncfModuleDto == null) + { + SetXncfModuleDto(); + } + return _xncfModuleDto; + } + } + /// + /// XncfModuleDto.Uid + /// + public string XncfModuleUid => XncfModuleDto?.Uid; + + /// + /// The XncfRegister currently operating + /// + public virtual IXncfRegister XncfRegister => XncfModuleDto != null ? XncfRegisterList.FirstOrDefault(z => z.Uid == XncfModuleDto.Uid) : null; + + protected readonly Lazy _xncfModuleService; + + protected AdminXncfModulePageModelBase(Lazy xncfModuleService) + { + _xncfModuleService = xncfModuleService; + } + + public virtual void SetXncfModuleDto() + { + if (Uid.IsNullOrEmpty()) + { + throw new XncfPageException(null, "页面未提供UID!"); + } + + var xncfModule = _xncfModuleService.Value.GetObject(z => z.Uid == Uid); + if (xncfModule == null) + { + throw new XncfPageException(null, "尚未注册 XNCF 模块,UID:" + Uid); + } + + _xncfModuleDto = _xncfModuleService.Value.Mapper.Map(xncfModule); + } + } +} diff --git a/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/AdminAuthorizeAttribute.cs b/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/AdminAuthorizeAttribute.cs index 4231b449c..994849c76 100644 --- a/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/AdminAuthorizeAttribute.cs +++ b/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/AdminAuthorizeAttribute.cs @@ -1,26 +1,26 @@ -using Microsoft.AspNetCore.Authorization; -using Senparc.Ncf.Core.Config; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.AreaBase.Admin.Filters -{ - /// - /// 当前 Area 授权处理特性 - /// - public class AdminAuthorizeAttribute : AuthorizeAttribute - { - //AuthorizeAttribute 可以和 MVC 通用:https://docs.microsoft.com/en-us/aspnet/core/razor-pages/filter?view=aspnetcore-2.2 - public static string AuthenticationScheme => SiteConfig.NcfAdminAuthorizeScheme; - - public AdminAuthorizeAttribute() - { - base.AuthenticationSchemes = AuthenticationScheme; - } - public AdminAuthorizeAttribute(string policy) : this() - { - this.Policy = policy; - } - } -} +using Microsoft.AspNetCore.Authorization; +using Senparc.Ncf.Core.Config; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.AreaBase.Admin.Filters +{ + /// + /// Current Area authorization processing characteristics + /// + public class AdminAuthorizeAttribute : AuthorizeAttribute + { + //AuthorizeAttribute can be used in common with MVC: https://docs.microsoft.com/en-us/aspnet/core/razor-pages/filter?view=aspnetcore-2.2 + public static string AuthenticationScheme => SiteConfig.NcfAdminAuthorizeScheme; + + public AdminAuthorizeAttribute() + { + base.AuthenticationSchemes = AuthenticationScheme; + } + public AdminAuthorizeAttribute(string policy) : this() + { + this.Policy = policy; + } + } +} diff --git a/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/AuthenticationAsyncPageFilterAttribute.cs b/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/AuthenticationAsyncPageFilterAttribute.cs index 5ee286d62..18ac08aca 100644 --- a/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/AuthenticationAsyncPageFilterAttribute.cs +++ b/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/AuthenticationAsyncPageFilterAttribute.cs @@ -1,80 +1,80 @@ -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Http.Extensions; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Authorization; -using Microsoft.AspNetCore.Mvc.Filters; -using Senparc.Ncf.Core; -using Senparc.Ncf.Core.WorkContext.Provider; -using Senparc.Ncf.Service; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Ncf.AreaBase.Admin.Filters -{ - - //https://docs.microsoft.com/zh-cn/aspnet/core/razor-pages/filter?view=aspnetcore-3.1 - - [Obsolete("AuthenticationResultFilterAttribute")] - public class AuthenticationAsyncPageFilterAttribute : IAsyncPageFilter - { - private readonly SysRolePermissionService _sysPermissionService; - private readonly IAdminWorkContextProvider _adminWorkContextProvider; - private readonly SysMenuService _sysMenuService; - - public AuthenticationAsyncPageFilterAttribute(SysRolePermissionService sysPermissionService, Core.WorkContext.Provider.IAdminWorkContextProvider adminWorkContextProvider, SysMenuService _sysMenuService) - { - this._sysPermissionService = sysPermissionService; - this._adminWorkContextProvider = adminWorkContextProvider; - this._sysMenuService = _sysMenuService; - } - - public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) - { - //在调用处理程序方法前,但在模型绑定结束后,进行异步调用。 - - //context.ActionDescriptor.FilterDescriptors - var authenticateResult = await context.HttpContext.AuthenticateAsync(AdminAuthorizeAttribute.AuthenticationScheme); - if (authenticateResult.Succeeded && !context.Filters.Any(_ => _ is AllowAnonymousFilter)) - { - AdminPageModelBase adminPageModel = context.HandlerInstance as AdminPageModelBase; - //adminPageModel.SysMenuDtos = await _sysMenuService.GetMenuTreeDtoByCacheAsync(); - adminPageModel.AdminWorkContext = _adminWorkContextProvider.GetAdminWorkContext(); - - bool hasPageRoute = context.RouteData.Values.TryGetValue("page", out object page); - bool hasAreaRoute = context.RouteData.Values.TryGetValue("area", out object area); - - bool hasRight = hasPageRoute && hasAreaRoute; - if (hasRight) - { - var url = context.HttpContext.Request.Path;/*.GetEncodedPathAndQuery()*/; - hasRight = await _sysPermissionService.HasPermissionAsync(url/*string.Concat("/", area, page)*/); - } - - if (!hasRight /*&& !(adminPageModel is Pages.IndexModel)*/) - { - IActionResult actionResult = new Microsoft.AspNetCore.Mvc.RedirectResult("/Admin/Forbidden"); - //跳出 - if (context.HttpContext.Request.Headers.TryGetValue("x-requested-with", out Microsoft.Extensions.Primitives.StringValues strings)) - { - if (strings.Contains("XMLHttpRequest")) - { - actionResult = new JsonResult(new AjaxReturnModel() { Success = false, Msg = "您没有权限访问" }) { StatusCode = 401 }; - } - } - context.Result = actionResult; - return;//If an IAsyncPageFilter provides a result value by setting the Result property of PageHandlerExecutingContext to a non-null value, then it cannot call the next filter by invoking PageHandlerExecutionDelegate. - } - } - await next.Invoke(); - } - - public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) - { - //在选择处理程序方法后,但在模型绑定发生前,进行异步调用。 - return Task.CompletedTask; - } - } -} +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Authorization; +using Microsoft.AspNetCore.Mvc.Filters; +using Senparc.Ncf.Core; +using Senparc.Ncf.Core.WorkContext.Provider; +using Senparc.Ncf.Service; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Ncf.AreaBase.Admin.Filters +{ + + //https://docs.microsoft.com/zh-cn/aspnet/core/razor-pages/filter?view=aspnetcore-3.1 + + [Obsolete("AuthenticationResultFilterAttribute")] + public class AuthenticationAsyncPageFilterAttribute : IAsyncPageFilter + { + private readonly SysRolePermissionService _sysPermissionService; + private readonly IAdminWorkContextProvider _adminWorkContextProvider; + private readonly SysMenuService _sysMenuService; + + public AuthenticationAsyncPageFilterAttribute(SysRolePermissionService sysPermissionService, Core.WorkContext.Provider.IAdminWorkContextProvider adminWorkContextProvider, SysMenuService _sysMenuService) + { + this._sysPermissionService = sysPermissionService; + this._adminWorkContextProvider = adminWorkContextProvider; + this._sysMenuService = _sysMenuService; + } + + public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) + { + //Make an asynchronous call before the handler method is called, but after model binding ends. + + //context.ActionDescriptor.FilterDescriptors + var authenticateResult = await context.HttpContext.AuthenticateAsync(AdminAuthorizeAttribute.AuthenticationScheme); + if (authenticateResult.Succeeded && !context.Filters.Any(_ => _ is AllowAnonymousFilter)) + { + AdminPageModelBase adminPageModel = context.HandlerInstance as AdminPageModelBase; + //adminPageModel.SysMenuDtos = await _sysMenuService.GetMenuTreeDtoByCacheAsync(); + adminPageModel.AdminWorkContext = _adminWorkContextProvider.GetAdminWorkContext(); + + bool hasPageRoute = context.RouteData.Values.TryGetValue("page", out object page); + bool hasAreaRoute = context.RouteData.Values.TryGetValue("area", out object area); + + bool hasRight = hasPageRoute && hasAreaRoute; + if (hasRight) + { + var url = context.HttpContext.Request.Path;/*.GetEncodedPathAndQuery()*/; + hasRight = await _sysPermissionService.HasPermissionAsync(url/*string.Concat("/", area, page)*/); + } + + if (!hasRight /*&& !(adminPageModel is Pages.IndexModel)*/) + { + IActionResult actionResult = new Microsoft.AspNetCore.Mvc.RedirectResult("/Admin/Forbidden"); + //Jump out + if (context.HttpContext.Request.Headers.TryGetValue("x-requested-with", out Microsoft.Extensions.Primitives.StringValues strings)) + { + if (strings.Contains("XMLHttpRequest")) + { + actionResult = new JsonResult(new AjaxReturnModel() { Success = false, Msg = "您没有权限访问" }) { StatusCode = 401 }; + } + } + context.Result = actionResult; + return;//If an IAsyncPageFilter provides a result value by setting the Result property of PageHandlerExecutingContext to a non-null value, then it cannot call the next filter by invoking PageHandlerExecutionDelegate. + } + } + await next.Invoke(); + } + + public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) + { + //Make an asynchronous call after the handler method is selected, but before model binding occurs. + return Task.CompletedTask; + } + } +} diff --git a/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/AuthenticationResultFilterAttribute.cs b/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/AuthenticationResultFilterAttribute.cs index acb31a55c..c7a7b2f15 100644 --- a/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/AuthenticationResultFilterAttribute.cs +++ b/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/AuthenticationResultFilterAttribute.cs @@ -1,117 +1,117 @@ -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Authorization; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.Caching.Distributed; -using Senparc.Ncf.Core; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Core.WorkContext.Provider; -using Senparc.Ncf.Service; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.EntityFrameworkCore.Internal; - -namespace Senparc.Ncf.AreaBase.Admin.Filters -{ - /// - /// 校验权限的类 - /// - public class AuthenticationResultFilterAttribute : IAsyncPageFilter, IFilterMetadata - { - private IServiceProvider _serviceProvider; - public AuthenticationResultFilterAttribute(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) - { - if (context.HandlerMethod == null) - { - context.Result = new OkObjectResult(new AjaxReturnModel() { Success = false, Msg = $"404,未找到对应的Handler。请检查请求方法请求地址是否有误!请求方法:{context.HttpContext.Request.Method}" }) { StatusCode = 404 }; - return; - } - await ValidatePermissionAsync(_serviceProvider, context, next); - } - - /// - /// 验证权限得方法 - /// - /// - /// - /// - /// - public virtual async Task ValidatePermissionAsync(IServiceProvider serviceProvider, PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) - { - bool canAccessResource = false; - bool isAjax = false; - isAjax = IsAjax(context); - CustomerResourceAttribute attributeCodes = context.HandlerMethod.MethodInfo - .GetCustomAttributes(typeof(CustomerResourceAttribute), false) - .OfType() - .FirstOrDefault(); - bool isIgnore = context.Filters.OfType().Any(); - IEnumerable resourceCodes = attributeCodes?.ResourceCodes.ToList() ?? new List() { "*" };//当前方法的资源Code - //Console.WriteLine("isAjax:{0}, isIgnore:{1}", isAjax, isIgnore); - System.Diagnostics.Debug.WriteLine("isAjax:{0}, isIgnore: {1}", isAjax, isIgnore); - if (isIgnore || (resourceCodes.Any(_ => "*".Equals(_)) && isAjax)) - { - await next(); - } - else - { - string url = string.Join(string.Empty, context.RouteData.Values.Values.Reverse()); - if (!url.StartsWith("/")) - { - url = string.Concat("/", url); // /Admin/AdminUserInfo/Index - } - System.Diagnostics.Debug.WriteLine("url:{0}", url); - canAccessResource = await serviceProvider.GetService().HasPermissionAsync(resourceCodes, url, isAjax);// await Task.FromResult(true);//TODO... - if (canAccessResource) - { - await next(); - } - else - { - string path = context.HttpContext.Request.Path.Value; - IActionResult actionResult = null; - if (isAjax) - { - actionResult = new OkObjectResult(new AjaxReturnModel(path) { Msg = "您没有权限访问", Success = false }) { StatusCode = (int)System.Net.HttpStatusCode.Forbidden }; - } - - context.Result = actionResult ?? new RedirectResult("/Admin/Forbidden?url=" + System.Web.HttpUtility.UrlEncode(path)); - } - } - } - - /// - /// 是否是Ajax请求 - /// - /// - /// - public virtual bool IsAjax(PageHandlerExecutingContext context) - { - bool isAjax = false; - if (context.HttpContext.Request.Headers.TryGetValue("x-requested-with", out Microsoft.Extensions.Primitives.StringValues strings)) - { - if (strings.Contains("XMLHttpRequest")) - { - isAjax = true; - } - } - return isAjax; - } - - public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) - { - return Task.CompletedTask; - } - } -} +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Authorization; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Caching.Distributed; +using Senparc.Ncf.Core; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Core.WorkContext.Provider; +using Senparc.Ncf.Service; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.EntityFrameworkCore.Internal; + +namespace Senparc.Ncf.AreaBase.Admin.Filters +{ + /// + /// Class for verifying permissions + /// + public class AuthenticationResultFilterAttribute : IAsyncPageFilter, IFilterMetadata + { + private IServiceProvider _serviceProvider; + public AuthenticationResultFilterAttribute(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) + { + if (context.HandlerMethod == null) + { + context.Result = new OkObjectResult(new AjaxReturnModel() { Success = false, Msg = $"404,未找到对应的Handler。请检查请求方法请求地址是否有误!请求方法:{context.HttpContext.Request.Method}" }) { StatusCode = 404 }; + return; + } + await ValidatePermissionAsync(_serviceProvider, context, next); + } + + /// + /// How to verify permissions + /// + /// + /// + /// + /// + public virtual async Task ValidatePermissionAsync(IServiceProvider serviceProvider, PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) + { + bool canAccessResource = false; + bool isAjax = false; + isAjax = IsAjax(context); + CustomerResourceAttribute attributeCodes = context.HandlerMethod.MethodInfo + .GetCustomAttributes(typeof(CustomerResourceAttribute), false) + .OfType() + .FirstOrDefault(); + bool isIgnore = context.Filters.OfType().Any(); + IEnumerable resourceCodes = attributeCodes?.ResourceCodes.ToList() ?? new List() { "*" };//Resource Code of the current method + //Console.WriteLine("isAjax:{0}, isIgnore:{1}", isAjax, isIgnore); + System.Diagnostics.Debug.WriteLine("isAjax:{0}, isIgnore: {1}", isAjax, isIgnore); + if (isIgnore || (resourceCodes.Any(_ => "*".Equals(_)) && isAjax)) + { + await next(); + } + else + { + string url = string.Join(string.Empty, context.RouteData.Values.Values.Reverse()); + if (!url.StartsWith("/")) + { + url = string.Concat("/", url); // /Admin/AdminUserInfo/Index + } + System.Diagnostics.Debug.WriteLine("url:{0}", url); + canAccessResource = await serviceProvider.GetService().HasPermissionAsync(resourceCodes, url, isAjax);// await Task.FromResult(true);//TODO... + if (canAccessResource) + { + await next(); + } + else + { + string path = context.HttpContext.Request.Path.Value; + IActionResult actionResult = null; + if (isAjax) + { + actionResult = new OkObjectResult(new AjaxReturnModel(path) { Msg = "您没有权限访问", Success = false }) { StatusCode = (int)System.Net.HttpStatusCode.Forbidden }; + } + + context.Result = actionResult ?? new RedirectResult("/Admin/Forbidden?url=" + System.Web.HttpUtility.UrlEncode(path)); + } + } + } + + /// + /// Is it an Ajax request? + /// + /// + /// + public virtual bool IsAjax(PageHandlerExecutingContext context) + { + bool isAjax = false; + if (context.HttpContext.Request.Headers.TryGetValue("x-requested-with", out Microsoft.Extensions.Primitives.StringValues strings)) + { + if (strings.Contains("XMLHttpRequest")) + { + isAjax = true; + } + } + return isAjax; + } + + public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) + { + return Task.CompletedTask; + } + } +} diff --git a/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/CustomerResourceAttribute.cs b/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/CustomerResourceAttribute.cs index df33e76d9..e8ebcd541 100644 --- a/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/CustomerResourceAttribute.cs +++ b/src/Basic/Senparc.Ncf.AreaBase/Admin/Filters/CustomerResourceAttribute.cs @@ -1,25 +1,25 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Microsoft.AspNetCore.Mvc.Filters; - -namespace Senparc.Ncf.AreaBase.Admin.Filters -{ - public class CustomerResourceAttribute : Attribute - { - public string[] ResourceCodes { get; set; } - - public CustomerResourceAttribute(params string[] resuouceCodes) - { - ResourceCodes = resuouceCodes; - } - } - - /// - /// 不进行权限校验 - /// - public class IgnoreAuthAttribute: Attribute, IFilterMetadata - { - - } -} +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace Senparc.Ncf.AreaBase.Admin.Filters +{ + public class CustomerResourceAttribute : Attribute + { + public string[] ResourceCodes { get; set; } + + public CustomerResourceAttribute(params string[] resuouceCodes) + { + ResourceCodes = resuouceCodes; + } + } + + /// + ///Do not perform permission verification + /// + public class IgnoreAuthAttribute: Attribute, IFilterMetadata + { + + } +} diff --git a/src/Basic/Senparc.Ncf.Core/AjaxReturnModel.cs b/src/Basic/Senparc.Ncf.Core/AjaxReturnModel.cs index 876292e43..8f3b9bc97 100644 --- a/src/Basic/Senparc.Ncf.Core/AjaxReturnModel.cs +++ b/src/Basic/Senparc.Ncf.Core/AjaxReturnModel.cs @@ -1,36 +1,36 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core -{ - - /// - /// ajax返回模型 - /// - public class AjaxReturnModel - { - public bool Success { get; set; } - - public string Msg { get; set; } - } - - /// - /// ajax返回模型 - /// - /// 返回的对象 - public class AjaxReturnModel : AjaxReturnModel - { - public T Data { get; set; } - - public AjaxReturnModel() - { - - } - - public AjaxReturnModel(T data) : this() - { - this.Data = data; - } - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core +{ + + /// + ///ajax return model + /// + public class AjaxReturnModel + { + public bool Success { get; set; } + + public string Msg { get; set; } + } + + /// + ///ajax return model + /// + /// Returned object + public class AjaxReturnModel : AjaxReturnModel + { + public T Data { get; set; } + + public AjaxReturnModel() + { + + } + + public AjaxReturnModel(T data) : this() + { + this.Data = data; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/AppServices/AppServiceLogger.cs b/src/Basic/Senparc.Ncf.Core/AppServices/AppServiceLogger.cs index c6b27e0b3..25208ad6c 100644 --- a/src/Basic/Senparc.Ncf.Core/AppServices/AppServiceLogger.cs +++ b/src/Basic/Senparc.Ncf.Core/AppServices/AppServiceLogger.cs @@ -1,48 +1,48 @@ -using Senparc.CO2NET.Trace; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.AppServices -{ - /// - /// AppService 日志处理 - /// - public class AppServiceLogger - { - StringBuilder stringBuilder = new StringBuilder(); - - public string Append() - { - stringBuilder.AppendLine(""); - return ""; - } - - public string Append(string msg) - { - stringBuilder.AppendLine($"[{SystemTime.Now.ToString()}]\t{msg}"); - return msg; - } - - public string GetLogs() - { - return stringBuilder.ToString(); - } - - public void SaveLogs(string name) - { - string logs = GetLogs(); - - //确保能够处理中文字符 - byte[] utf8Bytes = Encoding.UTF8.GetBytes(logs); - string utf8String = Encoding.UTF8.GetString(utf8Bytes); - - SenparcTrace.SendCustomLog(name, utf8String); - } - - public override string ToString() - { - return GetLogs(); - } - } -} +using Senparc.CO2NET.Trace; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.AppServices +{ + /// + ///AppService log processing + /// + public class AppServiceLogger + { + StringBuilder stringBuilder = new StringBuilder(); + + public string Append() + { + stringBuilder.AppendLine(""); + return ""; + } + + public string Append(string msg) + { + stringBuilder.AppendLine($"[{SystemTime.Now.ToString()}]\t{msg}"); + return msg; + } + + public string GetLogs() + { + return stringBuilder.ToString(); + } + + public void SaveLogs(string name) + { + string logs = GetLogs(); + + //Make sure it can handle Chinese characters + byte[] utf8Bytes = Encoding.UTF8.GetBytes(logs); + string utf8String = Encoding.UTF8.GetString(utf8Bytes); + + SenparcTrace.SendCustomLog(name, utf8String); + } + + public override string ToString() + { + return GetLogs(); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/AppServices/Exceptions/BaseAppServiceException.cs b/src/Basic/Senparc.Ncf.Core/AppServices/Exceptions/BaseAppServiceException.cs index 7dd3e689d..6e176cc73 100644 --- a/src/Basic/Senparc.Ncf.Core/AppServices/Exceptions/BaseAppServiceException.cs +++ b/src/Basic/Senparc.Ncf.Core/AppServices/Exceptions/BaseAppServiceException.cs @@ -1,19 +1,19 @@ -using Senparc.CO2NET.Exceptions; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.AppServices.Exceptions -{ - public class BaseAppServiceException : BaseException - { - /// - /// 状态码 - /// - public int StateCode { get; set; } - public BaseAppServiceException(int stateCode, string message, Exception inner, bool logged = false) : base(message, inner, logged) - { - StateCode = stateCode; - } - } -} +using Senparc.CO2NET.Exceptions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.AppServices.Exceptions +{ + public class BaseAppServiceException : BaseException + { + /// + /// status code + /// + public int StateCode { get; set; } + public BaseAppServiceException(int stateCode, string message, Exception inner, bool logged = false) : base(message, inner, logged) + { + StateCode = stateCode; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/AppServices/FunctionRenderAttribute.cs b/src/Basic/Senparc.Ncf.Core/AppServices/FunctionRenderAttribute.cs index 40efc5b82..344a57de9 100644 --- a/src/Basic/Senparc.Ncf.Core/AppServices/FunctionRenderAttribute.cs +++ b/src/Basic/Senparc.Ncf.Core/AppServices/FunctionRenderAttribute.cs @@ -1,31 +1,31 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.AppServices -{ - [AttributeUsage(/*AttributeTargets.Class |*/ AttributeTargets.Method)] - public class FunctionRenderAttribute : Attribute - { - /// - /// 名称 - /// - public string Name { get; set; } - /// - /// 说明 - /// - public string Description { get; set; } - /// - /// 分类到 XNCF 模块的 Regster 类型 - /// - public Type RegisterType { get; set; } - - public FunctionRenderAttribute(string name, string description, Type registerType/*TODO:可提供系统模块的默认值*/) - { - Name = name; - Description = description; - RegisterType = registerType; - } - - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.AppServices +{ + [AttributeUsage(/*AttributeTargets.Class |*/ AttributeTargets.Method)] + public class FunctionRenderAttribute : Attribute + { + /// + ///name + /// + public string Name { get; set; } + /// + /// illustrate + /// + public string Description { get; set; } + /// + /// Regster type classified to XNCF module + /// + public Type RegisterType { get; set; } + + public FunctionRenderAttribute(string name, string description, Type registerType/*TODO: Default values ​​for system modules can be provided*/) + { + Name = name; + Description = description; + RegisterType = registerType; + } + + } +} diff --git a/src/Basic/Senparc.Ncf.Core/AppServices/FunctionRenderSymbolHelper.cs b/src/Basic/Senparc.Ncf.Core/AppServices/FunctionRenderSymbolHelper.cs index 980119ae9..55fac6edb 100644 --- a/src/Basic/Senparc.Ncf.Core/AppServices/FunctionRenderSymbolHelper.cs +++ b/src/Basic/Senparc.Ncf.Core/AppServices/FunctionRenderSymbolHelper.cs @@ -6,7 +6,7 @@ namespace Senparc.Ncf.Core.AppServices { /// - /// FunctionRender 符号辅助类,负责解析 [#sym:FunctionRender] 标记。 + /// FunctionRender Symbol helper class responsible for parsing [#sym:FunctionRender] tags. /// public static class FunctionRenderSymbolHelper { @@ -16,7 +16,7 @@ public static class FunctionRenderSymbolHelper private static readonly Regex SymbolRegex = new Regex(@"\[#sym\s*:\s*(?[A-Za-z0-9_\.\-]+)\s*\]", RegexOptions.Compiled | RegexOptions.IgnoreCase); /// - /// 提取输入中的所有 sym 标记名称。 + /// Extract all `sym` marker names from the input. /// public static IReadOnlyList ExtractSymbolNames(string input) { @@ -36,7 +36,7 @@ public static IReadOnlyList ExtractSymbolNames(string input) } /// - /// 判断输入是否包含指定 sym 标记。 + /// Determine whether the input contains the specified sym tag. /// public static bool HasSymbol(string input, string symbolName) { @@ -49,7 +49,7 @@ public static bool HasSymbol(string input, string symbolName) } /// - /// 判断输入是否包含 FunctionRender sym 标记。 + /// Determines whether the input contains the FunctionRender sym tag. /// public static bool HasFunctionRenderSymbol(string input) { diff --git a/src/Basic/Senparc.Ncf.Core/AppServices/Helpers/AppServiceHelper.cs b/src/Basic/Senparc.Ncf.Core/AppServices/Helpers/AppServiceHelper.cs index 620f8ad80..c3d313edb 100644 --- a/src/Basic/Senparc.Ncf.Core/AppServices/Helpers/AppServiceHelper.cs +++ b/src/Basic/Senparc.Ncf.Core/AppServices/Helpers/AppServiceHelper.cs @@ -1,173 +1,173 @@ -using Senparc.CO2NET.Cache; -using Senparc.Ncf.Core.AppServices.Exceptions; -using Senparc.Ncf.Core.Config; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Core.AppServices -{ - /// - /// AppService 帮助类 - /// - public static class AppServiceHelper - { - - /// - /// AppService 的自动处理过程 - /// - /// 从 Service 返回的主体消息类型(必须为 DTO 或 值类型,不能是 Entity) - /// AppService - /// - /// - /// - /// 执行完成后是否保存日志 - /// 保存日志的名称,可选,如果留空,则返回当前 AppService 的名称 - /// - public static Task> GetResponseAsync(this IAppService appService, Func, AppServiceLogger, Task> func, Action, AppServiceLogger> exceptionHandler = null, Action, AppServiceLogger> afterFunc = null, bool saveLogAfterFinished = false, string saveLogName = null) - //where TData : class - { - return GetResponseAsync, TData>(appService, func, exceptionHandler, afterFunc, saveLogAfterFinished, saveLogName); - } - - ///// - ///// AppService 的自动处理过程(返回 string 数据) - ///// - ///// AppService - ///// - ///// - ///// - ///// 执行完成后是否保存日志 - ///// 保存日志的名称,可选,如果留空,则返回当前 AppService 的名称 - ///// - //public static Task> GetStringResponseAsync(this IAppService appService, Func, AppServiceLogger, Task> func, Action, AppServiceLogger> exceptionHandler = null, Action, AppServiceLogger> afterFunc = null, bool saveLogAfterFinished = false, string saveLogName = null) - //{ - // return GetResponseAsync, string>(appService, func, exceptionHandler, afterFunc, saveLogAfterFinished, saveLogName); - //} - - /// - /// AppService 的自动处理过程(返回 string 数据) - /// - /// AppService - /// - /// - /// - /// 执行完成后是否保存日志 - /// 保存日志的名称,可选,如果留空,则返回当前 AppService 的名称 - /// - public static Task GetStringResponseAsync(this IAppService appService, Func> func, Action exceptionHandler = null, Action afterFunc = null, bool saveLogAfterFinished = false, string saveLogName = null) - //where IStringAppResponse : StringAppResponse - { - //Func, AppServiceLogger, Task> adaptFunc = (response, logger) => - //{ - // return func?.Invoke((StringAppResponse)response, logger); - //}; - - //Action, AppServiceLogger> adaptExceptionHandler = (ex, response, logger) => - //{ - // exceptionHandler?.Invoke(ex, (StringAppResponse)response, logger); - //}; - - //Action, AppServiceLogger> adaptAfterFunc = (response, logger) => - //{ - // afterFunc?.Invoke((StringAppResponse)response, logger); - //}; - - return GetResponseAsync(appService, func, exceptionHandler, afterFunc, saveLogAfterFinished, saveLogName); - } - - /// - /// AppService 的自动处理过程 - /// - /// 从 Service 返回的主体消息类型(必须为 DTO 或 值类型,不能是 Entity) - /// - /// AppService - /// - /// - /// - /// 执行完成后是否保存日志 - /// 保存日志的名称,可选,如果留空,则返回当前 AppService 的名称 - /// - public static async Task GetResponseAsync(this IAppService appService, Func> func, Action exceptionHandler = null, Action afterFunc = null, bool saveLogAfterFinished = false, string saveLogName = null) - where TResponse : AppResponseBase, new() - { - var response = new TResponse(); - var logger = new AppServiceLogger(); - if (func == null) - { - response.StateCode = 404; - response.ErrorMessage = "未提供处理过程"; - logger.Append("未提供处理过程"); - return response; - } - - try - { - var result = await func.Invoke(response, logger); - if (result != null) - { - response.Data = result; - }; - - if (response.Success == null) - { - response.Success = true; - } - - ////判断文件类型 - //if (result is INcfFile) - //{ - - //} - - } - catch (Exception ex) - { - response.Success = false; - logger.Append($"发生错误({ex.GetType().FullName}):"); - logger.Append(ex.Message); - logger.Append(ex.StackTrace); - - if (ex is BaseAppServiceException appEx) - { - response.StateCode = appEx.StateCode; - response.ErrorMessage = appEx.Message; - logger.Append(appEx.InnerException?.StackTrace); - } - else - { - response.StateCode = 100; - response.ErrorMessage = ex.Message; - } - - exceptionHandler?.Invoke(ex, response, logger); - } - finally - { - afterFunc?.Invoke(response, logger); - - if (saveLogAfterFinished) - { - var name = saveLogName ?? $"完成执行:{appService.GetType().FullName}"; - logger.SaveLogs(saveLogName); - } - - try - { - if (SiteConfig.SenparcCoreSetting.RequestTempLogCacheMinutes > 0) - { - var tempId = response.RequestTempId; - var cache = appService.ServiceProvider.GetObjectCacheStrategyInstance(); - //为了加快响应速度,不等待 - _ = cache.SetAsync(tempId, logger.GetLogs(), TimeSpan.FromMinutes(SiteConfig.SenparcCoreSetting.RequestTempLogCacheMinutes)); - } - } - finally { } - } - - - - return response; - } - } -} +using Senparc.CO2NET.Cache; +using Senparc.Ncf.Core.AppServices.Exceptions; +using Senparc.Ncf.Core.Config; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Core.AppServices +{ + /// + ///AppService helper class + /// + public static class AppServiceHelper + { + + /// + /// Automatic processing process of AppService + /// + /// The body message type returned from Service (must be DTO or value type, cannot be Entity) + /// AppService + /// + /// + /// + /// Whether to save the log after execution is completed + /// The name of the saved log, optional, if left blank, returns the name of the current AppService + /// + public static Task> GetResponseAsync(this IAppService appService, Func, AppServiceLogger, Task> func, Action, AppServiceLogger> exceptionHandler = null, Action, AppServiceLogger> afterFunc = null, bool saveLogAfterFinished = false, string saveLogName = null) + //where TData : class + { + return GetResponseAsync, TData>(appService, func, exceptionHandler, afterFunc, saveLogAfterFinished, saveLogName); + } + + ///// + ///// Automatic processing process of AppService (return string data) + ///// + ///// AppService + ///// + ///// + ///// + ///// Whether to save the log after execution is completed + ///// The name of the saved log, optional, if left blank, the name of the current AppService will be returned + ///// + //public static Task> GetStringResponseAsync(this IAppService appService, Func, AppServiceLogger, Task> func, Action, AppServiceLogger> exceptionHandler = null, Action, AppServiceLogger> afterFunc = null, bool saveLogAfterFinished = false, string saveLogName = null) + //{ + // return GetResponseAsync, string>(appService, func, exceptionHandler, afterFunc, saveLogAfterFinished, saveLogName); + //} + + /// + /// Automatic processing process of AppService (return string data) + /// + /// AppService + /// + /// + /// + /// Whether to save the log after execution is completed + /// The name of the saved log, optional, if left blank, returns the name of the current AppService + /// + public static Task GetStringResponseAsync(this IAppService appService, Func> func, Action exceptionHandler = null, Action afterFunc = null, bool saveLogAfterFinished = false, string saveLogName = null) + //where IStringAppResponse : StringAppResponse + { + //Func, AppServiceLogger, Task> adaptFunc = (response, logger) => + //{ + // return func?.Invoke((StringAppResponse)response, logger); + //}; + + //Action, AppServiceLogger> adaptExceptionHandler = (ex, response, logger) => + //{ + // exceptionHandler?.Invoke(ex, (StringAppResponse)response, logger); + //}; + + //Action, AppServiceLogger> adaptAfterFunc = (response, logger) => + //{ + // afterFunc?.Invoke((StringAppResponse)response, logger); + //}; + + return GetResponseAsync(appService, func, exceptionHandler, afterFunc, saveLogAfterFinished, saveLogName); + } + + /// + /// Automatic processing process of AppService + /// + /// The body message type returned from Service (must be DTO or value type, cannot be Entity) + /// + /// AppService + /// + /// + /// + /// Whether to save the log after execution is completed + /// The name of the saved log, optional, if left blank, returns the name of the current AppService + /// + public static async Task GetResponseAsync(this IAppService appService, Func> func, Action exceptionHandler = null, Action afterFunc = null, bool saveLogAfterFinished = false, string saveLogName = null) + where TResponse : AppResponseBase, new() + { + var response = new TResponse(); + var logger = new AppServiceLogger(); + if (func == null) + { + response.StateCode = 404; + response.ErrorMessage = "未提供处理过程"; + logger.Append("未提供处理过程"); + return response; + } + + try + { + var result = await func.Invoke(response, logger); + if (result != null) + { + response.Data = result; + }; + + if (response.Success == null) + { + response.Success = true; + } + + ////Determine file type + //if (result is INcfFile) + //{ + + //} + + } + catch (Exception ex) + { + response.Success = false; + logger.Append($"发生错误({ex.GetType().FullName}):"); + logger.Append(ex.Message); + logger.Append(ex.StackTrace); + + if (ex is BaseAppServiceException appEx) + { + response.StateCode = appEx.StateCode; + response.ErrorMessage = appEx.Message; + logger.Append(appEx.InnerException?.StackTrace); + } + else + { + response.StateCode = 100; + response.ErrorMessage = ex.Message; + } + + exceptionHandler?.Invoke(ex, response, logger); + } + finally + { + afterFunc?.Invoke(response, logger); + + if (saveLogAfterFinished) + { + var name = saveLogName ?? $"完成执行:{appService.GetType().FullName}"; + logger.SaveLogs(saveLogName); + } + + try + { + if (SiteConfig.SenparcCoreSetting.RequestTempLogCacheMinutes > 0) + { + var tempId = response.RequestTempId; + var cache = appService.ServiceProvider.GetObjectCacheStrategyInstance(); + //For faster response time, do not wait + _ = cache.SetAsync(tempId, logger.GetLogs(), TimeSpan.FromMinutes(SiteConfig.SenparcCoreSetting.RequestTempLogCacheMinutes)); + } + } + finally { } + } + + + + return response; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/AppServices/Models/AppResponseBase.cs b/src/Basic/Senparc.Ncf.Core/AppServices/Models/AppResponseBase.cs index 6437ed0ab..15e888452 100644 --- a/src/Basic/Senparc.Ncf.Core/AppServices/Models/AppResponseBase.cs +++ b/src/Basic/Senparc.Ncf.Core/AppServices/Models/AppResponseBase.cs @@ -12,13 +12,13 @@ public interface IAppResponse string ErrorMessage { get; set; } object Data { get; set; } /// - /// 请求临时ID(用于调取日志) + /// Temporary request ID (used for log retrieval) /// string RequestTempId { get; } } /// - /// AppService 响应详细基础模型(一般提供给序列化 JSON 使用) + /// Base detailed response model for AppService (typically for JSON serialization) /// [Serializable] public class AppResponseBase : IAppResponse @@ -28,7 +28,7 @@ public class AppResponseBase : IAppResponse public string ErrorMessage { get; set; } public object Data { get; set; } /// - /// 请求临时ID(用于调取日志) + /// Temporary request ID (used for log retrieval) /// public string RequestTempId { get; private set; } @@ -59,7 +59,7 @@ public void ChangeRequestTempId(string newTempId) } /// - /// AppService 响应详细基础模型(一般提供给序列化 JSON 使用) + /// Base detailed response model for AppService (typically for JSON serialization) /// /// [Serializable] diff --git a/src/Basic/Senparc.Ncf.Core/AppServices/Models/StringAppResponse.cs b/src/Basic/Senparc.Ncf.Core/AppServices/Models/StringAppResponse.cs index 9ddddbe67..5f34386f0 100644 --- a/src/Basic/Senparc.Ncf.Core/AppServices/Models/StringAppResponse.cs +++ b/src/Basic/Senparc.Ncf.Core/AppServices/Models/StringAppResponse.cs @@ -1,13 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.AppServices -{ - /// - /// 泛型为 String 的 AppResponse - /// - public class StringAppResponse: AppResponseBase - { - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.AppServices +{ + /// + ///AppResponse whose generic type is String + /// + public class StringAppResponse: AppResponseBase + { + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Area/AreaData.cs b/src/Basic/Senparc.Ncf.Core/Area/AreaData.cs index 040977b01..f225742d0 100644 --- a/src/Basic/Senparc.Ncf.Core/Area/AreaData.cs +++ b/src/Basic/Senparc.Ncf.Core/Area/AreaData.cs @@ -386,10 +386,10 @@ public Base_AreaXmlVD GetAreaDataByProvinceAndCity(string provinceName, string c // //item.SetAttributeValue("MaxShopId", "100000"); // //Match city name - // //var citys = codeList.Where(s => item.CityName.Contains(s.City.Replace("自治区", "").Replace("县", "").Replace("市", ""))).ToList(); + // //var cities = codeList.Where(s => item.CityName.Contains(s.City.Replace("autonomous region", "").Replace("county", "").Replace("city", ""))).ToList(); - // var citys = codeList.Where(s => item.Attribute("CityName").Value.Contains(s.City.Replace("自治区",""))); + // var cities = codeList.Where(s => item.Attribute("CityName").Value.Contains(s.City.Replace("Autonomous Region",""))); // if (citys.Count() != 0) // { @@ -401,7 +401,7 @@ public Base_AreaXmlVD GetAreaDataByProvinceAndCity(string provinceName, string c // { // //City with no area code found // System.Web.HttpContext.Current.Response.Write( - // "省:"+this.GetProvincesData().Single(p=>p.ID==int.Parse(item.Attribute("PID").Value)).ProvinceName+" ,市:"+ item.Attribute("CityName").Value+"
"); + // "Province:"+this.GetProvincesData().Single(p=>p.ID==int.Parse(item.Attribute("PID").Value)).ProvinceName+", City: "+ item.Attribute("CityName").Value+"
"); // } // } // doc.Save(System.Web.HttpContext.Current.Server.MapPath(CITIES_XML_PATH+".bak")); @@ -484,7 +484,7 @@ public string GetCityCode(/*string provinceName,*/ string cityName, bool format) // //TODO: ideally use singleton pattern // XElement doc = this.GetXmlElement(CITIES_XML_PATH);//Get XML document // string xmlFilePath = System.Web.HttpContext.Current.Server.MapPath(CITIES_XML_PATH);//Path - // string backUpXmlFilePath = xmlFilePath + "." + DateTime.Now.ToString().Replace(":", "_") + ".更新MaxShopId(" + cityName + ").bak";//Backup file path + // string backUpXmlFilePath = xmlFilePath + "." + DateTime.Now.ToString().Replace(":", "_") + ".UpdateMaxShopId(" + cityName + ").bak";//Backup file path // var cityData = (from c in doc.Elements() where c.Attribute("CityName").Value == cityName select c).Single(); // //int shopId = int.Parse(cityData.Attribute("MaxShopId").Value); @@ -553,7 +553,7 @@ public static void GetProvinceCityNameFromIPCountry(string ipCountry, ref string /// Parse area name from IP Country field /// /// ipCountry string from IP database - /// Area level tokens, such as "省" "市" "区" + /// Area level tokens, such as "province" "city" "district" /// Cursor position to start searching /// private static string GetProvinceAreaNameFromIPCountry(string ipCountry, string[] areaNameList, ref int areaStartIndex) diff --git a/src/Basic/Senparc.Ncf.Core/Areas/AreaPageMenuItem.cs b/src/Basic/Senparc.Ncf.Core/Areas/AreaPageMenuItem.cs index 6d259c946..ebcf11934 100644 --- a/src/Basic/Senparc.Ncf.Core/Areas/AreaPageMenuItem.cs +++ b/src/Basic/Senparc.Ncf.Core/Areas/AreaPageMenuItem.cs @@ -1,37 +1,37 @@ -using Senparc.Ncf.Core.Models.DataBaseModel; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Areas -{ - /// - /// Area 页面菜单项 - /// - public class AreaPageMenuItem - { - public string Id { get; set; } - public string Url { get; set; } - public string Name { get; set; } - public string Icon { get; set; } - public string ParentId { get; set; } - public MenuType MenuType { get; set; } - - public AreaPageMenuItem(string url, string name, string icon) - { - Url = url; - Name = name; - Icon = icon; - } - - public AreaPageMenuItem(string id,string url, string name, string icon,string parentId,MenuType menuType) - { - Id = id; - Url = url; - Name = name; - Icon = icon; - ParentId = parentId; - MenuType = menuType; - } - } -} +using Senparc.Ncf.Core.Models.DataBaseModel; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Areas +{ + /// + ///Area page menu item + /// + public class AreaPageMenuItem + { + public string Id { get; set; } + public string Url { get; set; } + public string Name { get; set; } + public string Icon { get; set; } + public string ParentId { get; set; } + public MenuType MenuType { get; set; } + + public AreaPageMenuItem(string url, string name, string icon) + { + Url = url; + Name = name; + Icon = icon; + } + + public AreaPageMenuItem(string id,string url, string name, string icon,string parentId,MenuType menuType) + { + Id = id; + Url = url; + Name = name; + Icon = icon; + ParentId = parentId; + MenuType = menuType; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Areas/IAreaRegister.cs b/src/Basic/Senparc.Ncf.Core/Areas/IAreaRegister.cs index ccda61112..2cdb04b88 100644 --- a/src/Basic/Senparc.Ncf.Core/Areas/IAreaRegister.cs +++ b/src/Basic/Senparc.Ncf.Core/Areas/IAreaRegister.cs @@ -1,33 +1,33 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Areas -{ - /// - /// Area 的 Register 接口 - /// - public interface IAreaRegister - { - /// - /// 授权配置 - /// - /// - /// - /// - IMvcBuilder AuthorizeConfig(IMvcBuilder builder, Microsoft.Extensions.Hosting.IHostEnvironment/*IWebHostEnvironment*/ env); - - /// - /// 如果提供了 UI 界面,必须指定一个首页,如:Admin/ - /// 注意:此选项需要配合 XncfRegister 使用才有效,否则请忽略 - /// - string HomeUrl { get; } - - /// - /// 菜单项 - /// - List AreaPageMenuItems { get; } - } -} +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Areas +{ + /// + ///Area Register interface + /// + public interface IAreaRegister + { + /// + ///Authorization configuration + /// + /// + /// + /// + IMvcBuilder AuthorizeConfig(IMvcBuilder builder, Microsoft.Extensions.Hosting.IHostEnvironment/*IWebHostEnvironment*/ env); + + /// + /// If a UI interface is provided, a homepage must be specified, such as: Admin/ + /// Note: This option needs to be used with XncfRegister to be effective, otherwise please ignore + /// + string HomeUrl { get; } + + /// + ///menu item + /// + List AreaPageMenuItems { get; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/AssembleScan/AssembleScanHelper.cs b/src/Basic/Senparc.Ncf.Core/AssembleScan/AssembleScanHelper.cs index 0dbdbed0b..6bcd43ed8 100644 --- a/src/Basic/Senparc.Ncf.Core/AssembleScan/AssembleScanHelper.cs +++ b/src/Basic/Senparc.Ncf.Core/AssembleScan/AssembleScanHelper.cs @@ -107,7 +107,7 @@ public static List GetAssembiles(bool dynamicLoadAllDlls = true, bool if (dynamicLoadAllDlls) { - // 使用 AppDomain 或环境变量来动态获取路径 + // Use AppDomain or environment variables to dynamically obtain paths string directoryPath = AppDomain.CurrentDomain.BaseDirectory; // If needed, environment variables can also be used // string directoryPath = Environment.GetEnvironmentVariable("MY_APP_PATH"); diff --git a/src/Basic/Senparc.Ncf.Core/AssembleScan/AssembleScanItem.cs b/src/Basic/Senparc.Ncf.Core/AssembleScan/AssembleScanItem.cs index 7334c5c15..e41a1a1ab 100644 --- a/src/Basic/Senparc.Ncf.Core/AssembleScan/AssembleScanItem.cs +++ b/src/Basic/Senparc.Ncf.Core/AssembleScan/AssembleScanItem.cs @@ -1,58 +1,58 @@ -using Senparc.Ncf.Core.Exceptions; -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Text; - -namespace Senparc.Ncf.Core.AssembleScan -{ - public class AssembleScanItem - { - /// - /// 扫描是否结束 - /// - public bool ScanFinished { get; set; } - - /// - /// 扫描是否成功 - /// - public bool ScanSuccessed { get; set; } - - private Action _action; - - public AssembleScanItem(Action action) - { - _action = action ?? throw new ArgumentNullException("action"); - } - - private object _lock { get; set; } = new object(); - /// - /// 执行扫描 - /// - /// - public void Run(Assembly assembly) - { - lock (_lock) - { - try - { - _action(assembly); - ScanSuccessed &= true; - } - catch (Exception ex) - { - ScanSuccessed = false; - new NcfExceptionBase("执行程序集扫描出错", ex); - } - finally - { - } - } - } - - public void Finished() - { - ScanFinished = true; - } - } -} +using Senparc.Ncf.Core.Exceptions; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace Senparc.Ncf.Core.AssembleScan +{ + public class AssembleScanItem + { + /// + /// Is scanning finished? + /// + public bool ScanFinished { get; set; } + + /// + ///Scan is successful + /// + public bool ScanSuccessed { get; set; } + + private Action _action; + + public AssembleScanItem(Action action) + { + _action = action ?? throw new ArgumentNullException("action"); + } + + private object _lock { get; set; } = new object(); + /// + /// perform scan + /// + /// + public void Run(Assembly assembly) + { + lock (_lock) + { + try + { + _action(assembly); + ScanSuccessed &= true; + } + catch (Exception ex) + { + ScanSuccessed = false; + new NcfExceptionBase("执行程序集扫描出错", ex); + } + finally + { + } + } + } + + public void Finished() + { + ScanFinished = true; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Authorization/ICheckPermission.cs b/src/Basic/Senparc.Ncf.Core/Authorization/ICheckPermission.cs index 01f49667e..5e0cacb65 100644 --- a/src/Basic/Senparc.Ncf.Core/Authorization/ICheckPermission.cs +++ b/src/Basic/Senparc.Ncf.Core/Authorization/ICheckPermission.cs @@ -1,21 +1,21 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Core.Authorization -{ - /// - /// 检查权限 - /// - public interface ICheckPermission - { - /// - /// 检查权限 - /// - /// - /// - /// - Task HasPermissionAsync(string[] resourceCodes, int adminUserInfoId); - } -} +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Core.Authorization +{ + /// + ///check permissions + /// + public interface ICheckPermission + { + /// + ///check permissions + /// + /// + /// + /// + Task HasPermissionAsync(string[] resourceCodes, int adminUserInfoId); + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Authorization/PermissionAttribute.cs b/src/Basic/Senparc.Ncf.Core/Authorization/PermissionAttribute.cs index d8bde06dd..c1ddc4a0d 100644 --- a/src/Basic/Senparc.Ncf.Core/Authorization/PermissionAttribute.cs +++ b/src/Basic/Senparc.Ncf.Core/Authorization/PermissionAttribute.cs @@ -1,25 +1,25 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace Senparc.Ncf.Core.Authorization -{ - /// - /// 权限特性 - /// 用法[Permission("role.add,role.update")] - /// - public class PermissionAttribute : TypeFilterAttribute - { - //public string[] Codes { get; set; } - - /// - /// code里面不能存在英文逗号 TODO - /// - /// 多个code 英文逗号分割 - public PermissionAttribute(string codes) - : base(typeof(PermissionFilterAttribute)) - { - //Arguments = new[] { new PermissionRequirement(Codes) }; - Arguments = new[] { new PermissionRequirement(codes.Split(",")) }; - } - } -} +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Senparc.Ncf.Core.Authorization +{ + /// + ///permissions properties + /// Usage [Permission("role.add,role.update")] + /// + public class PermissionAttribute : TypeFilterAttribute + { + //public string[] Codes { get; set; } + + /// + //English commas cannot exist in / code TODO + /// + /// Multiple codes separated by English commas + public PermissionAttribute(string codes) + : base(typeof(PermissionFilterAttribute)) + { + //Arguments = new[] { new PermissionRequirement(Codes) }; + Arguments = new[] { new PermissionRequirement(codes.Split(",")) }; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Authorization/PermissionFilterAttribute.cs b/src/Basic/Senparc.Ncf.Core/Authorization/PermissionFilterAttribute.cs index 1634d8266..f3e5d5951 100644 --- a/src/Basic/Senparc.Ncf.Core/Authorization/PermissionFilterAttribute.cs +++ b/src/Basic/Senparc.Ncf.Core/Authorization/PermissionFilterAttribute.cs @@ -1,37 +1,37 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Senparc.Ncf.Core.AppServices; -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Core.Authorization -{ - public class PermissionFilterAttribute : Attribute, IAsyncAuthorizationFilter - { - private readonly PermissionRequirement _requirement; - private readonly IAuthorizationService _authorizationService; - - public PermissionFilterAttribute(IAuthorizationService authorizationService, PermissionRequirement requirement) - { - _authorizationService = authorizationService; - _requirement = requirement; - } - - public async Task OnAuthorizationAsync(AuthorizationFilterContext context) - { - var result = await _authorizationService.AuthorizeAsync(context.HttpContext.User, null, _requirement); - if (!result.Succeeded) - { - //TODO... ResourceCodes 是否需要暴露出去? - AppResponseBase responseBase = new AppResponseBase() { Data = _requirement.ResourceCodes, Success = false, ErrorMessage = "您没有此资源的操作权限。" }; - context.Result = new OkObjectResult(responseBase) - { - StatusCode = (int)System.Net.HttpStatusCode.Forbidden - }; - } - } - } -} +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Senparc.Ncf.Core.AppServices; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Core.Authorization +{ + public class PermissionFilterAttribute : Attribute, IAsyncAuthorizationFilter + { + private readonly PermissionRequirement _requirement; + private readonly IAuthorizationService _authorizationService; + + public PermissionFilterAttribute(IAuthorizationService authorizationService, PermissionRequirement requirement) + { + _authorizationService = authorizationService; + _requirement = requirement; + } + + public async Task OnAuthorizationAsync(AuthorizationFilterContext context) + { + var result = await _authorizationService.AuthorizeAsync(context.HttpContext.User, null, _requirement); + if (!result.Succeeded) + { + //TODO... Do ResourceCodes need to be exposed? + AppResponseBase responseBase = new AppResponseBase() { Data = _requirement.ResourceCodes, Success = false, ErrorMessage = "您没有此资源的操作权限。" }; + context.Result = new OkObjectResult(responseBase) + { + StatusCode = (int)System.Net.HttpStatusCode.Forbidden + }; + } + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Authorization/PermissionHandler.cs b/src/Basic/Senparc.Ncf.Core/Authorization/PermissionHandler.cs index 6db1d406c..fef1e75f7 100644 --- a/src/Basic/Senparc.Ncf.Core/Authorization/PermissionHandler.cs +++ b/src/Basic/Senparc.Ncf.Core/Authorization/PermissionHandler.cs @@ -1,53 +1,53 @@ -using Microsoft.AspNetCore.Authorization; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Core.Authorization -{ - /// - /// 校验 - /// - public class PermissionHandler : AuthorizationHandler - { - private readonly ICheckPermission _checkPermission; - - public PermissionHandler(ICheckPermission checkPermission) - { - _checkPermission = checkPermission; - } - - protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) - { - if (requirement.ResourceCodes.Length == 1 && PermissionRequirement.All.Equals(requirement.ResourceCodes[0])) - { - // 有且仅有一个* - context.Succeed(requirement); - return; - } - int adminUserInfoId = GetCurrentAdminUserInfoId(context); - bool hasPermission = await _checkPermission.HasPermissionAsync(requirement.ResourceCodes, adminUserInfoId); // 当前用户是否有权限 - if (hasPermission) - { - context.Succeed(requirement); - } - else - { - context.Fail(); - } - } - - /// - /// 获取当前登录人Id - /// - /// - /// - public virtual int GetCurrentAdminUserInfoId(AuthorizationHandlerContext context) - { - var user = context.User; - bool isConvertSucess = int.TryParse(user.Claims.FirstOrDefault(_ => _.Type == ClaimTypes.NameIdentifier)?.Value, out int adminUserInfoId); - return adminUserInfoId; - - } - } -} +using Microsoft.AspNetCore.Authorization; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Core.Authorization +{ + /// + ///check + /// + public class PermissionHandler : AuthorizationHandler + { + private readonly ICheckPermission _checkPermission; + + public PermissionHandler(ICheckPermission checkPermission) + { + _checkPermission = checkPermission; + } + + protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) + { + if (requirement.ResourceCodes.Length == 1 && PermissionRequirement.All.Equals(requirement.ResourceCodes[0])) + { + // There is and only one* + context.Succeed(requirement); + return; + } + int adminUserInfoId = GetCurrentAdminUserInfoId(context); + bool hasPermission = await _checkPermission.HasPermissionAsync(requirement.ResourceCodes, adminUserInfoId); // Does the current user have permissions? + if (hasPermission) + { + context.Succeed(requirement); + } + else + { + context.Fail(); + } + } + + /// + /// Get the current login user ID + /// + /// + /// + public virtual int GetCurrentAdminUserInfoId(AuthorizationHandlerContext context) + { + var user = context.User; + bool isConvertSucess = int.TryParse(user.Claims.FirstOrDefault(_ => _.Type == ClaimTypes.NameIdentifier)?.Value, out int adminUserInfoId); + return adminUserInfoId; + + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Authorization/PermissionRequirement.cs b/src/Basic/Senparc.Ncf.Core/Authorization/PermissionRequirement.cs index 418fdfb47..7c363aa79 100644 --- a/src/Basic/Senparc.Ncf.Core/Authorization/PermissionRequirement.cs +++ b/src/Basic/Senparc.Ncf.Core/Authorization/PermissionRequirement.cs @@ -1,35 +1,35 @@ -using Microsoft.AspNetCore.Authorization; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Senparc.Ncf.Core.Authorization -{ - /// - /// - /// - /// - public class PermissionRequirement: IAuthorizationRequirement - { - /// - /// 登录后即可访问 - /// - public const string All = "*"; - - /// - /// 资源code - /// - public string[] ResourceCodes { get; set; } - - public PermissionRequirement() - { - this.ResourceCodes = new string[] { All }; - } - - public PermissionRequirement(string[] resourceCodes) - { - ResourceCodes = resourceCodes.Where(code => !string.IsNullOrEmpty(code?.Trim())).Select(code => code.Trim()).ToArray(); // 去前后空格 - } - } -} +using Microsoft.AspNetCore.Authorization; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Senparc.Ncf.Core.Authorization +{ + /// + /// + /// + /// + public class PermissionRequirement: IAuthorizationRequirement + { + /// + /// Can be accessed after logging in + /// + public const string All = "*"; + + /// + ///resourcecode + /// + public string[] ResourceCodes { get; set; } + + public PermissionRequirement() + { + this.ResourceCodes = new string[] { All }; + } + + public PermissionRequirement(string[] resourceCodes) + { + ResourceCodes = resourceCodes.Where(code => !string.IsNullOrEmpty(code?.Trim())).Select(code => code.Trim()).ToArray(); // Remove leading and trailing spaces + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/AutoMapper/SystemProfile.cs b/src/Basic/Senparc.Ncf.Core/AutoMapper/SystemProfile.cs index 87a36af31..159b09b9a 100644 --- a/src/Basic/Senparc.Ncf.Core/AutoMapper/SystemProfile.cs +++ b/src/Basic/Senparc.Ncf.Core/AutoMapper/SystemProfile.cs @@ -1,36 +1,36 @@ -using AutoMapper; -using Microsoft.AspNetCore.Mvc.Rendering; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Core.Models.DataBaseModel; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.AutoMapper -{ - /// - /// AutoMapp 的 Profile - /// - public class SystemProfile : Profile - { - public SystemProfile() - { - CreateMap().ForMember(_ => _.Text, opt => opt.MapFrom(_ => _.MenuName)) - .ForMember(_ => _.Value, opt => opt.MapFrom(_ => _.Id)); - CreateMap(); - CreateMap().ForMember(_ => _.IsMenu, opt => opt.MapFrom(_ => true)); - CreateMap().ForMember(_ => _.IsMenu, opt => opt.MapFrom(_ => false)) - .ForMember(_ => _.MenuName, opt => opt.MapFrom(_ => _.ButtonName)) - .ForMember(_ => _.ParentId, opt => opt.MapFrom(_ => _.MenuId)) - .ForMember(_ => _.Sort, opt => opt.MapFrom(_ => 0)) - .ForMember(_ => _.Url, opt => opt.MapFrom(_ => _.Url)) - .ForMember(_ => _.Visible, opt => opt.MapFrom(_ => true)) - .ForMember(_ => _.Id, opt => opt.MapFrom(_ => _.Id)) - .ForMember(_ => _.ResourceCode, opt => opt.MapFrom(_ => _.OpearMark)); - CreateMap(); - - CreateMap(); - CreateMap(); - } - } -} +using AutoMapper; +using Microsoft.AspNetCore.Mvc.Rendering; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Core.Models.DataBaseModel; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.AutoMapper +{ + /// + /// Profile of AutoMapp + /// + public class SystemProfile : Profile + { + public SystemProfile() + { + CreateMap().ForMember(_ => _.Text, opt => opt.MapFrom(_ => _.MenuName)) + .ForMember(_ => _.Value, opt => opt.MapFrom(_ => _.Id)); + CreateMap(); + CreateMap().ForMember(_ => _.IsMenu, opt => opt.MapFrom(_ => true)); + CreateMap().ForMember(_ => _.IsMenu, opt => opt.MapFrom(_ => false)) + .ForMember(_ => _.MenuName, opt => opt.MapFrom(_ => _.ButtonName)) + .ForMember(_ => _.ParentId, opt => opt.MapFrom(_ => _.MenuId)) + .ForMember(_ => _.Sort, opt => opt.MapFrom(_ => 0)) + .ForMember(_ => _.Url, opt => opt.MapFrom(_ => _.Url)) + .ForMember(_ => _.Visible, opt => opt.MapFrom(_ => true)) + .ForMember(_ => _.Id, opt => opt.MapFrom(_ => _.Id)) + .ForMember(_ => _.ResourceCode, opt => opt.MapFrom(_ => _.OpearMark)); + CreateMap(); + + CreateMap(); + CreateMap(); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/AreaDataCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/AreaDataCache.cs index 7279dc423..1fab7b8e9 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/AreaDataCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/AreaDataCache.cs @@ -1,68 +1,68 @@ -using System.Collections.Generic; -using Senparc.CO2NET; -using Senparc.Ncf.Core.DI; -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.Core.Models; - - -namespace Senparc.Ncf.Core.Cache -{ - /// - /// 省数据缓存(来自XML文件中)。不要直接使用此方法,使用Common.AreaData获取。 - /// - [AutoDIType(DILifecycleType.Singleton)] - public class AreaDataCache_Province : BaseCache> - { - public const string CACHE_KEY = "AreaDataCache:Province"; - - - public AreaDataCache_Province() - : base(CACHE_KEY, null) - { - base.TimeOut = 9999; - } - - public override List Update() - { - return null; - } - } - - /// - /// 市数据缓存(来自XML文件中)。不要直接使用此方法,使用Common.AreaData获取。 - /// - public class AreaDataCache_City : BaseCache> - { - public const string CACHE_KEY = "AreaDataCache:City"; - - public AreaDataCache_City() - : base(CACHE_KEY, null) - { - base.TimeOut = 9999; - } - - public override List Update() - { - return null; - } - } - - /// - /// 区县数据缓存(来自XML文件中)。不要直接使用此方法,使用Common.AreaData获取。 - /// - public class AreaDataCache_District : BaseCache> - { - public const string CACHE_KEY = "AreaDataCache:District"; - - public AreaDataCache_District() - : base(CACHE_KEY, null) - { - base.TimeOut = 9999; - } - - public override List Update() - { - return null; - } - } -} +using System.Collections.Generic; +using Senparc.CO2NET; +using Senparc.Ncf.Core.DI; +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.Core.Models; + + +namespace Senparc.Ncf.Core.Cache +{ + /// + /// Provincial data cache (from XML file). Do not use this method directly, use Common.AreaData to get it. + /// + [AutoDIType(DILifecycleType.Singleton)] + public class AreaDataCache_Province : BaseCache> + { + public const string CACHE_KEY = "AreaDataCache:Province"; + + + public AreaDataCache_Province() + : base(CACHE_KEY, null) + { + base.TimeOut = 9999; + } + + public override List Update() + { + return null; + } + } + + /// + /// City data cache (from XML file). Do not use this method directly, use Common.AreaData to get it. + /// + public class AreaDataCache_City : BaseCache> + { + public const string CACHE_KEY = "AreaDataCache:City"; + + public AreaDataCache_City() + : base(CACHE_KEY, null) + { + base.TimeOut = 9999; + } + + public override List Update() + { + return null; + } + } + + /// + /// District and county data cache (from XML file). Do not use this method directly, use Common.AreaData to get it. + /// + public class AreaDataCache_District : BaseCache> + { + public const string CACHE_KEY = "AreaDataCache:District"; + + public AreaDataCache_District() + : base(CACHE_KEY, null) + { + base.TimeOut = 9999; + } + + public override List Update() + { + return null; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseCache.cs index ddfab3d56..988615552 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseCache.cs @@ -39,12 +39,12 @@ public BaseCache() { } //private ICacheStrategy _cache; /// - /// 缓存策略。 - /// 请尽量不要再BaseCache以外调用这个对象的方法,尤其Cache的Key在DictionaryCache中是会被重新定义的 + ///caching policy. + /// Please try not to call the method of this object outside BaseCache, especially the Key of Cache will be redefined in DictionaryCache /// public IBaseObjectCacheStrategy Cache { get; set; } /// - /// 超时时间,1400分钟为1天。 + /// Timeout time, 1400 minutes is 1 day. /// public int TimeOut { get; set; } @@ -63,14 +63,14 @@ public BaseCache(string cacheKey, INcfDbData db) } Cache = CacheStrategyFactory.GetObjectCacheStrategyInstance(); - this.CacheSetKey = cacheKey;//设置缓存集合键,必须提供 + this.CacheSetKey = cacheKey;//Set cache collection key, required } #region Synchronous Methods /// - /// Data不能在Update()方法中调用,否则会引发循环调用。Update()方法中应该使用SetData()方法 - /// Data只适用于简单类型,如果缓存类型为列表,则不适用 + ///Data cannot be called in the Update() method, otherwise it will cause a loop call. SetData() method should be used in Update() method + ///Data only works with simple types, not applicable if the cache type is a list /// public virtual T Data { @@ -97,7 +97,7 @@ public virtual T Data } /// - /// 设置整个缓存数据 + ///Set the entire cache data /// /// /// @@ -131,9 +131,9 @@ public virtual void UpdateToDatabase(T obj) #region Asynchronous Methods /// - /// 获取全部缓存数据 - /// Data不能在Update()方法中调用,否则会引发循环调用。Update()方法中应该使用SetData()方法 - /// Data只适用于简单类型,如果缓存类型为列表,则不适用 + /// Get all cached data + ///Data cannot be called in the Update() method, otherwise it will cause a loop call. SetData() method should be used in Update() method + ///Data only works with simple types, not applicable if the cache type is a list /// public virtual async Task GetDataAsync() { @@ -156,7 +156,7 @@ public virtual async Task GetDataAsync() } /// - /// 设置整个缓存数据 + ///Set the entire cache data /// /// /// diff --git a/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseDictionary/BaseDictionaryCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseDictionary/BaseDictionaryCache.cs index ea8a09f6a..4b2b9253d 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseDictionary/BaseDictionaryCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseDictionary/BaseDictionaryCache.cs @@ -50,7 +50,7 @@ public abstract class BaseDictionaryCache : BaseDictionaryCache /// /// - /// 单位:分钟。1440为一天。 + /// Unit: minutes. 1440 is one day. public BaseDictionaryCache(string CACHE_KEY, INcfDbData db, int timeOut) : base(CACHE_KEY, db, timeOut) { @@ -64,13 +64,13 @@ public abstract class BaseDictionaryCache : where TEntity : class/*, new()*/ { /// - /// 获取缓存中最终的Key + /// Get the final Key in the cache /// /// /// protected string GetFinalCacheKey(TKey key) { - //TODO:判断Key类型 + //TODO: Determine Key type TypeCode code = key == null ? TypeCode.DBNull : Type.GetTypeCode(key.GetType()); string keyCode = null; switch (code) @@ -95,7 +95,7 @@ protected string GetFinalCacheKey(TKey key) default: code = TypeCode.Object; string keyStr = System.Text.Json.JsonSerializer.Serialize(key); - keyCode = MD5.GetMD5Code(keyStr, "");//将对象序列化,然后拼接成字符串并转成MD5,确保唯一性。性能上可能会有一些损失,所以尽量不要太复杂的类型做Key + keyCode = MD5.GetMD5Code(keyStr, "");//Serialize the object, then concatenate it into a string and convert it to MD5 to ensure uniqueness. There may be some loss in performance, so try not to use too complex types as keys. break; } @@ -107,7 +107,7 @@ protected string GetFinalCacheKey(TKey key) } else { - finalKey = $"{CacheKey}@@@{keyCode}";//有的缓存策略可能不允许:作为分隔符[LocalCache 存在问题] + finalKey = $"{CacheKey}@@@{keyCode}";//Some caching policies may not allow: as a delimiter [Problem with LocalCache] } return finalKey; @@ -117,7 +117,7 @@ protected string GetFinalCacheKey(TKey key) //{ // using (var ms = new MemoryStream()) // { - // //.NET 8.0 中已标记为过时且无法编译 + // // Marked obsolete and uncompilable in .NET 8.0 // new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Serialize(ms, value); // return new ArraySegment(ms.GetBuffer(), 0, (int)ms.Length); @@ -125,7 +125,7 @@ protected string GetFinalCacheKey(TKey key) //} ///// - ///// 获取指定Key下所有的子Key的Value + ///// Get the Value of all sub-Keys under the specified Key ///// ///// xxx* ///// @@ -150,13 +150,13 @@ protected string GetFinalCacheKey(TKey key) /// /// /// - /// 单位:分钟。1440为一天。 + /// Unit: minutes. 1440 is one day. public BaseDictionaryCache(string CACHE_KEY, INcfDbData db, int timeOut) : base(CACHE_KEY, db) { base.TimeOut = timeOut; - //强制初始化 + //Force initialization //var load = base.Data; //var finalCacheKey = GetFinalCacheKey(key); //if (base.Cache.CheckExisted(finalCacheKey)) @@ -198,10 +198,10 @@ public virtual TValue InsertObjectToCache(TKey key, TEntity obj) } catch (Exception ex) { - //var msg = "System debug record cache a bug。发生错误:{0}。当前参数:base.Data:{1}(Count:{4}),key:{2},obj:{3}。Null情况分别是:{4},{5},{6}" + //var msg = "System debug record cache a bug. Error occurred: {0}. Current parameters: base.Data: {1} (Count: {4}), key: {2}, obj: {3}. Null cases are: {4}, {5}, {6}" // .With(ex.Message, base.Data, key, obj, base.Data == null, key == null, obj == null, base.Data.Count); - var msg = $"System debug record cache a bug。发生错误:{ex.Message}。再次访问base.Data=null:{base.Data == null}";//实际上这里base.Data还是为null + var msg = $"System debug record cache a bug。发生错误:{ex.Message}。再次访问base.Data=null:{base.Data == null}";//In fact, base.Data here is still null. LogUtility.SystemLogger.Debug(msg, ex); throw new Exception(msg, ex); } @@ -292,10 +292,10 @@ public virtual async Task InsertObjectToCacheAsync(TKey key, TEntity obj } catch (Exception ex) { - //var msg = "System debug record cache a bug。发生错误:{0}。当前参数:base.Data:{1}(Count:{4}),key:{2},obj:{3}。Null情况分别是:{4},{5},{6}" + //var msg = "System debug record cache a bug. Error occurred: {0}. Current parameters: base.Data: {1} (Count: {4}), key: {2}, obj: {3}. Null cases are: {4}, {5}, {6}" // .With(ex.Message, base.Data, key, obj, base.Data == null, key == null, obj == null, base.Data.Count); - var msg = $"System debug record cache a bug。发生错误:{ex.Message}。再次访问base.Data=null:{base.Data == null}";//实际上这里base.Data还是为null + var msg = $"System debug record cache a bug。发生错误:{ex.Message}。再次访问base.Data=null:{base.Data == null}";//In fact, base.Data here is still null. LogUtility.SystemLogger.Debug(msg, ex); throw new Exception(msg, ex); } diff --git a/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseDictionary/BaseStringDictionary/BaseStringDictionaryCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseDictionary/BaseStringDictionary/BaseStringDictionaryCache.cs index 0b9a005fa..7f697dc47 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseDictionary/BaseStringDictionary/BaseStringDictionaryCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/BaseDictionary/BaseStringDictionary/BaseStringDictionaryCache.cs @@ -1,46 +1,46 @@ -using Senparc.Ncf.Core.Models; - -namespace Senparc.Ncf.Core.Cache -{ - public interface IBaseStringDictionaryCache : IBaseStringDictionaryCache where TValue : class,new() - { - } - public interface IBaseStringDictionaryCache : IBaseDictionaryCache where TValue : class,new() - { - - } - - public abstract class BaseStringDictionaryCache : BaseStringDictionaryCache, - IBaseStringDictionaryCache - where TValue : class, new() - { - /// - /// - /// - /// - /// - /// 单位:分钟。1440为一天。 - public BaseStringDictionaryCache(string CACHE_KEY, INcfDbData db, int timeOut) - : base(CACHE_KEY, db, timeOut) - { - base.TimeOut = timeOut; - } - } - - public abstract class BaseStringDictionaryCache : BaseDictionaryCache, IBaseStringDictionaryCache - where TValue : class,new() - where TEntity : class/*, new()*/ - { - /// - /// - /// - /// - /// - /// 单位:分钟。1440为一天。 - public BaseStringDictionaryCache(string CACHE_KEY, INcfDbData db, int timeOut) - : base(CACHE_KEY, db, timeOut) - { - base.TimeOut = timeOut; - } - } -} +using Senparc.Ncf.Core.Models; + +namespace Senparc.Ncf.Core.Cache +{ + public interface IBaseStringDictionaryCache : IBaseStringDictionaryCache where TValue : class,new() + { + } + public interface IBaseStringDictionaryCache : IBaseDictionaryCache where TValue : class,new() + { + + } + + public abstract class BaseStringDictionaryCache : BaseStringDictionaryCache, + IBaseStringDictionaryCache + where TValue : class, new() + { + /// + /// + /// + /// + /// + /// Unit: minutes. 1440 is one day. + public BaseStringDictionaryCache(string CACHE_KEY, INcfDbData db, int timeOut) + : base(CACHE_KEY, db, timeOut) + { + base.TimeOut = timeOut; + } + } + + public abstract class BaseStringDictionaryCache : BaseDictionaryCache, IBaseStringDictionaryCache + where TValue : class,new() + where TEntity : class/*, new()*/ + { + /// + /// + /// + /// + /// + /// Unit: minutes. 1440 is one day. + public BaseStringDictionaryCache(string CACHE_KEY, INcfDbData db, int timeOut) + : base(CACHE_KEY, db, timeOut) + { + base.TimeOut = timeOut; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/ICacheData.cs b/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/ICacheData.cs index 81c244db0..58e2a1766 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/ICacheData.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/BaseCache/ICacheData.cs @@ -1,14 +1,14 @@ -using System; - -namespace Senparc.Ncf.Core.Cache -{ - public interface ICacheData - { - /// - /// 缓存键 - /// - string Key { get; } - - DateTime CacheTime { get; set; } - } -} +using System; + +namespace Senparc.Ncf.Core.Cache +{ + public interface ICacheData + { + /// + ///cache key + /// + string Key { get; } + + DateTime CacheTime { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/BaseCacheBindable.cs b/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/BaseCacheBindable.cs index ba4338644..92692d104 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/BaseCacheBindable.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/BaseCacheBindable.cs @@ -7,13 +7,13 @@ namespace Senparc.Ncf.Core.Cache { /// - /// 所有需要分布式缓存的实体基类 + /// All entity base classes that require distributed caching /// [Serializable] public abstract class BaseCacheBindable : BindableBase where T : class, ICacheData, new() { ///// - ///// 缓存键 + ///// Cache key ///// //public abstract object Key { get; } @@ -44,9 +44,9 @@ protected void BaseCacheBindable_PropertyChanged(object sender, System.Component var mqKey = CacheDataMessageQueue.GenerateKey("SenparcCache", sender.GetType(), objCacheData.Key as string, "UpdateCache"); - //获取对应Container的缓存相关 + //Get the cache related information of the corresponding Container - //加入消息列队,每过一段时间进行自动更新,防止属性连续被编辑,短时间内反复更新缓存。 + //Join the message queue and automatically update every period of time to prevent attributes from being edited continuously and update the cache repeatedly in a short period of time. CacheDataMessageQueue mq = new CacheDataMessageQueue(); mq.Add(mqKey, () => { @@ -63,7 +63,7 @@ protected void BaseCacheBindable_PropertyChanged(object sender, System.Component /// - /// 设置Container属性 + ///Set Container properties /// /// /// diff --git a/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/CacheDataMessageQueue.cs b/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/CacheDataMessageQueue.cs index 384c49e58..f8cd69389 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/CacheDataMessageQueue.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/CacheDataMessageQueue.cs @@ -1,143 +1,143 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Senparc.Ncf.Core.Cache -{ - /// - /// 缓存消息列队,用于自动更新分布式缓存数据 - /// - public class CacheDataMessageQueue - { - /// - /// 列队数据集合 - /// - private static Dictionary MessageQueueDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); - - /// - /// 同步执行锁 - /// - private static object MessageQueueSyncLock = new object(); - /// - /// 立即同步所有缓存执行锁(给OperateQueue()使用) - /// - private static object FlushCacheLock = new object(); - - /// - /// 生成Key - /// - /// 列队应用名称,如“ContainerBag” - /// 操作对象类型 - /// 对象唯一标识Key - /// 操作名称,如“UpdateContainerBag” - /// - public static string GenerateKey(string name, Type senderType, string identityKey, string actionName) - { - var key = string.Format("Name@{0}||Type@{1}||Key@{2}||ActionName@{3}", - name, senderType, identityKey, actionName); - return key; - } - - /// - /// 操作列队 - /// - public static void OperateQueue() - { - lock (FlushCacheLock) - { - var mq = new CacheDataMessageQueue(); - var key = mq.GetCurrentKey(); //获取最新的Key - while (!string.IsNullOrEmpty(key)) - { - var mqItem = mq.GetItem(key); //获取任务项 - mqItem.Action(); //执行 - mq.Remove(key); //清除 - key = mq.GetCurrentKey(); //获取最新的Key - } - } - } - - /// - /// 获取当前等待执行的Key - /// - /// - public string GetCurrentKey() - { - lock (MessageQueueSyncLock) - { - return MessageQueueDictionary.Keys.FirstOrDefault(); - } - } - - /// - /// 获取SenparcMessageQueueItem - /// - /// - /// - public CacheDataMessageQueueItem GetItem(string key) - { - lock (MessageQueueSyncLock) - { - if (MessageQueueDictionary.ContainsKey(key)) - { - return MessageQueueDictionary[key]; - } - return null; - } - } - - /// - /// 添加列队成员 - /// - /// - /// - public CacheDataMessageQueueItem Add(string key, Action action) - { - lock (MessageQueueSyncLock) - { - //if (!MessageQueueDictionary.ContainsKey(key)) - //{ - // MessageQueueList.Add(key); - //} - //else - //{ - // MessageQueueList.Remove(key); - // MessageQueueList.Add(key);//移动到末尾 - //} - - var mqItem = new CacheDataMessageQueueItem(key, action); - MessageQueueDictionary[key] = mqItem; - return mqItem; - } - } - - /// - /// 移除列队成员 - /// - /// - public void Remove(string key) - { - lock (MessageQueueSyncLock) - { - if (MessageQueueDictionary.ContainsKey(key)) - { - MessageQueueDictionary.Remove(key); - //MessageQueueList.Remove(key); - } - } - } - - /// - /// 获得当前列队数量 - /// - /// - public int GetCount() - { - lock (MessageQueueSyncLock) - { - return MessageQueueDictionary.Count; - } - } - - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Senparc.Ncf.Core.Cache +{ + /// + /// Cache message queue, used to automatically update distributed cache data + /// + public class CacheDataMessageQueue + { + /// + ///Queue data collection + /// + private static Dictionary MessageQueueDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Synchronous execution lock + /// + private static object MessageQueueSyncLock = new object(); + /// + /// Immediately synchronize all cache execution locks (used by OperateQueue()) + /// + private static object FlushCacheLock = new object(); + + /// + /// Generate Key + /// + /// Queue application name, such as "ContainerBag" + /// Operation object type + /// Object unique identification Key + /// Action name, such as "UpdateContainerBag" + /// + public static string GenerateKey(string name, Type senderType, string identityKey, string actionName) + { + var key = string.Format("Name@{0}||Type@{1}||Key@{2}||ActionName@{3}", + name, senderType, identityKey, actionName); + return key; + } + + /// + ///Operation queue + /// + public static void OperateQueue() + { + lock (FlushCacheLock) + { + var mq = new CacheDataMessageQueue(); + var key = mq.GetCurrentKey(); //Get the latest Key + while (!string.IsNullOrEmpty(key)) + { + var mqItem = mq.GetItem(key); //Get task items + mqItem.Action(); //implement + mq.Remove(key); //Clear + key = mq.GetCurrentKey(); //Get the latest Key + } + } + } + + /// + /// Get the Key currently waiting for execution + /// + /// + public string GetCurrentKey() + { + lock (MessageQueueSyncLock) + { + return MessageQueueDictionary.Keys.FirstOrDefault(); + } + } + + /// + /// Get SenparcMessageQueueItem + /// + /// + /// + public CacheDataMessageQueueItem GetItem(string key) + { + lock (MessageQueueSyncLock) + { + if (MessageQueueDictionary.ContainsKey(key)) + { + return MessageQueueDictionary[key]; + } + return null; + } + } + + /// + ///Add queue members + /// + /// + /// + public CacheDataMessageQueueItem Add(string key, Action action) + { + lock (MessageQueueSyncLock) + { + //if (!MessageQueueDictionary.ContainsKey(key)) + //{ + // MessageQueueList.Add(key); + //} + //else + //{ + // MessageQueueList.Remove(key); + // MessageQueueList.Add(key);//Move to the end + //} + + var mqItem = new CacheDataMessageQueueItem(key, action); + MessageQueueDictionary[key] = mqItem; + return mqItem; + } + } + + /// + ///Remove queue members + /// + /// + public void Remove(string key) + { + lock (MessageQueueSyncLock) + { + if (MessageQueueDictionary.ContainsKey(key)) + { + MessageQueueDictionary.Remove(key); + //MessageQueueList.Remove(key); + } + } + } + + /// + /// Get the current queue number + /// + /// + public int GetCount() + { + lock (MessageQueueSyncLock) + { + return MessageQueueDictionary.Count; + } + } + + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/CacheDataMessageQueueItem.cs b/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/CacheDataMessageQueueItem.cs index e6f312bf5..8065875fe 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/CacheDataMessageQueueItem.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/CacheDataMessageQueue/CacheDataMessageQueueItem.cs @@ -1,41 +1,41 @@ -using System; - -namespace Senparc.Ncf.Core.Cache -{ - /// - /// CacheDataMessageQueue消息列队项 - /// - public class CacheDataMessageQueueItem - { - /// - /// 列队项唯一标识 - /// - public string Key { get; set; } - /// - /// 列队项目命中触发时执行的委托 - /// - public Action Action { get; set; } - /// - /// 此实例对象的创建时间 - /// - public DateTime AddTime { get; set; } - /// - /// 项目说明(主要用于调试) - /// - public string Description { get; set; } - - /// - /// 初始化SenparcMessageQueue消息列队项 - /// - /// - /// - /// - public CacheDataMessageQueueItem(string key, Action action, string description = null) - { - Key = key; - Action = action; - Description = description; - AddTime = DateTime.Now; - } - } -} +using System; + +namespace Senparc.Ncf.Core.Cache +{ + /// + ///CacheDataMessageQueue message queue entry + /// + public class CacheDataMessageQueueItem + { + /// + /// Unique identifier of queue item + /// + public string Key { get; set; } + /// + /// Delegation executed when the queue item is hit and triggered + /// + public Action Action { get; set; } + /// + /// The creation time of this instance object + /// + public DateTime AddTime { get; set; } + /// + /// Item description (mainly for debugging) + /// + public string Description { get; set; } + + /// + /// Initialize SenparcMessageQueue message queue items + /// + /// + /// + /// + public CacheDataMessageQueueItem(string key, Action action, string description = null) + { + Key = key; + Action = action; + Description = description; + AddTime = DateTime.Now; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/CommonDataCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/CommonDataCache.cs index 79a39640c..b5c865e29 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/CommonDataCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/CommonDataCache.cs @@ -1,41 +1,41 @@ -using System; - -namespace Senparc.Ncf.Core.Cache -{ - public class CommonDataCache : BaseCache, ICommonDataCache where T : class,new() - { - public const string CACHE_KEY = "__CommonDataCache"; - string _key; - Func _fun; - - /// - /// 公用缓存模块,默认超时时间:1440分钟(1天) - /// - /// 全局唯一Key(只需要确保在CommonDataCache模块内唯一) - /// - public CommonDataCache(string key, Func fun) - :this(key, 1440, fun) - { - } - - /// - /// 公用缓存模块 - /// - /// 全局唯一Key(只需要确保在CommonDataCache模块内唯一) - /// 缓存时间(分钟) - /// - public CommonDataCache(string key, int timeout, Func fun) - : base(CACHE_KEY + key) - { - _key = CACHE_KEY + key; - base.TimeOut = timeout; - this._fun = fun; - } - - public override T Update() - { - base.SetData(_fun(), base.TimeOut, null); - return base.Data; - } - } -} +using System; + +namespace Senparc.Ncf.Core.Cache +{ + public class CommonDataCache : BaseCache, ICommonDataCache where T : class,new() + { + public const string CACHE_KEY = "__CommonDataCache"; + string _key; + Func _fun; + + /// + ///Public cache module, default timeout: 1440 minutes (1 day) + /// + /// Globally unique Key (only needs to be unique within the CommonDataCache module) + /// + public CommonDataCache(string key, Func fun) + :this(key, 1440, fun) + { + } + + /// + ///Public cache module + /// + /// Globally unique Key (only needs to be unique within the CommonDataCache module) + /// Cache time (minutes) + /// + public CommonDataCache(string key, int timeout, Func fun) + : base(CACHE_KEY + key) + { + _key = CACHE_KEY + key; + base.TimeOut = timeout; + this._fun = fun; + } + + public override T Update() + { + base.SetData(_fun(), base.TimeOut, null); + return base.Data; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/EntityCache/FullSystemConfigCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/EntityCache/FullSystemConfigCache.cs index 2253482b6..69757cefd 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/EntityCache/FullSystemConfigCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/EntityCache/FullSystemConfigCache.cs @@ -1,91 +1,91 @@ -using System; -using System.Linq; -using Microsoft.AspNetCore.Http; -using Senparc.CO2NET; -using Senparc.Ncf.Core.Cache; -using Senparc.Ncf.Core.DI; -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.Core.Exceptions; -using Senparc.Ncf.Core.Models; -using Microsoft.Extensions.DependencyInjection; - -namespace Senparc.Ncf.Core.Cache -{ - [AutoDIType(DILifecycleType.Scoped)] - public class FullSystemConfigCache : BaseCache/*, IFullSystemConfigCache*/ - { - public const string CACHE_KEY = "FullSystemConfigCache"; - private INcfDbData _dataContext => base._db as INcfDbData; - - public FullSystemConfigCache(INcfDbData db) - : base(CACHE_KEY, db) - { - base.TimeOut = 1440; - } - - public override FullSystemConfig Update() - { - SystemConfig systemConfig = null; - try - { - systemConfig = (_dataContext.BaseDataContext as SenparcEntitiesBase).Set().FirstOrDefault(); - } - catch (Exception ex) - { - var msg = @$"FullSystemConfigCache 访问数据库异常,推测系统未安装或未正确配置数据库。 -提示信息: -{ex.Message} -{ex.StackTrace} -"; - new NcfUninstallException(msg, null); - } - - FullSystemConfig fullSystemConfig; - if (systemConfig != null) - { - fullSystemConfig = FullSystemConfig.CreateEntity(systemConfig); - } - else - { - string hostName = null; - try - { - var httpContextAccessor = SenparcDI.GetServiceProvider().GetService(); - var httpContext = httpContextAccessor.HttpContext; - var urlData = httpContext.Request; - var scheme = urlData.Scheme;//协议 - var host = urlData.Host.Host;//主机名(不带端口) - var port = urlData.Host.Port ?? -1;//端口(因为从.NET Framework移植,因此不直接使用urlData.Host) - string portSetting = null;//Url中的端口部分 - string schemeUpper = scheme.ToUpper();//协议(大写) - string baseUrl = httpContext.Request.PathBase;//子站点应用路径 - - if (port == -1 || //这个条件只有在 .net core 中, Host.Port == null 的情况下才会发生 - (schemeUpper == "HTTP" && port == 80) || - (schemeUpper == "HTTPS" && port == 443)) - { - portSetting = "";//使用默认值 - } - else - { - portSetting = ":" + port;//添加端口 - } - - hostName = $"{scheme}://{host}{portSetting}"; - - } - catch - { - } - - //尝试安装 - - throw new NcfUninstallException($"NCF 系统未初始化,请先执行 {hostName}/Install 进行数据初始化", null); - } - - - base.SetData(fullSystemConfig, base.TimeOut, null); - return base.Data; - } - } -} +using System; +using System.Linq; +using Microsoft.AspNetCore.Http; +using Senparc.CO2NET; +using Senparc.Ncf.Core.Cache; +using Senparc.Ncf.Core.DI; +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.Core.Exceptions; +using Senparc.Ncf.Core.Models; +using Microsoft.Extensions.DependencyInjection; + +namespace Senparc.Ncf.Core.Cache +{ + [AutoDIType(DILifecycleType.Scoped)] + public class FullSystemConfigCache : BaseCache/*, IFullSystemConfigCache*/ + { + public const string CACHE_KEY = "FullSystemConfigCache"; + private INcfDbData _dataContext => base._db as INcfDbData; + + public FullSystemConfigCache(INcfDbData db) + : base(CACHE_KEY, db) + { + base.TimeOut = 1440; + } + + public override FullSystemConfig Update() + { + SystemConfig systemConfig = null; + try + { + systemConfig = (_dataContext.BaseDataContext as SenparcEntitiesBase).Set().FirstOrDefault(); + } + catch (Exception ex) + { + var msg = @$"FullSystemConfigCache 访问数据库异常,推测系统未安装或未正确配置数据库。 +提示信息: +{ex.Message} +{ex.StackTrace} +"; + new NcfUninstallException(msg, null); + } + + FullSystemConfig fullSystemConfig; + if (systemConfig != null) + { + fullSystemConfig = FullSystemConfig.CreateEntity(systemConfig); + } + else + { + string hostName = null; + try + { + var httpContextAccessor = SenparcDI.GetServiceProvider().GetService(); + var httpContext = httpContextAccessor.HttpContext; + var urlData = httpContext.Request; + var scheme = urlData.Scheme;//protocol + var host = urlData.Host.Host;//Hostname (without port) + var port = urlData.Host.Port ?? -1;//Port (does not use urlData.Host directly because it is ported from .NET Framework) + string portSetting = null;//Port part in URL + string schemeUpper = scheme.ToUpper();//Agreement (uppercase) + string baseUrl = httpContext.Request.PathBase;//Subsite application path + + if (port == -1 || //This condition only occurs when Host.Port == null in .net core + (schemeUpper == "HTTP" && port == 80) || + (schemeUpper == "HTTPS" && port == 443)) + { + portSetting = "";//Use default value + } + else + { + portSetting = ":" + port;//Add port + } + + hostName = $"{scheme}://{host}{portSetting}"; + + } + catch + { + } + + //try to install + + throw new NcfUninstallException($"NCF 系统未初始化,请先执行 {hostName}/Install 进行数据初始化", null); + } + + + base.SetData(fullSystemConfig, base.TimeOut, null); + return base.Data; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/Extensions/BaseCacheExtensions.cs b/src/Basic/Senparc.Ncf.Core/Cache/Extensions/BaseCacheExtensions.cs index 62ecd5dfa..5fca75a5c 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/Extensions/BaseCacheExtensions.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/Extensions/BaseCacheExtensions.cs @@ -8,7 +8,7 @@ namespace Senparc.Ncf.Core.Cache.Extensions public static class BaseCacheExtensions { /// - /// 获取所有缓存集合 + /// Get all cache collections /// /// /// @@ -19,7 +19,7 @@ public static class BaseCacheExtensions } /// - /// 【异步方法】获取所有缓存信息集合 + /// [Async] Get all cache information collections /// /// /// @@ -30,7 +30,7 @@ public static class BaseCacheExtensions } /// - /// 获取所有以key为前缀的缓存信息集合 + /// Get all cache information collections prefixed by key /// /// /// @@ -42,7 +42,7 @@ public static class BaseCacheExtensions } /// - /// 【异步方法】获取所有以key为前缀的缓存信息集合 + /// [Async] Get all cache information collections prefixed by key /// /// /// diff --git a/src/Basic/Senparc.Ncf.Core/Cache/Interface/IBaseCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/Interface/IBaseCache.cs index bc40bbfed..a87dbb4a7 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/Interface/IBaseCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/Interface/IBaseCache.cs @@ -10,7 +10,7 @@ public interface IBaseCache : IAutoDI { IBaseObjectCacheStrategy Cache { get; set; } /// - /// Data不能在Update()方法中调用,否则会引发循环调用。Update()方法中应该使用SetData()方法 + ///Data cannot be called in the Update() method, otherwise it will cause a loop call. SetData() method should be used in Update() method /// DateTime CacheTime { get; set; } DateTime CacheTimeOut { get; set; } @@ -33,7 +33,7 @@ public interface IBaseCache : IAutoDI ///// - ///// 更新到缓存 + ///// Update to cache ///// ///// ///// diff --git a/src/Basic/Senparc.Ncf.Core/Cache/Interface/IBaseCacheStrategy.cs b/src/Basic/Senparc.Ncf.Core/Cache/Interface/IBaseCacheStrategy.cs index 6e857d776..150f335fb 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/Interface/IBaseCacheStrategy.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/Interface/IBaseCacheStrategy.cs @@ -1,33 +1,33 @@ -using System; -using System.Collections.Generic; -using Senparc.CO2NET.Cache; - -namespace Senparc.Ncf.Core.Cache -{ - - //public interface IBaseCacheStrategy - //{ - // /// - // /// 开始一个同步锁 - // /// - // /// - // /// - // /// - // /// - // /// - // ICacheLock BeginCacheLock(string resourceName, string key, int retryCount = 0, - // TimeSpan retryDelay = new TimeSpan()); - - //} - - /// - /// 公共缓存策略接口 - /// - public interface INcfCacheStrategy : IBaseObjectCacheStrategy - { - /// - /// 整个Cache集合的Key - /// - string CacheSetKey { get; set; } - } -} +using System; +using System.Collections.Generic; +using Senparc.CO2NET.Cache; + +namespace Senparc.Ncf.Core.Cache +{ + + //public interface IBaseCacheStrategy + //{ + // /// + // /// Start a synchronization lock + // /// + // /// + // /// + // /// + // /// + // /// + // ICacheLock BeginCacheLock(string resourceName, string key, int retryCount = 0, + // TimeSpan retryDelay = new TimeSpan()); + + //} + + /// + ///Public cache policy interface + /// + public interface INcfCacheStrategy : IBaseObjectCacheStrategy + { + /// + /// Key of the entire Cache collection + /// + string CacheSetKey { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/MethodCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/MethodCache.cs index 0bb60fb7c..728c4a4cc 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/MethodCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/MethodCache.cs @@ -10,11 +10,11 @@ public static class MethodCache #region Synchronous Methods /// - /// 获取缓存 + /// Get cache /// /// /// - /// 当未命中缓存时存入并返回的结果 + /// The result stored and returned when the cache is missed /// /// public static T GetMethodCache(string cacheKey, Func func, int timeoutSeconds) where T : class @@ -28,21 +28,21 @@ public static T GetMethodCache(string cacheKey, Func func, int timeoutSeco if (!cache.CheckExisted(cacheKey)) { - cache.Set(cacheKey, func(), //每次储存的是重新执行过的最新的结果 + cache.Set(cacheKey, func(), //What is stored each time is the latest result of re-execution. TimeSpan.FromSeconds(timeoutSeconds)); } - result = cache.Get(cacheKey);//输出结果 + result = cache.Get(cacheKey);//Output results return result; } /// - /// 获取缓存,默认缓存时间为 60 分钟 + /// Get cache, the default cache time is 60 minutes /// /// /// - /// 当未命中缓存时存入并返回的结果 + /// The result stored and returned when the cache is missed /// public static T GetMethodCache(string cacheKey, Func func) where T : class { @@ -50,10 +50,10 @@ public static T GetMethodCache(string cacheKey, Func func) where T : class } /// - /// 清除缓存 + /// clear cache /// /// - public static void ClearMethodCache(string cacheKey) where T : class //虽然这边的T不需要传入,不过为了拿到CacheStrategy仍然需要提供 + public static void ClearMethodCache(string cacheKey) where T : class //Although T here does not need to be passed in, it still needs to be provided in order to get CacheStrategy { cacheKey = cacheKey.ToUpper(); @@ -69,11 +69,11 @@ public static void ClearMethodCache(string cacheKey) where T : class //虽然 #region Asynchronous Methods /// - /// 获取缓存 + /// Get cache /// /// /// - /// 当未命中缓存时存入并返回的结果 + /// The result stored and returned when the cache is missed /// /// public static async Task GetMethodCacheAsync(string cacheKey, Func> func, int timeoutSeconds) where T : class @@ -93,11 +93,11 @@ public static async Task GetMethodCacheAsync(string cacheKey, Func } /// - /// 获取缓存,默认缓存时间为 60 分钟 + /// Get cache, the default cache time is 60 minutes /// /// /// - /// 当未命中缓存时存入并返回的结果 + /// The result stored and returned when the cache is missed /// public static Task GetMethodCacheAsync(string cacheKey, Func> func) where T : class { @@ -105,7 +105,7 @@ public static Task GetMethodCacheAsync(string cacheKey, Func> func } /// - /// 清除缓存 + /// clear cache /// /// public static async Task ClearMethodCacheAsync(string cacheKey) where T : class diff --git a/src/Basic/Senparc.Ncf.Core/Cache/MobileLoginCodeCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/MobileLoginCodeCache.cs index 32987d9f2..804597830 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/MobileLoginCodeCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/MobileLoginCodeCache.cs @@ -1,90 +1,90 @@ -using Senparc.CO2NET; -using Senparc.CO2NET.Extensions; -using Senparc.Ncf.Core.DI; -using Senparc.Ncf.Core.Enums; -using System; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Core.Cache -{ - [Serializable] - public class MobileLoginCode - { - /// - /// 完整带域名格式的用户名:<用户名>@<域名> - /// - public string FullDomainUserName { get; set; } - /// - /// 用户名 - /// - public string UserName - { - get - { - if (FullDomainUserName.IsNullOrEmpty()) - { - return string.Empty; - } - return FullDomainUserName.Split('@')[0]; - } - } - - /// - /// 域名 - /// - public string Domain - { - get - { - if (FullDomainUserName.IsNullOrEmpty()) - { - return string.Empty; - } - return FullDomainUserName.Split('@')[1]; - } - } - public string Code { get; set; } - public DateTime AddTime { get; set; } - public string Url { get; set; } - public bool IsValid - { - get { return AddTime.AddMinutes(3) > DateTime.Now; } - } - } - - public interface IMobileLoginCodeCache : IBaseStringDictionaryCache - { - - } - - [AutoDIType(DILifecycleType.Singleton)] - public class MobileLoginCodeCache : BaseStringDictionaryCache, IMobileLoginCodeCache - { - public MobileLoginCodeCache() - : base("MobileLoginCodeCache", null, 120) - { - } - - public override MobileLoginCode InsertObjectToCache(string key) - { - var mobileLoginCode = new MobileLoginCode() - { - AddTime = DateTime.Now, - FullDomainUserName = "", - Code = key - }; - return this.InsertObjectToCache(key, mobileLoginCode); - } - - public override async Task InsertObjectToCacheAsync(string key) - { - var mobileLoginCode = new MobileLoginCode() - { - AddTime = DateTime.Now, - FullDomainUserName = "", - Code = key - }; - return await this.InsertObjectToCacheAsync(key, mobileLoginCode).ConfigureAwait(false); - } - } -} +using Senparc.CO2NET; +using Senparc.CO2NET.Extensions; +using Senparc.Ncf.Core.DI; +using Senparc.Ncf.Core.Enums; +using System; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Core.Cache +{ + [Serializable] + public class MobileLoginCode + { + /// + /// Complete username with domain name format: @ + /// + public string FullDomainUserName { get; set; } + /// + /// username + /// + public string UserName + { + get + { + if (FullDomainUserName.IsNullOrEmpty()) + { + return string.Empty; + } + return FullDomainUserName.Split('@')[0]; + } + } + + /// + ///domain name + /// + public string Domain + { + get + { + if (FullDomainUserName.IsNullOrEmpty()) + { + return string.Empty; + } + return FullDomainUserName.Split('@')[1]; + } + } + public string Code { get; set; } + public DateTime AddTime { get; set; } + public string Url { get; set; } + public bool IsValid + { + get { return AddTime.AddMinutes(3) > DateTime.Now; } + } + } + + public interface IMobileLoginCodeCache : IBaseStringDictionaryCache + { + + } + + [AutoDIType(DILifecycleType.Singleton)] + public class MobileLoginCodeCache : BaseStringDictionaryCache, IMobileLoginCodeCache + { + public MobileLoginCodeCache() + : base("MobileLoginCodeCache", null, 120) + { + } + + public override MobileLoginCode InsertObjectToCache(string key) + { + var mobileLoginCode = new MobileLoginCode() + { + AddTime = DateTime.Now, + FullDomainUserName = "", + Code = key + }; + return this.InsertObjectToCache(key, mobileLoginCode); + } + + public override async Task InsertObjectToCacheAsync(string key) + { + var mobileLoginCode = new MobileLoginCode() + { + AddTime = DateTime.Now, + FullDomainUserName = "", + Code = key + }; + return await this.InsertObjectToCacheAsync(key, mobileLoginCode).ConfigureAwait(false); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/DemoLoginKeyCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/DemoLoginKeyCache.cs index 75bc3d26f..33fb79c28 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/DemoLoginKeyCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/DemoLoginKeyCache.cs @@ -18,7 +18,7 @@ public DemoLoginKeyCacheData(string openId, QrCodeLoginDataType qrCodeLoginDataT } /// - /// 登录许可缓存(缓存数据:UserId) + /// Login permission cache (cache data: UserId) /// public interface IDemoLoginKeyCache : IQueueCache { @@ -39,7 +39,7 @@ public static async Task CreateAsync() { var instance = new DemoLoginKeyCache(); - // 初始化父类中的异步初始化操作 + // Initialize the asynchronous initialization operation in the parent class await instance.InitializeAsync(); return instance; @@ -47,10 +47,10 @@ public static async Task CreateAsync() private async Task InitializeAsync() { - // 调用父类的静态工厂方法进行异步初始化 + // Call the static factory method of the parent class for asynchronous initialization var queueCache = await QueueCache.CreateAsync(cacheKey, timeoutSeconds); - // 将初始化的结果赋值给当前实例 + // Assign the initialization result to the current instance this.MessageQueue = queueCache.MessageQueue; this.MessageCollection = queueCache.MessageCollection; } diff --git a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/OAuthCodeCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/OAuthCodeCache.cs index 0f4dcc4b3..516d63b75 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/OAuthCodeCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/OAuthCodeCache.cs @@ -21,7 +21,7 @@ public OAuthCodeData(int accountId, int appId, string appKey, string appSecret) } /// - /// 登录许可缓存(缓存数据:UserId) + /// Login permission cache (cache data: UserId) /// public interface IOAuthCodeCache : IQueueCache { diff --git a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/PhoneCheckCodeCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/PhoneCheckCodeCache.cs index 6eb84b9cd..5e2422fd4 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/PhoneCheckCodeCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/PhoneCheckCodeCache.cs @@ -14,7 +14,7 @@ public PhoneCheckCodeData(string phone) } ///// - ///// 手机验证码 + ///// Mobile phone verification code ///// //public interface IPhoneCheckCodeCache : IQueueCache //{ @@ -22,7 +22,7 @@ public PhoneCheckCodeData(string phone) //} /// - /// 手机验证码 + ///Mobile phone verification code /// [Serializable] public class PhoneCheckCodeCache : QueueCache/*, IPhoneCheckCodeCache*/ diff --git a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeBaseData.cs b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeBaseData.cs index c10296355..8aff6c950 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeBaseData.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeBaseData.cs @@ -1,34 +1,34 @@ -using System; - -namespace Senparc.Ncf.Core.Cache -{ - /// - /// 二维码缓存[暂未使用] - /// - [Serializable] - public class QrCodeBaseData - { - /// - /// 即SceneId的字 - /// - public string Key { get; set; } - public int SceneId { get; set; } - public string Ticket { get; set; } - public DateTime ExpireTime { get; set; } - public Guid Guid { get; set; } - /// - /// 验证通过 - /// - public bool CheckPassed { get; set; } - public string UserName { get; set; } - - public QrCodeBaseData(int sceneId, int expireSeconds, string ticket, Guid guid) - { - SceneId = sceneId; - Key = sceneId.ToString(); - Ticket = ticket; - ExpireTime = DateTime.Now.AddSeconds(expireSeconds - 5); - Guid = guid; - } - } -} +using System; + +namespace Senparc.Ncf.Core.Cache +{ + /// + /// QR code cache [not used yet] + /// + [Serializable] + public class QrCodeBaseData + { + /// + /// is the word of SceneId + /// + public string Key { get; set; } + public int SceneId { get; set; } + public string Ticket { get; set; } + public DateTime ExpireTime { get; set; } + public Guid Guid { get; set; } + /// + ///verification passed + /// + public bool CheckPassed { get; set; } + public string UserName { get; set; } + + public QrCodeBaseData(int sceneId, int expireSeconds, string ticket, Guid guid) + { + SceneId = sceneId; + Key = sceneId.ToString(); + Ticket = ticket; + ExpireTime = DateTime.Now.AddSeconds(expireSeconds - 5); + Guid = guid; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeGroupCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeGroupCache.cs index db26d0eb5..730b4c9c3 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeGroupCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeGroupCache.cs @@ -5,7 +5,7 @@ namespace Senparc.Ncf.Core.Cache.QueueCache public enum QrCodeGroupDataType { /// - /// 门派 + /// sect /// Group, } @@ -14,7 +14,7 @@ public enum QrCodeGroupDataType public class QrCodeGroupData { /// - /// 即SceneId的字符串 + /// is the string of SceneId /// public string Key { get; set; } /// @@ -41,7 +41,7 @@ public QrCodeGroupData(int sceneId, int expireSeconds, string ticket, } /// - /// 门派许可缓存(缓存数据:GroupId) + /// Martial arts license cache (cache data: GroupId) /// public interface IQrCodeGroupCache : IQueueCache { diff --git a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeLoginCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeLoginCache.cs index fb7b0f5d5..d32e516ee 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeLoginCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeLoginCache.cs @@ -5,7 +5,7 @@ namespace Senparc.Ncf.Core.Cache public enum QrCodeLoginDataType { /// - /// 登陆 + ///login /// Login, /// @@ -22,7 +22,7 @@ public enum QrCodeLoginDataType public class QrCodeLoginData { /// - /// 即SceneId的字符串 + /// is the string of SceneId /// public string Key { get; set; } public int SceneId { get; set; } @@ -30,7 +30,7 @@ public class QrCodeLoginData public DateTime ExpireTime { get; set; } public Guid LoginGuid { get; set; } /// - /// 验证通过 + ///verification passed /// public bool CheckPassed { get; set; } public string UserName { get; set; } @@ -48,7 +48,7 @@ public QrCodeLoginData(int sceneId, int expireSeconds, string ticket, Guid login } /// - /// 登录许可缓存(缓存数据:UserId) + /// Login permission cache (cache data: UserId) /// public interface IQrCodeLoginCache : IQueueCache { diff --git a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeRegCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeRegCache.cs index 9ca3ff4a5..8ddbef727 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeRegCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QrCodeRegCache.cs @@ -5,7 +5,7 @@ namespace Senparc.Ncf.Core.Cache public enum QrCodeRegDataType { /// - /// 注册 + /// register /// Reg, } @@ -14,7 +14,7 @@ public enum QrCodeRegDataType public class QrCodeRegData { /// - /// 即SceneId的字符串 + /// is the string of SceneId /// public string Key { get; set; } public int SceneId { get; set; } @@ -37,7 +37,7 @@ public QrCodeRegData(int sceneId, int expireSeconds, string ticket, Guid regGuid } /// - /// 登录许可缓存(缓存数据:UserId) + /// Login permission cache (cache data: UserId) /// public interface IQrCodeRegCache : IQueueCache { diff --git a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QueueCache.cs b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QueueCache.cs index 86ff66ce0..d7210cb3c 100644 --- a/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QueueCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Cache/QueueCache/QueueCache.cs @@ -11,10 +11,10 @@ public interface IQueueCache Dictionary> MessageCollection { get; set; } string CreateKey(); /// - /// 插入缓存 + ///insert cache /// /// - /// 如果为null将自动生成16位Guid + /// If it is null, a 16-bit Guid will be automatically generated /// string Insert(T obj, string key); QueueCacheData Get(string key, bool removeDataWhenExist = true); @@ -33,7 +33,7 @@ public class QueueCache : IQueueCache /// /// /// - /// 小于等于0则没有过期时间 + /// If it is less than or equal to 0, there will be no expiration time protected QueueCache(string cacheKey, int timeoutSeconds) { _cacheKey = cacheKey; @@ -57,11 +57,11 @@ public static async Task> CreateAsync(string cacheKey, int timeout } /// - /// 获取MessageContext,如果不存在,返回null - /// 这个方法的更重要意义在于操作TM队列,及时移除过期信息,并将最新活动的对象移到尾部 + /// Get MessageContext, if it does not exist, return null + /// The more important significance of this method is to operate the TM queue, remove expired information in time, and move the latest active objects to the end. /// - /// 用户名(OpenId) - /// 是否清除key + /// Username (OpenId) + /// Whether to clear key /// private QueueCacheData GetMessageContext(string key, bool removeDataWhenExist = true) { @@ -72,8 +72,8 @@ private QueueCacheData GetMessageContext(string key, bool removeDataWhenExist var timeSpan = DateTime.Now - firstMessageContext.LastActiveTime; if (removeDataWhenExist && _timeoutSeconds >= 0 && timeSpan.TotalSeconds >= _timeoutSeconds) { - MessageQueue.RemoveAt(0);//从队列中移除过期对象 - MessageCollection.Remove(firstMessageContext.Key);//从集合中删除过期对象 + MessageQueue.RemoveAt(0);//Remove expired objects from queue + MessageCollection.Remove(firstMessageContext.Key);//Remove expired objects from collection } else { @@ -82,9 +82,9 @@ private QueueCacheData GetMessageContext(string key, bool removeDataWhenExist } /* - * 全局只有在这里用到MessageCollection.ContainsKey - * 充分分离MessageCollection内部操作, - * 为以后变化或扩展MessageCollection留余地 + *Global MessageCollection.ContainsKey is only used here + * Fully separate the internal operations of MessageCollection, + * Leave room for future changes or extensions to MessageCollection */ if (!MessageCollection.ContainsKey(key)) { @@ -100,11 +100,11 @@ private QueueCacheData GetMessageContext_CreateIfNotExists(string key) if (messageContext == null) { - //全局只在这一个地方使用MessageCollection[Key]写入 + //Globally only use MessageCollection[Key] to write in this one place MessageCollection[key] = new QueueCacheData(key); messageContext = GetMessageContext(key); - //插入列队 - MessageQueue.Add(messageContext); //最新的排到末尾 + //insert queue + MessageQueue.Add(messageContext); //Newest at the end } return messageContext; } @@ -123,8 +123,8 @@ public virtual string Insert(T obj, string key) // var timeSpan = DateTime.Now - firstMessageContext.LastActiveTime; // if (timeSpan.TotalSeconds >= _timeoutSeconds) // { - // MessageQueue.RemoveAt(0);//从队列中移除过期对象 - // MessageCollection.Remove(firstMessageContext.Key);//从集合中删除过期对象 + // MessageQueue.RemoveAt(0);//Remove expired objects from the queue + // MessageCollection.Remove(firstMessageContext.Key);//Remove expired objects from the collection // } // else // { @@ -143,12 +143,12 @@ public virtual string Insert(T obj, string key) var messageContextInQueue = MessageQueue.IndexOf(messageContext); if (MessageQueue.Count > 1 && messageContextInQueue != MessageQueue.Count - 1) { - //如果不是新建的对象,把当前对象移到队列尾部(新对象已经在底部) - MessageQueue.RemoveAt(messageContextInQueue); //移除当前对象 - MessageQueue.Add(messageContext); //插入到末尾 + //If it is not a new object, move the current object to the end of the queue (the new object is already at the bottom) + MessageQueue.RemoveAt(messageContextInQueue); //Remove current object + MessageQueue.Add(messageContext); //insert at end } - messageContext.LastActiveTime = DateTime.Now;//记录请求时间 + messageContext.LastActiveTime = DateTime.Now;//Log request time return key; } diff --git a/src/Basic/Senparc.Ncf.Core/Config/AIPluginHub.cs b/src/Basic/Senparc.Ncf.Core/Config/AIPluginHub.cs index 077da37ea..2f85a160c 100644 --- a/src/Basic/Senparc.Ncf.Core/Config/AIPluginHub.cs +++ b/src/Basic/Senparc.Ncf.Core/Config/AIPluginHub.cs @@ -10,7 +10,7 @@ namespace Senparc.Ncf.Core { /// - /// 注册 AI 插件 + ///Register AI plugin /// public class AIPluginHub { @@ -19,7 +19,7 @@ public class AIPluginHub AIPluginHub() { } /// - /// AIPluginHub 的全局单例 + /// Global singleton of AIPluginHub /// public static AIPluginHub Instance { @@ -43,7 +43,7 @@ static Nested() { } /// - /// 添加 Plugin 类型 + /// Add Plugin type /// /// @@ -58,7 +58,7 @@ public void Add(Type pluginType) } /// - /// 获取 Plugin 类型 + /// Get Plugin type /// /// /// @@ -91,7 +91,7 @@ public void Add(Type pluginType) } /// - /// 获取所有 Plugin 类型 + /// Get all Plugin types /// /// public List GetAllPluginNames() diff --git a/src/Basic/Senparc.Ncf.Core/Config/SiteConfig.Core.cs b/src/Basic/Senparc.Ncf.Core/Config/SiteConfig.Core.cs index 16af446e5..1807ccb71 100644 --- a/src/Basic/Senparc.Ncf.Core/Config/SiteConfig.Core.cs +++ b/src/Basic/Senparc.Ncf.Core/Config/SiteConfig.Core.cs @@ -1,200 +1,199 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Senparc.CO2NET; -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Utility; -using System.Collections.Generic; -using System.Threading; -using System; -using Senparc.Ncf.Core.Utility; -using System.IO; - -namespace Senparc.Ncf.Core.Config -{ - public static partial class SiteConfig - { - /// - /// Website physical path - /// - public static string ApplicationPath { get; set; } - public static string WebRootPath { get; set; } - - /// - /// Settings - /// Must be injected when system starts - /// - public static SenparcCoreSetting SenparcCoreSetting { get; set; } = new SenparcCoreSetting(); - //{ - // get - // { - // IServiceProvider serviceProvider = SenparcDI.GlobalServiceCollection.BuildServiceProvider(); - // var scs = serviceProvider.GetService>(); - // return scs.Value; - // } - //} - - /// - /// Whether in Debug mode (manually defined) - /// - public static bool IsDebug => SenparcCoreSetting.IsDebug; - - /// - /// Whether this is a test site - /// - public static bool IsTestSite => SenparcCoreSetting.IsTestSite; - - public static Dictionary _memcachedAddressesDic; - public const string WEIXIN_FILTER_IGNORE = "senparcnofilter1"; - public const string WEIXIN_OFFICIAL_AVATAR_KEY = "WXoDOkC8A"; //Take first 8 chars - public const string WEIXIN_OFFICIAL_QR_CODE_KEY = "WX631IC8A"; //Take first 8 chars - public const string WEIXIN_APP_TOKEN_KEY = "WEIXIN_APP_TOKEN_KEY_FOR_NCF"; //WeChat APP Token encryption - public const long MIN_WEIXINUSERINFO_ID = 10000000000000; //Minimum custom WeixinUserInfo Id - public const decimal PROJECTDMANDDEPOSIT = 1000; //Default project deposit - public const string CERT_P12_ADDRESS = @"E:\";//Storage path for WeChat Pay certificate - - /* UID 建议格式: NCF公司ID-项目类型-保留字段(可随机)-内部类别1-内部类别2*/ - public const string SYSTEM_XNCF_TANENT_UID = "00000000-0000-0000-0000-000000000001"; - public const string SYSTEM_XNCF_MODULE_SYSTEM_CORE_UID = "00000000-0000-0000-0001-000000000001"; - public const string SYSTEM_XNCF_MODULE_SERVICE_MANAGER_UID = "00000000-0000-0000-0001-000000000002"; - public const string SYSTEM_XNCF_MODULE_SYSTEM_PERMISSION_UID = "00000000-0000-0000-0001-000000000003"; - public const string SYSTEM_XNCF_MODULE_XNCF_MODULE_MANAGER_UID = "00000000-0000-0000-0001-000000000004"; - public const string SYSTEM_XNCF_MODULE_MENU_UID = "00000000-0000-0000-0001-000000000005"; - public const string SYSTEM_XNCF_BASE_AREAS = "00000000-0000-0000-0001-000000000006"; - - - public const string SYSTEM_XNCF_MODULE_AREAS_ADMIN_UID = "00000000-0000-0001-0001-000000000001"; - public const string SYSTEM_XNCF_MODULE_ACCOUNTS_UID = "00000000-0000-0001-0001-000000000002"; - - public const string TENANT_DEFAULT_NAME = "DEFAULT";//Default multi-tenant TenantName (not treated as any special tenant) - - /// - /// Developer income ratio - /// - public static readonly long DeveloperIncomRate = (long)0.5; - - /// - /// Cache type - /// - public static CacheType CacheType - { - get => SenparcCoreSetting.CacheType; - set => SenparcCoreSetting.CacheType = value; - } - - //以下参数放到SiteConfig.cs中 - //public readonly static string VERSION = "1.3.2"; - //public const string GLOBAL_PASSWORD_SALT = "senparc@20131113"; - - public const string VERSION = "0.0.1"; - public static string SenparcConfigDirctory = "~/App_Data/Database/"; - public const string AntiForgeryTokenSalt = "SOUIDEA__SENPARC"; - public const string WEIXIN_USER_AVATAR_KEY = "SENPARC_"; //Take first 8 chars - public const string DomainName = "https://ncf.senparc.com"; - public const string DefaultTemplate = "default"; - public const int SMSSENDWAITSECONDS = 60; //Phone verification duration - public const string DEFAULT_AVATAR = "/Content/Images/userinfonopic.png"; //Default avatar - - public const string DEFAULT_MEMCACHED_ADDRESS_1 = "192.168.184.91"; - public const int DEFAULT_MEMCACHED_PORT_1 = 11210; - - /// - /// WBS format - /// - public static readonly string WBSFormat = "000"; - - /// - /// User online inactivity timeout (minutes) - /// - public static readonly int UserOnlineTimeoutMinutes = 10; - /// - /// Max login attempts without verification code - /// - public static readonly int TryLoginTimes = 1; - /// - /// Max user login attempts without verification code - /// - public static readonly int TryUserLoginTimes = 3; - /// - /// Maximum number of database backup files - /// - public static readonly int MaxBackupDatabaseCount = 200; - /// - /// Whether running unit test - /// - public static readonly bool IsUnitTest = false; - ///// - ///// AI 插件注册 - ///// - //public static readonly AIPlugins AIPlugins = new AIPlugins(); - - private static bool _isInstalling = false; - - /// - /// Whether installation is in progress; if true, installation-check exception is not thrown - /// - public static bool IsInstalling - { - get - { - return _isInstalling || !CheckInstallFinishedFileExisted(); - } - set - { - _isInstalling = false; - } - } - - /// - /// Check whether installation-finished status file exists - /// - /// - public static bool CheckInstallFinishedFileExisted() - { - var fileExist = File.Exists(Server.GetMapPath("~/App_Data/install-finished.txt")); - return fileExist; - } - - /// - /// Manually set installation status to finished - /// - public static async void SetInstallFinished() - { - _isInstalling = false; - var filePath = Server.GetMapPath("~/App_Data/install-finished.txt"); - if (!CheckInstallFinishedFileExisted()) - { - var text = @$"After this file is successfully installed by the system, it should only be modified or removed when you need to reinstall the entire system! Please operate with caution!! - -This file is created after successful system installation. Modify or remove it only when you need to reinstall the whole system. Please operate with caution!!"; - await File.WriteAllTextAsync(filePath, text); - } - } - - public static int PageViewCount { get; set; } //Frontend page views after site startup - - - /// - /// Whether database module should be loaded - /// - public static bool DatabaseXncfLoaded { get; set; } - - /// - /// System state - /// - public static NcfCoreState NcfCoreState { get; } = NcfCoreState.Instance; - - //Async threads - public static Dictionary AsynThread = new Dictionary(); //Background running threads - - /// - /// Cookie login scheme for Admin - /// - public readonly static string NcfAdminAuthorizeScheme = "NcfAdminAuthorizeScheme"; - /// - /// Cookie login scheme for User - /// - public readonly static string NcfUserAuthorizeScheme = "NcfUserAuthorizeScheme"; - } +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Senparc.CO2NET; +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Utility; +using System.Collections.Generic; +using System.Threading; +using System; +using Senparc.Ncf.Core.Utility; +using System.IO; + +namespace Senparc.Ncf.Core.Config +{ + public static partial class SiteConfig + { + /// + /// Website physical path + /// + public static string ApplicationPath { get; set; } + public static string WebRootPath { get; set; } + + /// + /// Settings + /// Must be injected when system starts + /// + public static SenparcCoreSetting SenparcCoreSetting { get; set; } = new SenparcCoreSetting(); + //{ + // get + // { + // IServiceProvider serviceProvider = SenparcDI.GlobalServiceCollection.BuildServiceProvider(); + // var scs = serviceProvider.GetService>(); + // return scs.Value; + // } + //} + + /// + /// Whether in Debug mode (manually defined) + /// + public static bool IsDebug => SenparcCoreSetting.IsDebug; + + /// + /// Whether this is a test site + /// + public static bool IsTestSite => SenparcCoreSetting.IsTestSite; + + public static Dictionary _memcachedAddressesDic; + public const string WEIXIN_FILTER_IGNORE = "senparcnofilter1"; + public const string WEIXIN_OFFICIAL_AVATAR_KEY = "WXoDOkC8A"; //Take first 8 chars + public const string WEIXIN_OFFICIAL_QR_CODE_KEY = "WX631IC8A"; //Take first 8 chars + public const string WEIXIN_APP_TOKEN_KEY = "WEIXIN_APP_TOKEN_KEY_FOR_NCF"; //WeChat APP Token encryption + public const long MIN_WEIXINUSERINFO_ID = 10000000000000; //Minimum custom WeixinUserInfo Id + public const decimal PROJECTDMANDDEPOSIT = 1000; //Default project deposit + public const string CERT_P12_ADDRESS = @"E:\";//Storage path for WeChat Pay certificate + + /* UID Recommended format: NCF company ID-project type-reserved field (can be random)-internal category 1-internal category 2*/ + public const string SYSTEM_XNCF_TANENT_UID = "00000000-0000-0000-0000-000000000001"; + public const string SYSTEM_XNCF_MODULE_SYSTEM_CORE_UID = "00000000-0000-0000-0001-000000000001"; + public const string SYSTEM_XNCF_MODULE_SERVICE_MANAGER_UID = "00000000-0000-0000-0001-000000000002"; + public const string SYSTEM_XNCF_MODULE_SYSTEM_PERMISSION_UID = "00000000-0000-0000-0001-000000000003"; + public const string SYSTEM_XNCF_MODULE_XNCF_MODULE_MANAGER_UID = "00000000-0000-0000-0001-000000000004"; + public const string SYSTEM_XNCF_MODULE_MENU_UID = "00000000-0000-0000-0001-000000000005"; + public const string SYSTEM_XNCF_BASE_AREAS = "00000000-0000-0000-0001-000000000006"; + + + public const string SYSTEM_XNCF_MODULE_AREAS_ADMIN_UID = "00000000-0000-0001-0001-000000000001"; + public const string SYSTEM_XNCF_MODULE_ACCOUNTS_UID = "00000000-0000-0001-0001-000000000002"; + + public const string TENANT_DEFAULT_NAME = "DEFAULT";//Default multi-tenant TenantName (not treated as any special tenant) + + /// + /// Developer income ratio + /// + public static readonly long DeveloperIncomRate = (long)0.5; + + /// + /// Cache type + /// + public static CacheType CacheType + { + get => SenparcCoreSetting.CacheType; + set => SenparcCoreSetting.CacheType = value; + } + + //Put the following parameters into SiteConfig.csmiddle//public readonly static string VERSION = "1.3.2"; + //public const string GLOBAL_PASSWORD_SALT = "senparc@20131113"; + + public const string VERSION = "0.0.1"; + public static string SenparcConfigDirctory = "~/App_Data/Database/"; + public const string AntiForgeryTokenSalt = "SOUIDEA__SENPARC"; + public const string WEIXIN_USER_AVATAR_KEY = "SENPARC_"; //Take first 8 chars + public const string DomainName = "https://ncf.senparc.com"; + public const string DefaultTemplate = "default"; + public const int SMSSENDWAITSECONDS = 60; //Phone verification duration + public const string DEFAULT_AVATAR = "/Content/Images/userinfonopic.png"; //Default avatar + + public const string DEFAULT_MEMCACHED_ADDRESS_1 = "192.168.184.91"; + public const int DEFAULT_MEMCACHED_PORT_1 = 11210; + + /// + /// WBS format + /// + public static readonly string WBSFormat = "000"; + + /// + /// User online inactivity timeout (minutes) + /// + public static readonly int UserOnlineTimeoutMinutes = 10; + /// + /// Max login attempts without verification code + /// + public static readonly int TryLoginTimes = 1; + /// + /// Max user login attempts without verification code + /// + public static readonly int TryUserLoginTimes = 3; + /// + /// Maximum number of database backup files + /// + public static readonly int MaxBackupDatabaseCount = 200; + /// + /// Whether running unit test + /// + public static readonly bool IsUnitTest = false; + ///// + ///// AI plug-in registration + ///// + //public static readonly AIPlugins AIPlugins = new AIPlugins(); + + private static bool _isInstalling = false; + + /// + /// Whether installation is in progress; if true, installation-check exception is not thrown + /// + public static bool IsInstalling + { + get + { + return _isInstalling || !CheckInstallFinishedFileExisted(); + } + set + { + _isInstalling = false; + } + } + + /// + /// Check whether installation-finished status file exists + /// + /// + public static bool CheckInstallFinishedFileExisted() + { + var fileExist = File.Exists(Server.GetMapPath("~/App_Data/install-finished.txt")); + return fileExist; + } + + /// + /// Manually set installation status to finished + /// + public static async void SetInstallFinished() + { + _isInstalling = false; + var filePath = Server.GetMapPath("~/App_Data/install-finished.txt"); + if (!CheckInstallFinishedFileExisted()) + { + var text = @$"After this file is successfully installed by the system, it should only be modified or removed when you need to reinstall the entire system! Please operate with caution!! + +This file is created after successful system installation. Modify or remove it only when you need to reinstall the whole system. Please operate with caution!!"; + await File.WriteAllTextAsync(filePath, text); + } + } + + public static int PageViewCount { get; set; } //Frontend page views after site startup + + + /// + /// Whether database module should be loaded + /// + public static bool DatabaseXncfLoaded { get; set; } + + /// + /// System state + /// + public static NcfCoreState NcfCoreState { get; } = NcfCoreState.Instance; + + //Async threads + public static Dictionary AsynThread = new Dictionary(); //Background running threads + + /// + /// Cookie login scheme for Admin + /// + public readonly static string NcfAdminAuthorizeScheme = "NcfAdminAuthorizeScheme"; + /// + /// Cookie login scheme for User + /// + public readonly static string NcfUserAuthorizeScheme = "NcfUserAuthorizeScheme"; + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Core/Exceptions/NcfDatabaseException.cs b/src/Basic/Senparc.Ncf.Core/Exceptions/NcfDatabaseException.cs index a3d619469..0f7a4b21f 100644 --- a/src/Basic/Senparc.Ncf.Core/Exceptions/NcfDatabaseException.cs +++ b/src/Basic/Senparc.Ncf.Core/Exceptions/NcfDatabaseException.cs @@ -1,32 +1,32 @@ -using Senparc.CO2NET.Trace; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Exceptions -{ - /// - /// NCF 数据库异常 - /// - public class NcfDatabaseException : NcfExceptionBase - { - /// - /// 构造函数 - /// - /// - /// DatabaseConfiguration 类型 - /// DbContext 类型,如:SenparcEntities - /// - public NcfDatabaseException(string message, Type typeOfDatabaseConfiguration, Type typeOfDbContext = null, Exception inner = null) - : base(message, inner, true) - { - message += @$" -DatabaseConfiguration 类型:{(typeOfDatabaseConfiguration == null ? "未提供" : typeOfDatabaseConfiguration.Name)} -DbContext 类型:{(typeOfDbContext == null ? "未提供" : typeOfDbContext.Name)} -"; - - SenparcTrace.BaseExceptionLog(this); - } - - } -} +using Senparc.CO2NET.Trace; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Exceptions +{ + /// + /// NCF database exception + /// + public class NcfDatabaseException : NcfExceptionBase + { + /// + ///Constructor + /// + /// + /// DatabaseConfiguration type + /// DbContext type, such as: SenparcEntities + /// + public NcfDatabaseException(string message, Type typeOfDatabaseConfiguration, Type typeOfDbContext = null, Exception inner = null) + : base(message, inner, true) + { + message += @$" +DatabaseConfiguration 类型:{(typeOfDatabaseConfiguration == null ? "未提供" : typeOfDatabaseConfiguration.Name)} +DbContext 类型:{(typeOfDbContext == null ? "未提供" : typeOfDbContext.Name)} +"; + + SenparcTrace.BaseExceptionLog(this); + } + + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Exceptions/NcfUninstallException.cs b/src/Basic/Senparc.Ncf.Core/Exceptions/NcfUninstallException.cs index 32f05ac29..0a9055906 100644 --- a/src/Basic/Senparc.Ncf.Core/Exceptions/NcfUninstallException.cs +++ b/src/Basic/Senparc.Ncf.Core/Exceptions/NcfUninstallException.cs @@ -1,21 +1,21 @@ -using Senparc.Ncf.Core.Exceptions; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core -{ - /// - /// NCF 未安装 - /// - public class NcfUninstallException : NcfExceptionBase - { - public NcfUninstallException(string message, bool logged = false) : this(message, null, logged) - { - } - - public NcfUninstallException(string message, Exception inner = null, bool logged = false) : base(message, inner, logged) - { - } - } -} +using Senparc.Ncf.Core.Exceptions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core +{ + /// + ///NCF not installed + /// + public class NcfUninstallException : NcfExceptionBase + { + public NcfUninstallException(string message, bool logged = false) : this(message, null, logged) + { + } + + public NcfUninstallException(string message, Exception inner = null, bool logged = false) : base(message, inner, logged) + { + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Extensions/AuthorizeAttributeExtensions.cs b/src/Basic/Senparc.Ncf.Core/Extensions/AuthorizeAttributeExtensions.cs index 8fa153a39..cc3888554 100644 --- a/src/Basic/Senparc.Ncf.Core/Extensions/AuthorizeAttributeExtensions.cs +++ b/src/Basic/Senparc.Ncf.Core/Extensions/AuthorizeAttributeExtensions.cs @@ -1,20 +1,20 @@ -using Microsoft.AspNetCore.Authorization; - -namespace Senparc.Ncf.Core.Extensions -{ - - //TODO: 独立到各个模块中 - - public class UserAuthorizeAttribute : AuthorizeAttribute - { - public const string AuthenticationScheme = "NcfUserAuthorizeScheme"; - public UserAuthorizeAttribute(string policy) : this() - { - this.Policy = policy; - } - public UserAuthorizeAttribute() - { - base.AuthenticationSchemes = AuthenticationScheme; - } - } -} +using Microsoft.AspNetCore.Authorization; + +namespace Senparc.Ncf.Core.Extensions +{ + + //TODO: separate into each module + + public class UserAuthorizeAttribute : AuthorizeAttribute + { + public const string AuthenticationScheme = "NcfUserAuthorizeScheme"; + public UserAuthorizeAttribute(string policy) : this() + { + this.Policy = policy; + } + public UserAuthorizeAttribute() + { + base.AuthenticationSchemes = AuthenticationScheme; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Extensions/ObjectExtensions.cs b/src/Basic/Senparc.Ncf.Core/Extensions/ObjectExtensions.cs index 8171702a4..d963d5ae2 100644 --- a/src/Basic/Senparc.Ncf.Core/Extensions/ObjectExtensions.cs +++ b/src/Basic/Senparc.Ncf.Core/Extensions/ObjectExtensions.cs @@ -1,15 +1,15 @@ -namespace Senparc.Ncf.Core.Extensions -{ - public static class ObjectExtensions - { - /// - /// 判断对象是否为null - /// - /// - /// - public static bool IsNull(object obj) - { - return obj == null; - } - } +namespace Senparc.Ncf.Core.Extensions +{ + public static class ObjectExtensions + { + /// + /// Determine whether the object is null + /// + /// + /// + public static bool IsNull(object obj) + { + return obj == null; + } + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/AdminUserInfoDto.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/AdminUserInfoDto.cs index c4017484e..3aa9159a4 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/AdminUserInfoDto.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/AdminUserInfoDto.cs @@ -1,32 +1,32 @@ -using Senparc.Ncf.Core.Models.DataBaseModel; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Text; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// AdminUserInfo 创建和更新 - /// - public class AdminUserInfoDto : DtoBase - { - public int Id { get; set; } - public string UserName { get; set; } - public string Password { get; set; } - - public string Note { get; set; } - - public string RealName { get; set; } - - public string Phone { get; set; } - } - - public class CreateOrUpdate_AdminUserInfoDto : AdminUserInfoDto - { - [Required] - [StringLength(20)] - new public string UserName { get; set; } - new public string Password { get; set; } - } -} +using Senparc.Ncf.Core.Models.DataBaseModel; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace Senparc.Ncf.Core.Models +{ + /// + ///AdminUserInfo Create and Update + /// + public class AdminUserInfoDto : DtoBase + { + public int Id { get; set; } + public string UserName { get; set; } + public string Password { get; set; } + + public string Note { get; set; } + + public string RealName { get; set; } + + public string Phone { get; set; } + } + + public class CreateOrUpdate_AdminUserInfoDto : AdminUserInfoDto + { + [Required] + [StringLength(20)] + new public string UserName { get; set; } + new public string Password { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/Base/DtoBase.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/Base/DtoBase.cs index df70191a1..3cc7497d7 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/Base/DtoBase.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/Base/DtoBase.cs @@ -1,63 +1,63 @@ -using System; -using System.ComponentModel.DataAnnotations; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// 数据库 Dto 基类 - /// - public class DtoBase : DtoBase - where T : struct - { - public T Id { get; set; } - } - - /// - /// 数据库 Dto 基类 - /// - public class DtoBase : DtoBase - where T : EntityBase - { - public TID Id { get; set; } - } - - - /// - /// 数据库 Dto 基类 - /// - public class DtoBase : IDtoBase - { - /// - /// 是否软删除 - /// - public bool Flag { get; set; } - - /// - /// - /// - [MaxLength(150)] - public string AdminRemark { get; set; } - - /// - /// - /// - [MaxLength(150)] - public string Remark { get; set; } - - /// - /// 添加时间 - /// - public DateTime AddTime { get; set; } - /// - /// 上次更新时间 - /// - public DateTime LastUpdateTime { get; set; } - - /// - /// 租户 ID - /// 如果为-1,则本系统不启用多租户 - /// 如果为0,则为系统公共数据(特殊情况使用) - /// - public int TenantId { get; set; } - } -} +using System; +using System.ComponentModel.DataAnnotations; + +namespace Senparc.Ncf.Core.Models +{ + /// + /// Database Dto base class + /// + public class DtoBase : DtoBase + where T : struct + { + public T Id { get; set; } + } + + /// + /// Database Dto base class + /// + public class DtoBase : DtoBase + where T : EntityBase + { + public TID Id { get; set; } + } + + + /// + /// Database Dto base class + /// + public class DtoBase : IDtoBase + { + /// + /// Whether to soft delete + /// + public bool Flag { get; set; } + + /// + /// + /// + [MaxLength(150)] + public string AdminRemark { get; set; } + + /// + /// + /// + [MaxLength(150)] + public string Remark { get; set; } + + /// + ///add time + /// + public DateTime AddTime { get; set; } + /// + /// last updated time + /// + public DateTime LastUpdateTime { get; set; } + + /// + ///Tenant ID + /// If it is -1, multi-tenancy is not enabled in this system + /// If it is 0, it is system public data (used in special circumstances) + /// + public int TenantId { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/Base/IDtoBase.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/Base/IDtoBase.cs index f02176546..a11a06dad 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/Base/IDtoBase.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/Base/IDtoBase.cs @@ -1,55 +1,55 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Text; -using Microsoft.AspNetCore.Routing.Tree; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// 所有 DTO 接口或类的基类 - /// - public interface IDtoBase : IDtoBase - { - T Id { get; set; } - } - - /// - /// 所有 DTO 接口或类的基类 - /// - public interface IDtoBase - { - /// - /// 是否软删除 - /// - bool Flag { get; set; } - - /// - /// - /// - [MaxLength(150)] - string AdminRemark { get; set; } - - /// - /// - /// - [MaxLength(150)] - string Remark { get; set; } - - /// - /// 添加时间 - /// - DateTime AddTime { get; set; } - /// - /// 上次更新时间 - /// - DateTime LastUpdateTime { get; set; } - - /// - /// 租户 ID - /// 如果为-1,则本系统不启用多租户 - /// 如果为0,则为系统公共数据(特殊情况使用) - /// - public int TenantId { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; +using Microsoft.AspNetCore.Routing.Tree; + +namespace Senparc.Ncf.Core.Models +{ + /// + /// Base class for all DTO interfaces or classes + /// + public interface IDtoBase : IDtoBase + { + T Id { get; set; } + } + + /// + /// Base class for all DTO interfaces or classes + /// + public interface IDtoBase + { + /// + /// Whether to soft delete + /// + bool Flag { get; set; } + + /// + /// + /// + [MaxLength(150)] + string AdminRemark { get; set; } + + /// + /// + /// + [MaxLength(150)] + string Remark { get; set; } + + /// + ///add time + /// + DateTime AddTime { get; set; } + /// + /// last updated time + /// + DateTime LastUpdateTime { get; set; } + + /// + ///Tenant ID + /// If it is -1, multi-tenancy is not enabled in this system + /// If it is 0, it is system public data (used in special circumstances) + /// + public int TenantId { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysButtonDto.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysButtonDto.cs index 6d859cbe0..f79f1cd7f 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysButtonDto.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysButtonDto.cs @@ -1,40 +1,40 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Text; - -namespace Senparc.Ncf.Core.Models.DataBaseModel -{ - public class SysButtonDto : DtoBase - { - - //public bool IsDeleted { get; set; } - - public string Id { get; set; } - - /// - /// 菜单id - /// - [MaxLength(50)] - public string MenuId { get; set; } - - /// - /// 操作名称 - /// - [MaxLength(50)] - //[Required] - public string ButtonName { get; set; } - - /// - /// 操作标识 - /// - [MaxLength(50)] - public string OpearMark { get; set; } - - /// - /// 按钮对应的请求地址 - /// - [MaxLength(350)] - public string Url { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace Senparc.Ncf.Core.Models.DataBaseModel +{ + public class SysButtonDto : DtoBase + { + + //public bool IsDeleted { get; set; } + + public string Id { get; set; } + + /// + /// menu id + /// + [MaxLength(50)] + public string MenuId { get; set; } + + /// + /// operation name + /// + [MaxLength(50)] + //[Required] + public string ButtonName { get; set; } + + /// + /// operation identifier + /// + [MaxLength(50)] + public string OpearMark { get; set; } + + /// + ///Request address corresponding to the button + /// + [MaxLength(350)] + public string Url { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysMenuDto.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysMenuDto.cs index b3fada627..790d22420 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysMenuDto.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysMenuDto.cs @@ -1,90 +1,90 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Text; - -namespace Senparc.Ncf.Core.Models.DataBaseModel -{ - public class SysMenuDto : DtoBase - { - - /// - /// 是否是菜单 - /// - [Obsolete("使用MenuType字段代替")] - public bool IsMenu { get; set; } - - [MaxLength(50)] - public string Id { get; set; } - - [MaxLength(150)] - [Required] - public string MenuName { get; set; } - - /// - /// 父菜单 - /// - [MaxLength(50)] - public string ParentId { get; set; } - - [MaxLength(350)] - public string Url { get; set; } - - /// - /// 图标 - /// - [MaxLength(50)] - public string Icon { get; set; } - - /// - /// - /// - public int Sort { get; set; } - - /// - /// 是否可见 - /// - public bool Visible { get; set; } - - public string ResourceCode { get; set; } - - public bool IsLocked { get; set; } - - public SysMenuDto() { } - - public MenuType MenuType { get; set; } - - - public SysMenuDto(bool isMenu, string id, string menuName, string parentId, string url, string icon, int sort, bool visible, string resourceCode, MenuType menuType = MenuType.菜单) - { - IsMenu = isMenu; - Id = id; - MenuName = menuName; - ParentId = parentId; - Url = url; - Icon = icon; - Sort = sort; - Visible = visible; - ResourceCode = resourceCode; - MenuType = menuType; - } - - } - - /// - /// 菜单树 - /// - public class SysMenuTreeItemDto - { - public string MenuName { get; set; } - - public string Id { get; set; } - - public bool IsMenu { get; set; } - - public IList Children { get; set; } - public string Icon { get; set; } - - public string Url { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace Senparc.Ncf.Core.Models.DataBaseModel +{ + public class SysMenuDto : DtoBase + { + + /// + /// Whether it is a menu + /// + [Obsolete("使用MenuType字段代替")] + public bool IsMenu { get; set; } + + [MaxLength(50)] + public string Id { get; set; } + + [MaxLength(150)] + [Required] + public string MenuName { get; set; } + + /// + ///parent menu + /// + [MaxLength(50)] + public string ParentId { get; set; } + + [MaxLength(350)] + public string Url { get; set; } + + /// + /// icon + /// + [MaxLength(50)] + public string Icon { get; set; } + + /// + /// + /// + public int Sort { get; set; } + + /// + /// is visible + /// + public bool Visible { get; set; } + + public string ResourceCode { get; set; } + + public bool IsLocked { get; set; } + + public SysMenuDto() { } + + public MenuType MenuType { get; set; } + + + public SysMenuDto(bool isMenu, string id, string menuName, string parentId, string url, string icon, int sort, bool visible, string resourceCode, MenuType menuType = MenuType.菜单) + { + IsMenu = isMenu; + Id = id; + MenuName = menuName; + ParentId = parentId; + Url = url; + Icon = icon; + Sort = sort; + Visible = visible; + ResourceCode = resourceCode; + MenuType = menuType; + } + + } + + /// + ///menu tree + /// + public class SysMenuTreeItemDto + { + public string MenuName { get; set; } + + public string Id { get; set; } + + public bool IsMenu { get; set; } + + public IList Children { get; set; } + public string Icon { get; set; } + + public string Url { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysPermissionDto.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysPermissionDto.cs index d5ca33be3..a8931db6c 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysPermissionDto.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysPermissionDto.cs @@ -1,25 +1,25 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Models.DataBaseModel -{ - public class SysPermissionDto : DtoBase - { - public string RoleId { get; set; } - - public string PermissionId { get; set; } - - public bool IsMenu { get; set; } - - /// - /// 角色代码 - /// - public string RoleCode { get; set; } - - /// - /// 资源(按钮)代码 - /// - public string ResourceCode { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Models.DataBaseModel +{ + public class SysPermissionDto : DtoBase + { + public string RoleId { get; set; } + + public string PermissionId { get; set; } + + public bool IsMenu { get; set; } + + /// + ///role code + /// + public string RoleCode { get; set; } + + /// + /// Resource (button) code + /// + public string ResourceCode { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysRoleAdminUserInfoDto.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysRoleAdminUserInfoDto.cs index d037b4cf0..c8b1dbc5a 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysRoleAdminUserInfoDto.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysRoleAdminUserInfoDto.cs @@ -1,29 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Models.DataBaseModel -{ - public class SysRoleAdminUserInfoDto : DtoBase - { - /// - /// 角色名称 - /// - public string RoleName { get; set; } - - /// - /// 角色编号 - /// - public string RoleId { get; set; } - - /// - /// 用户编号 - /// - public int AdminAccountId { get; set; } - - /// - /// 当前用户是否有此角色 - /// - public bool HasRole { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Models.DataBaseModel +{ + public class SysRoleAdminUserInfoDto : DtoBase + { + /// + /// role name + /// + public string RoleName { get; set; } + + /// + /// role number + /// + public string RoleId { get; set; } + + /// + ///user number + /// + public int AdminAccountId { get; set; } + + /// + /// Whether the current user has this role + /// + public bool HasRole { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysRoleDto.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysRoleDto.cs index c33092678..3383f46c1 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysRoleDto.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/SysRoleDto.cs @@ -1,30 +1,30 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Text; - -namespace Senparc.Ncf.Core.Models.DataBaseModel -{ - public class SysRoleDto : DtoBase - { - [MaxLength(50)] - public string Id { get; set; } - - /// - /// 角色名称 - /// - [MaxLength(50)] - public string RoleName { get; set; } - - /// - /// 角色代码 - /// - [MaxLength(50)] - public string RoleCode { get; set; } - - /// - /// 启用 - /// - public bool Enabled { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace Senparc.Ncf.Core.Models.DataBaseModel +{ + public class SysRoleDto : DtoBase + { + [MaxLength(50)] + public string Id { get; set; } + + /// + /// role name + /// + [MaxLength(50)] + public string RoleName { get; set; } + + /// + ///role code + /// + [MaxLength(50)] + public string RoleCode { get; set; } + + /// + ///enable + /// + public bool Enabled { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/XscfModuleDto.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/XscfModuleDto.cs index c2baf8e70..db88798ad 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/XscfModuleDto.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Dto/XscfModuleDto.cs @@ -1,129 +1,129 @@ -using Microsoft.EntityFrameworkCore; -using Senparc.Ncf.Core.Enums; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Text; - -namespace Senparc.Ncf.Core.Models.DataBaseModel -{ - public class XncfModuleDto : DtoBase - { - public int Id { get; private set; } - public string Name { get; private set; } - public string Uid { get; private set; } - public string MenuName { get; private set; } - public string Version { get; private set; } - public string Description { get; private set; } - public string UpdateLog { get; private set; } - public bool AllowRemove { get; private set; } - public string MenuId { get; private set; } - public string Icon { get; private set; } - public XncfModules_State State { get; private set; } - private XncfModuleDto() { } - - - public XncfModuleDto(int id, string name, string uid, string menuName, string version, string description, string updateLog, bool allowRemove, string menuId, string icon, XncfModules_State state) - { - Id = id; - Name = name; - Uid = uid; - MenuName = menuName; - Version = version; - Description = description; - UpdateLog = updateLog; - AllowRemove = allowRemove; - MenuId = menuId; - Icon = icon; - State = state; - } - } - - public class CreateOrUpdate_XncfModuleDto : DtoBase - { - [Required, StringLength(100)] - public string Name { get; private set; } - [Required, StringLength(100)] - public string Uid { get; private set; } - [Required, StringLength(100)] - public string MenuName { get; private set; } - [Required] - public string Version { get; private set; } - [Required] - public string Description { get; private set; } - - [Required] - public string UpdateLog { get; private set; } - [Required] - public bool AllowRemove { get; private set; } - public string MenuId { get; private set; } - public string Icon { get; private set; } - [Required] - public XncfModules_State State { get; private set; } - - private CreateOrUpdate_XncfModuleDto() { } - - public CreateOrUpdate_XncfModuleDto(string name, string uid, string menuName, string version, string description, string updateLog, bool allowRemove, string menuId, string icon, XncfModules_State state) - { - Name = name; - Uid = uid; - MenuName = menuName; - Version = version; - Description = description; - UpdateLog = updateLog; - AllowRemove = allowRemove; - MenuId = menuId; - Icon = icon; - State = state; - } - } - - public class UpdateVersion_XncfModuleDto : DtoBase - { - - [Required, StringLength(100)] - public string Name { get; private set; } - [Required, StringLength(100)] - public string Uid { get; private set; } - [Required, StringLength(100)] - public string MenuName { get; private set; } - [Required] - public string Version { get; private set; } - [Required] - public string Description { get; private set; } - public string Icon { get; private set; } - - private UpdateVersion_XncfModuleDto() { } - - - public UpdateVersion_XncfModuleDto(string name, string uid, string menuName, string version, string description,string icon) - { - Name = name; - Uid = uid; - MenuName = menuName; - Version = version; - Description = description; - Icon = icon; - } - } - - /// - /// 跟新菜单Id - /// - public class UpdateMenuId_XncfModuleDto : DtoBase - { - public string Uid { get; set; } - - public string MenuId { get; private set; } - - private UpdateMenuId_XncfModuleDto() { } - - - public UpdateMenuId_XncfModuleDto(string uid, string menuId) - { - Uid = uid; - MenuId = menuId; - } - } - -} +using Microsoft.EntityFrameworkCore; +using Senparc.Ncf.Core.Enums; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace Senparc.Ncf.Core.Models.DataBaseModel +{ + public class XncfModuleDto : DtoBase + { + public int Id { get; private set; } + public string Name { get; private set; } + public string Uid { get; private set; } + public string MenuName { get; private set; } + public string Version { get; private set; } + public string Description { get; private set; } + public string UpdateLog { get; private set; } + public bool AllowRemove { get; private set; } + public string MenuId { get; private set; } + public string Icon { get; private set; } + public XncfModules_State State { get; private set; } + private XncfModuleDto() { } + + + public XncfModuleDto(int id, string name, string uid, string menuName, string version, string description, string updateLog, bool allowRemove, string menuId, string icon, XncfModules_State state) + { + Id = id; + Name = name; + Uid = uid; + MenuName = menuName; + Version = version; + Description = description; + UpdateLog = updateLog; + AllowRemove = allowRemove; + MenuId = menuId; + Icon = icon; + State = state; + } + } + + public class CreateOrUpdate_XncfModuleDto : DtoBase + { + [Required, StringLength(100)] + public string Name { get; private set; } + [Required, StringLength(100)] + public string Uid { get; private set; } + [Required, StringLength(100)] + public string MenuName { get; private set; } + [Required] + public string Version { get; private set; } + [Required] + public string Description { get; private set; } + + [Required] + public string UpdateLog { get; private set; } + [Required] + public bool AllowRemove { get; private set; } + public string MenuId { get; private set; } + public string Icon { get; private set; } + [Required] + public XncfModules_State State { get; private set; } + + private CreateOrUpdate_XncfModuleDto() { } + + public CreateOrUpdate_XncfModuleDto(string name, string uid, string menuName, string version, string description, string updateLog, bool allowRemove, string menuId, string icon, XncfModules_State state) + { + Name = name; + Uid = uid; + MenuName = menuName; + Version = version; + Description = description; + UpdateLog = updateLog; + AllowRemove = allowRemove; + MenuId = menuId; + Icon = icon; + State = state; + } + } + + public class UpdateVersion_XncfModuleDto : DtoBase + { + + [Required, StringLength(100)] + public string Name { get; private set; } + [Required, StringLength(100)] + public string Uid { get; private set; } + [Required, StringLength(100)] + public string MenuName { get; private set; } + [Required] + public string Version { get; private set; } + [Required] + public string Description { get; private set; } + public string Icon { get; private set; } + + private UpdateVersion_XncfModuleDto() { } + + + public UpdateVersion_XncfModuleDto(string name, string uid, string menuName, string version, string description,string icon) + { + Name = name; + Uid = uid; + MenuName = menuName; + Version = version; + Description = description; + Icon = icon; + } + } + + /// + /// Follow the new menu ID + /// + public class UpdateMenuId_XncfModuleDto : DtoBase + { + public string Uid { get; set; } + + public string MenuId { get; private set; } + + private UpdateMenuId_XncfModuleDto() { } + + + public UpdateMenuId_XncfModuleDto(string uid, string menuId) + { + Uid = uid; + MenuId = menuId; + } + } + +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Mapping/Base/BlankEntityTypeConfiguration.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Mapping/Base/BlankEntityTypeConfiguration.cs index c6d8555be..05b80c7e0 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Mapping/Base/BlankEntityTypeConfiguration.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Mapping/Base/BlankEntityTypeConfiguration.cs @@ -1,25 +1,25 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Models.DataBaseModel -{ - /// - /// 空的实现 IEntityTypeConfiguration 接口的类 - /// - /// - public class BlankEntityTypeConfiguration : IEntityTypeConfiguration - where TEntity : class - { - /// - /// 设置 TEntity 的实体 - /// - /// 用于设置实体类型(entity type)的 builder - public void Configure(EntityTypeBuilder builder) - { - - } - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Models.DataBaseModel +{ + /// + /// Empty class that implements the IEntityTypeConfiguration interface + /// + /// + public class BlankEntityTypeConfiguration : IEntityTypeConfiguration + where TEntity : class + { + /// + ///Set the entity of TEntity + /// + /// Builder used to set entity type (entity type) + public void Configure(EntityTypeBuilder builder) + { + + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Mapping/Base/ConfigurationMappingBase.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Mapping/Base/ConfigurationMappingBase.cs index 7674c0f2c..aaee980d1 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Mapping/Base/ConfigurationMappingBase.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/Mapping/Base/ConfigurationMappingBase.cs @@ -1,44 +1,44 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Models.DataBaseModel -{ - /// - /// 包含 Id(Key)的 ConfigurationMapping 基类 - /// - /// - public class ConfigurationMappingWithIdBase : ConfigurationMappingBase, IEntityTypeConfiguration - where TEntity : EntityBase - { - /// - /// 配置 实例 - /// - /// - public override void Configure(EntityTypeBuilder builder) - { - builder.HasKey(z => z.Id); - base.Configure(builder); - } - } - - /// - /// 不包含 Id(Key)的 ConfigurationMapping 基类 - /// - public class ConfigurationMappingBase : IEntityTypeConfiguration - where TEntity : EntityBase - { - /// - /// 配置 实例 - /// - /// - public virtual void Configure(EntityTypeBuilder builder) - { - //builder.Property(e => e.AddTime).HasColumnType("datetime").IsRequired(); - //builder.Property(e => e.LastUpdateTime).HasColumnType("datetime").IsRequired(); - } - - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Models.DataBaseModel +{ + /// + /// ConfigurationMapping base class containing Id (Key) + /// + /// + public class ConfigurationMappingWithIdBase : ConfigurationMappingBase, IEntityTypeConfiguration + where TEntity : EntityBase + { + /// + /// Configure instance + /// + /// + public override void Configure(EntityTypeBuilder builder) + { + builder.HasKey(z => z.Id); + base.Configure(builder); + } + } + + /// + /// ConfigurationMapping base class that does not contain Id (Key) + /// + public class ConfigurationMappingBase : IEntityTypeConfiguration + where TEntity : EntityBase + { + /// + /// Configure instance + /// + /// + public virtual void Configure(EntityTypeBuilder builder) + { + //builder.Property(e => e.AddTime).HasColumnType("datetime").IsRequired(); + //builder.Property(e => e.LastUpdateTime).HasColumnType("datetime").IsRequired(); + } + + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysButton.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysButton.cs index 1c7253b88..2f5e143de 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysButton.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysButton.cs @@ -1,64 +1,64 @@ -using Senparc.Ncf.Core.Models; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Text; - -namespace Senparc.Ncf.Core.Models.DataBaseModel -{ - /// - /// 菜单对应的按钮 - /// - [Table("SysButtons")] - public class SysButton : EntityBase - { - public SysButton() - { - Id = Guid.NewGuid().ToString(); - AddTime = DateTime.Now; - LastUpdateTime = AddTime; - } - - public SysButton(SysButtonDto sysButtonDto) : this() - { - MenuId = sysButtonDto.MenuId; - ButtonName = sysButtonDto.ButtonName; - OpearMark = sysButtonDto.OpearMark; - Url = sysButtonDto.Url; - } - - /// - /// 菜单id - /// - [MaxLength(150)] - public string MenuId { get; set; } - - /// - /// 操作名称 - /// - [MaxLength(150)] - [Required] - public string ButtonName { get; set; } - - /// - /// 操作标识 - /// - [MaxLength(150)] - public string OpearMark { get; set; } - - public void Update(SysButtonDto item) - { - ButtonName = item.ButtonName; - OpearMark = item.OpearMark; - Url = item.Url; - LastUpdateTime = DateTime.Now; - } - - /// - /// 按钮对应的请求地址 - /// - [MaxLength(500)] - public string Url { get; set; } - } -} +using Senparc.Ncf.Core.Models; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; + +namespace Senparc.Ncf.Core.Models.DataBaseModel +{ + /// + /// button corresponding to the menu + /// + [Table("SysButtons")] + public class SysButton : EntityBase + { + public SysButton() + { + Id = Guid.NewGuid().ToString(); + AddTime = DateTime.Now; + LastUpdateTime = AddTime; + } + + public SysButton(SysButtonDto sysButtonDto) : this() + { + MenuId = sysButtonDto.MenuId; + ButtonName = sysButtonDto.ButtonName; + OpearMark = sysButtonDto.OpearMark; + Url = sysButtonDto.Url; + } + + /// + /// menu id + /// + [MaxLength(150)] + public string MenuId { get; set; } + + /// + /// operation name + /// + [MaxLength(150)] + [Required] + public string ButtonName { get; set; } + + /// + /// operation identifier + /// + [MaxLength(150)] + public string OpearMark { get; set; } + + public void Update(SysButtonDto item) + { + ButtonName = item.ButtonName; + OpearMark = item.OpearMark; + Url = item.Url; + LastUpdateTime = DateTime.Now; + } + + /// + ///Request address corresponding to the button + /// + [MaxLength(500)] + public string Url { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysMenu.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysMenu.cs index a8b434b5e..5f0abac99 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysMenu.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysMenu.cs @@ -1,106 +1,106 @@ -using Senparc.Ncf.Core.Models; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Text; - -namespace Senparc.Ncf.Core.Models.DataBaseModel -{ - /// - /// 菜单表 - /// - [Table("SysMenus")] - public class SysMenu : EntityBase - { - - public SysMenu() - { - Id = Guid.NewGuid().ToString(); - AddTime = DateTime.Now; - this.LastUpdateTime = AddTime; - ResourceCode = string.Empty; - } - - public SysMenu(SysMenuDto sysMenuDto) : this() - { - LastUpdateTime = DateTime.Now; - Icon = sysMenuDto.Icon; - Sort = sysMenuDto.Sort; - Visible = sysMenuDto.Visible; - Url = sysMenuDto.Url; - ParentId = sysMenuDto.ParentId; - MenuName = sysMenuDto.MenuName; - IsLocked = sysMenuDto.IsLocked; - MenuType = sysMenuDto.MenuType; - ResourceCode = sysMenuDto.ResourceCode ?? string.Empty; - } - - [MaxLength(150)] - public new string Id { get; set; } - - [MaxLength(150)] - [Required] - public string MenuName { get; set; } - - /// - /// 父菜单 - /// - [MaxLength(150)] - public string ParentId { get; set; } - - [MaxLength(500)] - public string Url { get; set; } - - /// - /// 图标 - /// - [MaxLength(150)] - public string Icon { get; set; } - - /// - /// 是否锁定, 锁定后不能 修改和删除 - /// - public bool IsLocked { get; set; } - - /// - /// 类型 - /// - public MenuType MenuType { get; set; } - - /// - /// 操作资源 - /// - [MaxLength(150)] - public string ResourceCode { get; set; } - - public void Update(SysMenuDto sysMenuDto) - { - this.LastUpdateTime = DateTime.Now; - Icon = sysMenuDto.Icon; - Sort = sysMenuDto.Sort; - Visible = sysMenuDto.Visible; - Url = sysMenuDto.Url; - MenuName = sysMenuDto.MenuName; - MenuType = sysMenuDto.MenuType; - ResourceCode = sysMenuDto.ResourceCode; - } - /// - /// - /// - public int Sort { get; set; } - - /// - /// 是否可见 - /// - public bool Visible { get; set; } - } - - public enum MenuType - { - 无, - 菜单, - 页面, - 按钮 - } -} +using Senparc.Ncf.Core.Models; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; + +namespace Senparc.Ncf.Core.Models.DataBaseModel +{ + /// + /// menu table + /// + [Table("SysMenus")] + public class SysMenu : EntityBase + { + + public SysMenu() + { + Id = Guid.NewGuid().ToString(); + AddTime = DateTime.Now; + this.LastUpdateTime = AddTime; + ResourceCode = string.Empty; + } + + public SysMenu(SysMenuDto sysMenuDto) : this() + { + LastUpdateTime = DateTime.Now; + Icon = sysMenuDto.Icon; + Sort = sysMenuDto.Sort; + Visible = sysMenuDto.Visible; + Url = sysMenuDto.Url; + ParentId = sysMenuDto.ParentId; + MenuName = sysMenuDto.MenuName; + IsLocked = sysMenuDto.IsLocked; + MenuType = sysMenuDto.MenuType; + ResourceCode = sysMenuDto.ResourceCode ?? string.Empty; + } + + [MaxLength(150)] + public new string Id { get; set; } + + [MaxLength(150)] + [Required] + public string MenuName { get; set; } + + /// + ///parent menu + /// + [MaxLength(150)] + public string ParentId { get; set; } + + [MaxLength(500)] + public string Url { get; set; } + + /// + /// icon + /// + [MaxLength(150)] + public string Icon { get; set; } + + /// + /// Whether to lock or not, it cannot be modified or deleted after locking. + /// + public bool IsLocked { get; set; } + + /// + /// type + /// + public MenuType MenuType { get; set; } + + /// + ///operating resources + /// + [MaxLength(150)] + public string ResourceCode { get; set; } + + public void Update(SysMenuDto sysMenuDto) + { + this.LastUpdateTime = DateTime.Now; + Icon = sysMenuDto.Icon; + Sort = sysMenuDto.Sort; + Visible = sysMenuDto.Visible; + Url = sysMenuDto.Url; + MenuName = sysMenuDto.MenuName; + MenuType = sysMenuDto.MenuType; + ResourceCode = sysMenuDto.ResourceCode; + } + /// + /// + /// + public int Sort { get; set; } + + /// + /// is visible + /// + public bool Visible { get; set; } + } + + public enum MenuType + { + 无, + 菜单, + 页面, + 按钮 + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysRole.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysRole.cs index 13cff64f9..63bfde042 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysRole.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysRole.cs @@ -1,63 +1,63 @@ -using Senparc.Ncf.Core.Models; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Text; - -namespace Senparc.Ncf.Core.Models.DataBaseModel -{ - /// - /// 系统角色 - /// - [Table("SysRoles")] - public class SysRole : EntityBase - { - - public SysRole() - { - this.AddTime = DateTime.Now; - LastUpdateTime = AddTime; - Id = Guid.NewGuid().ToString(); - } - - public SysRole(SysRoleDto roleDto) : this() - { - RoleName = roleDto.RoleName; - Enabled = roleDto.Enabled; - RoleCode = roleDto.RoleCode; - Remark = roleDto.Remark; - AdminRemark = roleDto.AdminRemark; - } - - public void Update(SysRoleDto roleDto) - { - LastUpdateTime = DateTime.Now; - RoleName = roleDto.RoleName; - RoleCode = roleDto.RoleCode; - Enabled = roleDto.Enabled; - Remark = roleDto.Remark; - AdminRemark = roleDto.AdminRemark; - } - - /// - /// 启用状态 - /// - public bool Enabled { get; set; } - - [MaxLength(150)] - public new string Id { get; set; } - - /// - /// 角色名称 - /// - [MaxLength(150)] - public string RoleName { get; set; } - - /// - /// 角色代码 - /// - [MaxLength(150)] - public string RoleCode { get; set; } - } -} +using Senparc.Ncf.Core.Models; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; + +namespace Senparc.Ncf.Core.Models.DataBaseModel +{ + /// + ///system role + /// + [Table("SysRoles")] + public class SysRole : EntityBase + { + + public SysRole() + { + this.AddTime = DateTime.Now; + LastUpdateTime = AddTime; + Id = Guid.NewGuid().ToString(); + } + + public SysRole(SysRoleDto roleDto) : this() + { + RoleName = roleDto.RoleName; + Enabled = roleDto.Enabled; + RoleCode = roleDto.RoleCode; + Remark = roleDto.Remark; + AdminRemark = roleDto.AdminRemark; + } + + public void Update(SysRoleDto roleDto) + { + LastUpdateTime = DateTime.Now; + RoleName = roleDto.RoleName; + RoleCode = roleDto.RoleCode; + Enabled = roleDto.Enabled; + Remark = roleDto.Remark; + AdminRemark = roleDto.AdminRemark; + } + + /// + /// enabled status + /// + public bool Enabled { get; set; } + + [MaxLength(150)] + public new string Id { get; set; } + + /// + /// role name + /// + [MaxLength(150)] + public string RoleName { get; set; } + + /// + ///role code + /// + [MaxLength(150)] + public string RoleCode { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysRoleAdminUserInfo.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysRoleAdminUserInfo.cs index 24d99bc99..1d6810561 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysRoleAdminUserInfo.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysRoleAdminUserInfo.cs @@ -1,43 +1,43 @@ -using Senparc.Ncf.Core.Models; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Text; - -namespace Senparc.Ncf.Core.Models.DataBaseModel -{ - /// - /// 角色人员表 - /// - [Table("SysRoleAdminUserInfos")] - public class SysRoleAdminUserInfo : EntityBase - { - public SysRoleAdminUserInfo() - { - AddTime = DateTime.Now; - LastUpdateTime = DateTime.Now; - } - - public SysRoleAdminUserInfo(int accountId, string roleId, string roleCode) : this() - { - AccountId = accountId; - RoleId = roleId; - RoleCode = roleCode; - } - - [MaxLength(150)] - public string RoleCode { get; set; } - - /// - /// 管理员Id - /// - public int AccountId { get; set; } - - /// - /// 角色Id - /// - [MaxLength(150)] - public string RoleId { get; set; } - } -} +using Senparc.Ncf.Core.Models; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; + +namespace Senparc.Ncf.Core.Models.DataBaseModel +{ + /// + ///Character list + /// + [Table("SysRoleAdminUserInfos")] + public class SysRoleAdminUserInfo : EntityBase + { + public SysRoleAdminUserInfo() + { + AddTime = DateTime.Now; + LastUpdateTime = DateTime.Now; + } + + public SysRoleAdminUserInfo(int accountId, string roleId, string roleCode) : this() + { + AccountId = accountId; + RoleId = roleId; + RoleCode = roleCode; + } + + [MaxLength(150)] + public string RoleCode { get; set; } + + /// + ///adminId + /// + public int AccountId { get; set; } + + /// + /// roleId + /// + [MaxLength(150)] + public string RoleId { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysRolePermission.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysRolePermission.cs index 15c9ac75e..c89debcc4 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysRolePermission.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SysRolePermission.cs @@ -1,64 +1,64 @@ -using Senparc.Ncf.Core.Models; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Text; - -namespace Senparc.Ncf.Core.Models.DataBaseModel -{ - /// - /// 角色菜单表 - /// - [Table("SysRolePermissions")] - public class SysRolePermission : EntityBase - { - /* 注意:这里 Table 如果用 SysPermissions,将和 SQL Server 的系统表冲突 - * 参考:https://docs.microsoft.com/zh-cn/sql/relational-databases/system-compatibility-views/sys-syspermissions-transact-sql?redirectedfrom=MSDN&view=sql-server-ver15 - */ - - public SysRolePermission() - { - AddTime = DateTime.Now; - LastUpdateTime = DateTime.Now; - } - - public SysRolePermission(SysPermissionDto item) : this() - { - RoleId = item.RoleId; - IsMenu = item.IsMenu; - PermissionId = item.PermissionId; - RoleCode = item.RoleCode; - ResourceCode = item.ResourceCode; - } - - /// - /// 角色代码 - /// - [MaxLength(150)] - public string RoleCode { get; set; } - - /// - /// 资源(按钮)代码 - /// - [MaxLength(150)] - public string ResourceCode { get; set; } - - /// - /// 角色Id - /// - [MaxLength(150)] - public string RoleId { get; set; } - - /// - /// 是否是菜单 - /// - public bool IsMenu { get; set; } - - /// - /// 权限Id(菜单或者是按钮) - /// - [MaxLength(150)] - public string PermissionId { get; set; } - } -} +using Senparc.Ncf.Core.Models; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; + +namespace Senparc.Ncf.Core.Models.DataBaseModel +{ + /// + ///Character menu table + /// + [Table("SysRolePermissions")] + public class SysRolePermission : EntityBase + { + /* Note: If SysPermissions is used in Table here, it will conflict with the system tables of SQL Server. + * Reference: https://docs.microsoft.com/zh-cn/sql/relational-databases/system-compatibility-views/sys-syspermissions-transact-sql?redirectedfrom=MSDN&view=sql-server-ver15 + */ + + public SysRolePermission() + { + AddTime = DateTime.Now; + LastUpdateTime = DateTime.Now; + } + + public SysRolePermission(SysPermissionDto item) : this() + { + RoleId = item.RoleId; + IsMenu = item.IsMenu; + PermissionId = item.PermissionId; + RoleCode = item.RoleCode; + ResourceCode = item.ResourceCode; + } + + /// + ///role code + /// + [MaxLength(150)] + public string RoleCode { get; set; } + + /// + /// Resource (button) code + /// + [MaxLength(150)] + public string ResourceCode { get; set; } + + /// + /// roleId + /// + [MaxLength(150)] + public string RoleId { get; set; } + + /// + /// Whether it is a menu + /// + public bool IsMenu { get; set; } + + /// + /// Permission ID (menu or button) + /// + [MaxLength(150)] + public string PermissionId { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SystemConfig.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SystemConfig.cs index a1d116610..09f6dec08 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SystemConfig.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/SystemConfig.cs @@ -1,77 +1,77 @@ -using Senparc.Ncf.Core.Models; -using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - -namespace Senparc.Ncf.Core.Models -{ - [Serializable] - [Table("SystemConfigs")] - public partial class SystemConfig : EntityBase - { - [Required] - [MaxLength(100)] - public string SystemName { get; private set; } - - [MaxLength(100)] - public string MchId { get; private set; } - - [MaxLength(300)] - public string MchKey { get; private set; } - - [MaxLength(100)] - public string TenPayAppId { get; private set; } - - /// - /// 是否隐藏模块管理 - /// - public bool? HideModuleManager { get; private set; } - - public int NeuCharDeveloperId { get; private set; } - - [MaxLength(100)] - public string NeuCharAppKey { get; private set; } - - [MaxLength(100)] - public string NeuCharAppSecret { get; private set; } - - public SystemConfig(string systemName, string mchId, string mchKey, string tenPayAppId, bool? hideModuleManager, int neuCharDeveloperId, string neuCharAppKey, string neuCharAppSecret) - { - SystemName = systemName; - MchId = mchId; - MchKey = mchKey; - TenPayAppId = tenPayAppId; - HideModuleManager = hideModuleManager; - NeuCharDeveloperId = neuCharDeveloperId; - NeuCharAppKey = neuCharAppKey; - NeuCharAppSecret = neuCharAppSecret; - } - - /// - /// 更新 - /// - /// - /// - /// - /// - /// - public void Update(string systemName, string mchId, string mchKey, string tenPayAppId, bool? hideModuleManager/*, int neuCharDeveloperId, string neuCharAppKey, string neuCharAppSecret*/) - { - SystemName = systemName; - MchId = mchId; - MchKey = mchKey; - TenPayAppId = tenPayAppId; - HideModuleManager = hideModuleManager; - //NeuCharDeveloperId = neuCharDeveloperId; - //NeuCharAppKey = neuCharAppKey; - //NeuCharAppSecret = neuCharAppSecret; - } - - public void UpdateNeuCharAccount(int developerId, string appKey, string appSecret) - { - this.NeuCharDeveloperId = developerId; - this.NeuCharAppKey = appKey; - this.NeuCharAppSecret = appSecret; - } - } -} +using Senparc.Ncf.Core.Models; +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Senparc.Ncf.Core.Models +{ + [Serializable] + [Table("SystemConfigs")] + public partial class SystemConfig : EntityBase + { + [Required] + [MaxLength(100)] + public string SystemName { get; private set; } + + [MaxLength(100)] + public string MchId { get; private set; } + + [MaxLength(300)] + public string MchKey { get; private set; } + + [MaxLength(100)] + public string TenPayAppId { get; private set; } + + /// + /// Whether to hide module management + /// + public bool? HideModuleManager { get; private set; } + + public int NeuCharDeveloperId { get; private set; } + + [MaxLength(100)] + public string NeuCharAppKey { get; private set; } + + [MaxLength(100)] + public string NeuCharAppSecret { get; private set; } + + public SystemConfig(string systemName, string mchId, string mchKey, string tenPayAppId, bool? hideModuleManager, int neuCharDeveloperId, string neuCharAppKey, string neuCharAppSecret) + { + SystemName = systemName; + MchId = mchId; + MchKey = mchKey; + TenPayAppId = tenPayAppId; + HideModuleManager = hideModuleManager; + NeuCharDeveloperId = neuCharDeveloperId; + NeuCharAppKey = neuCharAppKey; + NeuCharAppSecret = neuCharAppSecret; + } + + /// + /// renew + /// + /// + /// + /// + /// + /// + public void Update(string systemName, string mchId, string mchKey, string tenPayAppId, bool? hideModuleManager/*, int neuCharDeveloperId, string neuCharAppKey, string neuCharAppSecret*/) + { + SystemName = systemName; + MchId = mchId; + MchKey = mchKey; + TenPayAppId = tenPayAppId; + HideModuleManager = hideModuleManager; + //NeuCharDeveloperId = neuCharDeveloperId; + //NeuCharAppKey = neuCharAppKey; + //NeuCharAppSecret = neuCharAppSecret; + } + + public void UpdateNeuCharAccount(int developerId, string appKey, string appSecret) + { + this.NeuCharDeveloperId = developerId; + this.NeuCharAppKey = appKey; + this.NeuCharAppSecret = appSecret; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/XncfModule.cs b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/XncfModule.cs index 7f3b5c60d..22ee178ab 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/XncfModule.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/DataBaseModel/XncfModule.cs @@ -1,86 +1,86 @@ -using Microsoft.EntityFrameworkCore; -using Senparc.Ncf.Core.Enums; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using System.Text; - -namespace Senparc.Ncf.Core.Models.DataBaseModel -{ - /// - /// 扩展模块信息 - /// - [Table("XncfModules")] - public class XncfModule : EntityBase - { - public string Name { get; private set; } - public string Uid { get; private set; } - public string MenuName { get; private set; } - public string Version { get; private set; } - public string Description { get; set; } - public string UpdateLog { get; private set; } - public bool AllowRemove { get; private set; } - public string MenuId { get; private set; } - /// - /// 使用 FontAwesome 图标,如:fa fa-star - /// - public string Icon { get; private set; } - - public XncfModules_State State { get; private set; } - - /// - /// 添加日志 - /// - /// - private void AddUpdateLog(string log) - { - UpdateLog += $"[{SystemTime.Now}] {log}\r\n"; - } - - - private XncfModule() { } - - - public XncfModule(string name, string uid, string menuName, string version, string description, string updateLog, bool allowRemove, string menuId,string icon, XncfModules_State state) - { - Name = name; - Uid = uid; - MenuName = menuName; - Version = version; - Description = description; - UpdateLog = updateLog; - AllowRemove = allowRemove; - MenuId = menuId; - Icon = icon; - State = state; - } - - public void Create() - { - AddUpdateLog($"创建新模块:{MenuName}:{Name} / {Uid}"); - } - - public void UpdateVersion(string version, string menuName, string description) - { - AddUpdateLog($"更新模块版本号:{MenuName}。版本:{Version} > {version} 菜单:{MenuName} > {menuName}"); - - Version = version; - MenuName = menuName; - Description = description; - - UpdateState(XncfModules_State.更新待审核); - } - - public void UpdateState(XncfModules_State newState) - { - AddUpdateLog($"更新模块状态:{MenuName}。状态:{State} > {newState}"); - State = newState; - } - - public void UpdateMenuId(string menuId) - { - MenuId = menuId; - AddUpdateLog($"已绑定菜单"); - } - } -} +using Microsoft.EntityFrameworkCore; +using Senparc.Ncf.Core.Enums; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; + +namespace Senparc.Ncf.Core.Models.DataBaseModel +{ + /// + ///Extension module information + /// + [Table("XncfModules")] + public class XncfModule : EntityBase + { + public string Name { get; private set; } + public string Uid { get; private set; } + public string MenuName { get; private set; } + public string Version { get; private set; } + public string Description { get; set; } + public string UpdateLog { get; private set; } + public bool AllowRemove { get; private set; } + public string MenuId { get; private set; } + /// + /// Use FontAwesome icons, such as: fa fa-star + /// + public string Icon { get; private set; } + + public XncfModules_State State { get; private set; } + + /// + ///Add log + /// + /// + private void AddUpdateLog(string log) + { + UpdateLog += $"[{SystemTime.Now}] {log}\r\n"; + } + + + private XncfModule() { } + + + public XncfModule(string name, string uid, string menuName, string version, string description, string updateLog, bool allowRemove, string menuId,string icon, XncfModules_State state) + { + Name = name; + Uid = uid; + MenuName = menuName; + Version = version; + Description = description; + UpdateLog = updateLog; + AllowRemove = allowRemove; + MenuId = menuId; + Icon = icon; + State = state; + } + + public void Create() + { + AddUpdateLog($"创建新模块:{MenuName}:{Name} / {Uid}"); + } + + public void UpdateVersion(string version, string menuName, string description) + { + AddUpdateLog($"更新模块版本号:{MenuName}。版本:{Version} > {version} 菜单:{MenuName} > {menuName}"); + + Version = version; + MenuName = menuName; + Description = description; + + UpdateState(XncfModules_State.更新待审核); + } + + public void UpdateState(XncfModules_State newState) + { + AddUpdateLog($"更新模块状态:{MenuName}。状态:{State} > {newState}"); + State = newState; + } + + public void UpdateMenuId(string menuId) + { + MenuId = menuId; + AddUpdateLog($"已绑定菜单"); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/EntityBase/EntityBase.cs b/src/Basic/Senparc.Ncf.Core/Models/EntityBase/EntityBase.cs index b07740abe..da6d66427 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/EntityBase/EntityBase.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/EntityBase/EntityBase.cs @@ -1,89 +1,89 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Text; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// 数据库实体基类 - /// - [Serializable] - public partial class EntityBase : IEntityBase, IMultiTenancy - { - #region IEntityBase 接口 - - /// - /// 是否软删除 - /// - public bool Flag { get; set; } - /// - /// 添加时间 - /// - public DateTime AddTime { get; set; } - /// - /// 上次更新时间 - /// - public DateTime LastUpdateTime { get; set; } - - #endregion - - #region IMultiTenancy 接口 - - /// - /// 租户 ID - /// 如果为-1,则本系统不启用多租户 - /// 如果为0,则为系统公共数据(特殊情况使用) - /// - public int TenantId { get; set; } - - #endregion - - /// - /// 仅管理员备注 - /// - [MaxLength(300)] - public string AdminRemark { get; set; } - - /// - /// 前台用户可见备注 - /// - [MaxLength(300)] - public string Remark { get; set; } - - - public EntityBase() - { - AddTime = SystemTime.Now.DateTime; - } - - /// - /// 更新最后更新时间 - /// - /// - protected void SetUpdateTime(DateTime? time = null) - { - if (AddTime == DateTime.MinValue) - { - AddTime = SystemTime.Now.LocalDateTime;//通常在添加的时候发生 - } - LastUpdateTime = time ?? SystemTime.Now.LocalDateTime; - } - - } - - /// - /// 带单一主键的数据库实体基类 - /// - /// - [Serializable] - public partial class EntityBase : EntityBase, IEntityBase, IMultiTenancyEntityBase/*默认支持多租户接口*/ - { - /// - /// 主键 - /// - [Key] - public TKey Id { get; set; } - - } -} +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace Senparc.Ncf.Core.Models +{ + /// + /// Database entity base class + /// + [Serializable] + public partial class EntityBase : IEntityBase, IMultiTenancy + { + #region IEntityBase 接口 + + /// + /// Whether to soft delete + /// + public bool Flag { get; set; } + /// + ///add time + /// + public DateTime AddTime { get; set; } + /// + /// last updated time + /// + public DateTime LastUpdateTime { get; set; } + + #endregion + + #region IMultiTenancy 接口 + + /// + ///Tenant ID + /// If it is -1, multi-tenancy is not enabled in this system + /// If it is 0, it is system public data (used in special circumstances) + /// + public int TenantId { get; set; } + + #endregion + + /// + ///Admin comments only + /// + [MaxLength(300)] + public string AdminRemark { get; set; } + + /// + /// Remarks visible to front-end users + /// + [MaxLength(300)] + public string Remark { get; set; } + + + public EntityBase() + { + AddTime = SystemTime.Now.DateTime; + } + + /// + ///Update last update time + /// + /// + protected void SetUpdateTime(DateTime? time = null) + { + if (AddTime == DateTime.MinValue) + { + AddTime = SystemTime.Now.LocalDateTime;//Usually happens when adding + } + LastUpdateTime = time ?? SystemTime.Now.LocalDateTime; + } + + } + + /// + /// Database entity base class with single primary key + /// + /// + [Serializable] + public partial class EntityBase : EntityBase, IEntityBase, IMultiTenancyEntityBase/*Supports multi-tenant interface by default*/ + { + /// + /// primary key + /// + [Key] + public TKey Id { get; set; } + + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/EntityBase/IAggregateRoot.cs b/src/Basic/Senparc.Ncf.Core/Models/EntityBase/IAggregateRoot.cs index 8c1a3fc5f..204de06ed 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/EntityBase/IAggregateRoot.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/EntityBase/IAggregateRoot.cs @@ -1,13 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// 聚合根 - /// - public interface IAggregateRoot - { - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Models +{ + /// + /// aggregate root + /// + public interface IAggregateRoot + { + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/EntityBase/IEntityBase.cs b/src/Basic/Senparc.Ncf.Core/Models/EntityBase/IEntityBase.cs index 046ab378b..fd5b90084 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/EntityBase/IEntityBase.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/EntityBase/IEntityBase.cs @@ -1,34 +1,34 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// 所有实体的最底层接口(支持软删除) - /// - public interface IEntityBase : ISoftDelete - { - /// - /// 添加时间 - /// - DateTime AddTime { get; set; } - /// - /// 更新时间 - /// - DateTime LastUpdateTime { get; set; } - } - - public interface IEntityBase : IEntityBase - { - /// - /// 主键 - /// - TKey Id { get; set; } - } - - public interface IMultiTenancyEntityBase : IEntityBase, IMultiTenancy - { - - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Models +{ + /// + /// The lowest level interface of all entities (supports soft deletion) + /// + public interface IEntityBase : ISoftDelete + { + /// + ///add time + /// + DateTime AddTime { get; set; } + /// + ///Update time + /// + DateTime LastUpdateTime { get; set; } + } + + public interface IEntityBase : IEntityBase + { + /// + /// primary key + /// + TKey Id { get; set; } + } + + public interface IMultiTenancyEntityBase : IEntityBase, IMultiTenancy + { + + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/EntityBase/IMultiTenancy.cs b/src/Basic/Senparc.Ncf.Core/Models/EntityBase/IMultiTenancy.cs index 9fb54c56f..fb23ce0ea 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/EntityBase/IMultiTenancy.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/EntityBase/IMultiTenancy.cs @@ -1,17 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// 多租户接口 - /// - public interface IMultiTenancy - { - /// - /// 租户Id - /// - public int TenantId { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Models +{ + /// + ///Multi-tenant interface + /// + public interface IMultiTenancy + { + /// + ///TenantId + /// + public int TenantId { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/EntityBase/ISoftDelete.cs b/src/Basic/Senparc.Ncf.Core/Models/EntityBase/ISoftDelete.cs index dcec4d47c..6c1446b69 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/EntityBase/ISoftDelete.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/EntityBase/ISoftDelete.cs @@ -1,17 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// 数据库数据软删除接口 - /// - public interface ISoftDelete - { - /// - /// 是否软删除 - /// - bool Flag { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Models +{ + /// + /// Database data soft deletion interface + /// + public interface ISoftDelete + { + /// + /// Whether to soft delete + /// + bool Flag { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/EntitySetKeys.cs b/src/Basic/Senparc.Ncf.Core/Models/EntitySetKeys.cs index 51137fb3f..8169f7b4d 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/EntitySetKeys.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/EntitySetKeys.cs @@ -1,172 +1,172 @@ -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.EntityFrameworkCore; -using Senparc.CO2NET.Exceptions; -using Senparc.CO2NET.Extensions; -using Senparc.CO2NET.Trace; -using Senparc.Ncf.Core.Exceptions; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// 所有 EntityFramework 中 Entitey 的 SetKey 的集合 - /// - public static class EntitySetKeys - { - private static EntitySetKeysDictionary AllKeys = new EntitySetKeysDictionary(); - - internal static ConcurrentBag DbContextStore { get; set; } = new ConcurrentBag(); - - internal static object DbContextStoreLock = new object(); - - /// - /// 加载制定 DbContext 中的 SetKey - /// - /// - /// 尝试强制载入,如果之前已经载入,则删除后重新载入 - /// - public static EntitySetKeysDictionary TryLoadSetInfo(Type tryLoadDbContextType, bool forceLoad = false) - { - lock (EntitySetKeys.DbContextStoreLock) - { - if (!tryLoadDbContextType.IsSubclassOf(typeof(DbContext))) - { - throw new ArgumentException($"{nameof(tryLoadDbContextType)}不是 DbContext 的子类!", nameof(tryLoadDbContextType)); - } - - var removeSuccess = true; - if (!forceLoad) - { - if (EntitySetKeys.DbContextStore.Contains(tryLoadDbContextType)) - { - return AllKeys;//已经载入过了,直接返回现有对象,不再加载 - } - } - else - { - //强制载入 - removeSuccess = EntitySetKeys.DbContextStore.TryTake(out tryLoadDbContextType); - SenparcTrace.BaseExceptionLog(new BaseException($"EntitySetKeys.DbContextStore.TryTake 失败,tryLoadDbContextType 类型:{tryLoadDbContextType.FullName}")); - } - - if (removeSuccess) - { - EntitySetKeys.DbContextStore.Add(tryLoadDbContextType); - } - - //初始化的时候从ORM中自动读取实体集名称及实体类别名称 - var clientProperties = tryLoadDbContextType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty); - - var properities = new List(); - properities.AddRange(clientProperties); - - foreach (var prop in properities) - { - try - { - //ObjectQuery,ObjectSet for EF4,DbSet for EF Code First - if (prop.PropertyType.Name.IndexOf("DbSet") != -1 && prop.PropertyType.GetGenericArguments().Length > 0) - { - var dbSetType = prop.PropertyType.GetGenericArguments()[0]; - if (!AllKeys.ContainsKey(dbSetType)) - { - AllKeys[dbSetType] = new SetKeyInfo(prop.Name, dbSetType, tryLoadDbContextType);//获取第一个泛型 - } - else if (!AllKeys[dbSetType].SenparcEntityTypes.Contains(tryLoadDbContextType)) - { - AllKeys[dbSetType].SenparcEntityTypes.Add(tryLoadDbContextType);//给这个 dbSetType 添加一个新的 DbContext 关联类型 - } - } - } - catch(Exception ex) - { - Console.WriteLine($"{nameof(EntitySetKeys)}.TryLoadSetInfo() 发生异常:"); - Console.WriteLine(ex); - } - } - } - return AllKeys; - } - - /// - /// 获取 Entity SetKey 集合 - /// - /// DbContext 类型 - /// - public static EntitySetKeysDictionary GetEntitySetInfo(Type dbContextType) - { - EntitySetKeysDictionary dic = new EntitySetKeysDictionary(); - foreach (var setKeyInfo in AllKeys.Values.Where(z => z.SenparcEntityTypes.Contains(dbContextType))) - { - if (!dic.ContainsKey(setKeyInfo.DbSetType)) - { - var addSuccess = dic.TryAdd(setKeyInfo.DbSetType, setKeyInfo); - if (!addSuccess) - { - SenparcTrace.BaseExceptionLog(new NcfDatabaseException($"GetEntitySetInfo 发生异常,DbSetType:{setKeyInfo.DbSetType.Name},setKeyInfo:{setKeyInfo.ToJson()}", null, dbContextType)); - } - } - } - return dic; - } - - /// - /// 获取所有 Entity 的 SetKey - /// - /// - public static EntitySetKeysDictionary GetAllEntitySetInfo() - { - return AllKeys; - } - } - - - public class SetKeyInfo - { - /// - /// SetKey 属性名称 - /// - public string SetName { get; set; } - /// - /// DbSet 属性类型 - /// - public Type DbSetType { get; set; } - /// - /// SenparcEntity 类型 - /// - public List SenparcEntityTypes { get; set; } - - public SetKeyInfo(string setName, Type dbSetType, Type senparcEntityType) - { - SetName = setName; - DbSetType = dbSetType; - SenparcEntityTypes = new List() { senparcEntityType }; - } - } - - /// - /// 与ORM实体类对应的实体集 - /// - public class EntitySetKeysDictionary : ConcurrentDictionary - { - public new SetKeyInfo this[Type entityType] - { - get - { - if (!base.ContainsKey(entityType)) - { - throw new Exception($"未找到实体类型:{entityType.FullName}"); - } - return base[entityType]; - } - set - { - base[entityType] = value; - } - } - } +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.EntityFrameworkCore; +using Senparc.CO2NET.Exceptions; +using Senparc.CO2NET.Extensions; +using Senparc.CO2NET.Trace; +using Senparc.Ncf.Core.Exceptions; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Senparc.Ncf.Core.Models +{ + /// + /// A collection of SetKeys of Entitey in all EntityFrameworks + /// + public static class EntitySetKeys + { + private static EntitySetKeysDictionary AllKeys = new EntitySetKeysDictionary(); + + internal static ConcurrentBag DbContextStore { get; set; } = new ConcurrentBag(); + + internal static object DbContextStoreLock = new object(); + + /// + /// Load the SetKey in the specified DbContext + /// + /// + /// Try to force load, if it has been loaded before, delete and reload + /// + public static EntitySetKeysDictionary TryLoadSetInfo(Type tryLoadDbContextType, bool forceLoad = false) + { + lock (EntitySetKeys.DbContextStoreLock) + { + if (!tryLoadDbContextType.IsSubclassOf(typeof(DbContext))) + { + throw new ArgumentException($"{nameof(tryLoadDbContextType)}不是 DbContext 的子类!", nameof(tryLoadDbContextType)); + } + + var removeSuccess = true; + if (!forceLoad) + { + if (EntitySetKeys.DbContextStore.Contains(tryLoadDbContextType)) + { + return AllKeys;//It has already been loaded. Return the existing object directly and no longer load it. + } + } + else + { + //force load + removeSuccess = EntitySetKeys.DbContextStore.TryTake(out tryLoadDbContextType); + SenparcTrace.BaseExceptionLog(new BaseException($"EntitySetKeys.DbContextStore.TryTake 失败,tryLoadDbContextType 类型:{tryLoadDbContextType.FullName}")); + } + + if (removeSuccess) + { + EntitySetKeys.DbContextStore.Add(tryLoadDbContextType); + } + + //During initialization, the entity set name and entity category name are automatically read from the ORM. + var clientProperties = tryLoadDbContextType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty); + + var properities = new List(); + properities.AddRange(clientProperties); + + foreach (var prop in properities) + { + try + { + //ObjectQuery,ObjectSet for EF4,DbSet for EF Code First + if (prop.PropertyType.Name.IndexOf("DbSet") != -1 && prop.PropertyType.GetGenericArguments().Length > 0) + { + var dbSetType = prop.PropertyType.GetGenericArguments()[0]; + if (!AllKeys.ContainsKey(dbSetType)) + { + AllKeys[dbSetType] = new SetKeyInfo(prop.Name, dbSetType, tryLoadDbContextType);//Get the first generic + } + else if (!AllKeys[dbSetType].SenparcEntityTypes.Contains(tryLoadDbContextType)) + { + AllKeys[dbSetType].SenparcEntityTypes.Add(tryLoadDbContextType);//Add a new DbContext associated type to this dbSetType + } + } + } + catch(Exception ex) + { + Console.WriteLine($"{nameof(EntitySetKeys)}.TryLoadSetInfo() 发生异常:"); + Console.WriteLine(ex); + } + } + } + return AllKeys; + } + + /// + /// Get the Entity SetKey collection + /// + /// DbContext type + /// + public static EntitySetKeysDictionary GetEntitySetInfo(Type dbContextType) + { + EntitySetKeysDictionary dic = new EntitySetKeysDictionary(); + foreach (var setKeyInfo in AllKeys.Values.Where(z => z.SenparcEntityTypes.Contains(dbContextType))) + { + if (!dic.ContainsKey(setKeyInfo.DbSetType)) + { + var addSuccess = dic.TryAdd(setKeyInfo.DbSetType, setKeyInfo); + if (!addSuccess) + { + SenparcTrace.BaseExceptionLog(new NcfDatabaseException($"GetEntitySetInfo 发生异常,DbSetType:{setKeyInfo.DbSetType.Name},setKeyInfo:{setKeyInfo.ToJson()}", null, dbContextType)); + } + } + } + return dic; + } + + /// + /// Get the SetKey of all Entities + /// + /// + public static EntitySetKeysDictionary GetAllEntitySetInfo() + { + return AllKeys; + } + } + + + public class SetKeyInfo + { + /// + ///SetKey property name + /// + public string SetName { get; set; } + /// + ///DbSet property type + /// + public Type DbSetType { get; set; } + /// + ///SenparcEntity type + /// + public List SenparcEntityTypes { get; set; } + + public SetKeyInfo(string setName, Type dbSetType, Type senparcEntityType) + { + SetName = setName; + DbSetType = dbSetType; + SenparcEntityTypes = new List() { senparcEntityType }; + } + } + + /// + /// Entity set corresponding to ORM entity class + /// + public class EntitySetKeysDictionary : ConcurrentDictionary + { + public new SetKeyInfo this[Type entityType] + { + get + { + if (!base.ContainsKey(entityType)) + { + throw new Exception($"未找到实体类型:{entityType.FullName}"); + } + return base[entityType]; + } + set + { + base[entityType] = value; + } + } + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Core/Models/ExtensionEntity.Core.cs b/src/Basic/Senparc.Ncf.Core/Models/ExtensionEntity.Core.cs index 094059d9f..35908f1d2 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/ExtensionEntity.Core.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/ExtensionEntity.Core.cs @@ -1,378 +1,378 @@ -using Senparc.CO2NET.Extensions; -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.Core.Models.DataBaseModel; -using Senparc.Ncf.Core.Utility; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Runtime.Serialization; - -//using WURFL; - -namespace Senparc.Ncf.Core.Models -{ - - #region 全局 - - /// - /// 分页 - /// - /// - public class PagedList : List //where T : class /*,new()*/ - { - public PagedList(List list, int pageIndex, int pageCount, int totalCount) : this(list, pageIndex, pageCount, totalCount, null) { } - - public PagedList(List list, int pageIndex, int pageCount, int totalCount, int? skipCount = null) - { - AddRange(list); - PageIndex = pageIndex; - PageCount = pageCount; - TotalCount = totalCount < 0 ? list.Count : totalCount; - SkipCount = skipCount ?? Senparc.Ncf.Core.Utility.Extensions.GetSkipRecord(pageIndex, pageCount); - } - - public int PageIndex { get; set; } - - public int PageCount { get; set; } - - public int TotalCount { get; set; } - - public int SkipCount { get; set; } - - public int TotalPageNumber => Convert.ToInt32((TotalCount - 1) / PageCount) + 1; - } - - /// - /// 网页Meta标签集合 - /// - public class MetaCollection : Dictionary - { - //new public string this[MetaType metaType] - //{ - // get - // { - // if (!this.ContainsKey(metaType)) - // { - // this.Add(metaType, null); - // } - // return this[metaType]; - // } - // set { this[metaType] = value; } - //} - } - - /// - /// 首页图片切换 - /// - public class HomeSlider - { - public int Id { get; set; } - - public string Title { get; set; } - - public string Url { get; set; } - - public string Pic { get; set; } - - public int DisplayOrder { get; set; } - } - - /// - /// 系统配置文件 - /// - [Serializable] - public class SenparcConfig - { - public int Id { get; set; } - - public string Name { get; set; } - - //public string Host { get; set; } - - //public string DataBase { get; set; } - - //public string UserName { get; set; } - - //public string Password { get; set; } - - //public string Provider { get; set; } - - //public string ConnectionString { get; set; } - - public string ConnectionStringFull { get; set; } - - public string ApplicationPath { get; set; } - } - - /// - /// 全局提示消息 - /// - [Serializable] - public class Messager - { - public MessageType MessageType { get; set; } - - public string MessageText { get; set; } - - public bool ShowClose { get; set; } - - public Messager(MessageType messageType, string messageText, bool showClose = true) - { - MessageType = messageType; - MessageText = messageText; - ShowClose = showClose; - } - } - - /// - /// 日志 - /// - public class WebLog - { - public DateTime DateTime { get; set; } - - public string Level { get; set; } - - public string LoggerName { get; set; } - - public string Message { get; set; } - - public string Details { get; set; } - - public string ThreadName { get; set; } - - public int PageIndex { get; set; } - - public int Line { get; set; } - } - - #endregion - - #region 数据库实体扩展 - - #region FullEntity相关 - - public interface IBaseFullEntity - { - void CreateEntity(TEntity entity); - } - - [Serializable] - public abstract class BaseFullEntity : IBaseFullEntity - { - public virtual string Key - { - get; - } - - public virtual void CreateEntity(TEntity entity) - { - FullEntityCache.SetFullEntityCache(this, entity); - } - - /// - /// 创建对象 - /// - /// 对象类型 - /// 实体类型 - /// 实体实例 - /// - public static T CreateEntity(TEntity entity) - where T : BaseFullEntity, - new() - { - T obj = new T(); - obj.CreateEntity(entity); - return obj; - } - - /// - /// 创建对象列表 - /// - /// 对象类型 - /// 试题类型 - /// 实体列表 - /// - public static List CreateList(IEnumerable entityList) - where T : BaseFullEntity, - new() - { - var result = new List(); - foreach (var item in entityList) - { - T obj = BaseFullEntity.CreateEntity(item); - result.Add(obj); - } - return result; - } - } - - #endregion - - - [Serializable] - public partial class FullSystemConfigBase : BaseFullEntity - { - [AutoSetCache] - public string SystemName { get; set; } - - public override void CreateEntity(SystemConfig entity) - { - base.CreateEntity(entity); - } - } - - [Serializable] - public class FullSystemConfig : FullSystemConfigBase - { - [AutoSetCache] - public string MchId { get; set; } - [AutoSetCache] - public string MchKey { get; set; } - [AutoSetCache] - public string TenPayAppId { get; set; } - [AutoSetCache] - public bool? HideModuleManager { get; set; } - - [AutoSetCache] - public int NeuCharDeveloperId { get; set; } - - [AutoSetCache] - public string NeuCharAppKey { get; set; } - - [AutoSetCache] - public string NeuCharAppSecret { get; set; } - - public override void CreateEntity(SystemConfig entity) - { - base.CreateEntity(entity); - } - } - - - [Serializable] - public partial class FullXncfModule : BaseFullEntity - { - [AutoSetCache] - public int Id { get; set; } - [AutoSetCache] - public string Name { get; set; } - [AutoSetCache] - public string Uid { get; set; } - [AutoSetCache] - public string MenuName { get; set; } - [AutoSetCache] - public string Version { get; set; } - [AutoSetCache] - public string Description { get; set; } - [AutoSetCache] - public string UpdateLog { get; set; } - [AutoSetCache] - public bool AllowRemove { get; set; } - [AutoSetCache] - public string MenuId { get; set; } - [AutoSetCache] - public XncfModules_State State { get; set; } - [AutoSetCache] - public DateTime AddTime { get; set; } - [AutoSetCache] - public DateTime LastUpdateTime { get; set; } - - public override void CreateEntity(XncfModule entity) - { - base.CreateEntity(entity); - } - } - - #endregion - - - #region 省、市、区XML数据格式 - [DataContract] - [Serializable] - public class AreaXML_Provinces - { - [DataMember] - public int ID { get; set; } - - [DataMember] - public string ProvinceName { get; set; } - - /// - /// 地区代码 - /// - [DataMember] - public string DivisionsCode { get; set; } - - /// - /// 缩写(去掉“省”“市”“自治区”等) - /// - [DataMember] - public string ShortName { get; set; } - - public AreaXML_Provinces(int id, string provinceName, string divisionsCode, string shortName) - { - this.ID = id; - this.ProvinceName = provinceName; - this.DivisionsCode = divisionsCode; - this.ShortName = shortName; - } - } - - [DataContract] - [Serializable] - public class AreaXML_Cities - { - [DataMember] - public int ID { get; set; } - - [DataMember] - public int PID { get; set; } - - [DataMember] - public string CityName { get; set; } - - [DataMember] - public string ZipCode { get; set; } - - [DataMember] - public string CityCode { get; set; } - - [DataMember] - public int MaxShopId { get; set; } - - public AreaXML_Cities(int id, int pID, string cityName, string zipCode, string cityCode, int maxShopId) - { - this.ID = id; - this.PID = pID; - this.CityName = cityName; - this.ZipCode = zipCode; - this.CityCode = cityCode; - this.MaxShopId = maxShopId; - } - } - - [DataContract] - [Serializable] - public class AreaXML_Districts - { - [DataMember] - public int ID { get; set; } - - [DataMember] - public int CID { get; set; } - - [DataMember] - public string DistrictName { get; set; } - - public AreaXML_Districts(int id, int cID, string districtName) - { - this.ID = id; - this.CID = cID; - this.DistrictName = districtName; - } - } - - #endregion -} - +using Senparc.CO2NET.Extensions; +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.Core.Models.DataBaseModel; +using Senparc.Ncf.Core.Utility; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Runtime.Serialization; + +//using WURFL; + +namespace Senparc.Ncf.Core.Models +{ + + #region 全局 + + /// + ///pagination + /// + /// + public class PagedList : List //where T : class /*,new()*/ + { + public PagedList(List list, int pageIndex, int pageCount, int totalCount) : this(list, pageIndex, pageCount, totalCount, null) { } + + public PagedList(List list, int pageIndex, int pageCount, int totalCount, int? skipCount = null) + { + AddRange(list); + PageIndex = pageIndex; + PageCount = pageCount; + TotalCount = totalCount < 0 ? list.Count : totalCount; + SkipCount = skipCount ?? Senparc.Ncf.Core.Utility.Extensions.GetSkipRecord(pageIndex, pageCount); + } + + public int PageIndex { get; set; } + + public int PageCount { get; set; } + + public int TotalCount { get; set; } + + public int SkipCount { get; set; } + + public int TotalPageNumber => Convert.ToInt32((TotalCount - 1) / PageCount) + 1; + } + + /// + /// Web page Meta tag collection + /// + public class MetaCollection : Dictionary + { + //new public string this[MetaType metaType] + //{ + // get + // { + // if (!this.ContainsKey(metaType)) + // { + // this.Add(metaType, null); + // } + // return this[metaType]; + // } + // set { this[metaType] = value; } + //} + } + + /// + ///Homepage picture switch + /// + public class HomeSlider + { + public int Id { get; set; } + + public string Title { get; set; } + + public string Url { get; set; } + + public string Pic { get; set; } + + public int DisplayOrder { get; set; } + } + + /// + ///system configuration file + /// + [Serializable] + public class SenparcConfig + { + public int Id { get; set; } + + public string Name { get; set; } + + //public string Host { get; set; } + + //public string DataBase { get; set; } + + //public string UserName { get; set; } + + //public string Password { get; set; } + + //public string Provider { get; set; } + + //public string ConnectionString { get; set; } + + public string ConnectionStringFull { get; set; } + + public string ApplicationPath { get; set; } + } + + /// + ///Global prompt message + /// + [Serializable] + public class Messager + { + public MessageType MessageType { get; set; } + + public string MessageText { get; set; } + + public bool ShowClose { get; set; } + + public Messager(MessageType messageType, string messageText, bool showClose = true) + { + MessageType = messageType; + MessageText = messageText; + ShowClose = showClose; + } + } + + /// + /// log + /// + public class WebLog + { + public DateTime DateTime { get; set; } + + public string Level { get; set; } + + public string LoggerName { get; set; } + + public string Message { get; set; } + + public string Details { get; set; } + + public string ThreadName { get; set; } + + public int PageIndex { get; set; } + + public int Line { get; set; } + } + + #endregion + + #region 数据库实体扩展 + + #region FullEntity相关 + + public interface IBaseFullEntity + { + void CreateEntity(TEntity entity); + } + + [Serializable] + public abstract class BaseFullEntity : IBaseFullEntity + { + public virtual string Key + { + get; + } + + public virtual void CreateEntity(TEntity entity) + { + FullEntityCache.SetFullEntityCache(this, entity); + } + + /// + ///Create object + /// + /// Object type + /// Entity type + /// Entity instance + /// + public static T CreateEntity(TEntity entity) + where T : BaseFullEntity, + new() + { + T obj = new T(); + obj.CreateEntity(entity); + return obj; + } + + /// + ///Create object list + /// + /// Object type + /// Test type + /// Entity List + /// + public static List CreateList(IEnumerable entityList) + where T : BaseFullEntity, + new() + { + var result = new List(); + foreach (var item in entityList) + { + T obj = BaseFullEntity.CreateEntity(item); + result.Add(obj); + } + return result; + } + } + + #endregion + + + [Serializable] + public partial class FullSystemConfigBase : BaseFullEntity + { + [AutoSetCache] + public string SystemName { get; set; } + + public override void CreateEntity(SystemConfig entity) + { + base.CreateEntity(entity); + } + } + + [Serializable] + public class FullSystemConfig : FullSystemConfigBase + { + [AutoSetCache] + public string MchId { get; set; } + [AutoSetCache] + public string MchKey { get; set; } + [AutoSetCache] + public string TenPayAppId { get; set; } + [AutoSetCache] + public bool? HideModuleManager { get; set; } + + [AutoSetCache] + public int NeuCharDeveloperId { get; set; } + + [AutoSetCache] + public string NeuCharAppKey { get; set; } + + [AutoSetCache] + public string NeuCharAppSecret { get; set; } + + public override void CreateEntity(SystemConfig entity) + { + base.CreateEntity(entity); + } + } + + + [Serializable] + public partial class FullXncfModule : BaseFullEntity + { + [AutoSetCache] + public int Id { get; set; } + [AutoSetCache] + public string Name { get; set; } + [AutoSetCache] + public string Uid { get; set; } + [AutoSetCache] + public string MenuName { get; set; } + [AutoSetCache] + public string Version { get; set; } + [AutoSetCache] + public string Description { get; set; } + [AutoSetCache] + public string UpdateLog { get; set; } + [AutoSetCache] + public bool AllowRemove { get; set; } + [AutoSetCache] + public string MenuId { get; set; } + [AutoSetCache] + public XncfModules_State State { get; set; } + [AutoSetCache] + public DateTime AddTime { get; set; } + [AutoSetCache] + public DateTime LastUpdateTime { get; set; } + + public override void CreateEntity(XncfModule entity) + { + base.CreateEntity(entity); + } + } + + #endregion + + + #region 省、市、区XML数据格式 + [DataContract] + [Serializable] + public class AreaXML_Provinces + { + [DataMember] + public int ID { get; set; } + + [DataMember] + public string ProvinceName { get; set; } + + /// + ///area code + /// + [DataMember] + public string DivisionsCode { get; set; } + + /// + /// Abbreviation (remove "province", "city", "autonomous region", etc.) + /// + [DataMember] + public string ShortName { get; set; } + + public AreaXML_Provinces(int id, string provinceName, string divisionsCode, string shortName) + { + this.ID = id; + this.ProvinceName = provinceName; + this.DivisionsCode = divisionsCode; + this.ShortName = shortName; + } + } + + [DataContract] + [Serializable] + public class AreaXML_Cities + { + [DataMember] + public int ID { get; set; } + + [DataMember] + public int PID { get; set; } + + [DataMember] + public string CityName { get; set; } + + [DataMember] + public string ZipCode { get; set; } + + [DataMember] + public string CityCode { get; set; } + + [DataMember] + public int MaxShopId { get; set; } + + public AreaXML_Cities(int id, int pID, string cityName, string zipCode, string cityCode, int maxShopId) + { + this.ID = id; + this.PID = pID; + this.CityName = cityName; + this.ZipCode = zipCode; + this.CityCode = cityCode; + this.MaxShopId = maxShopId; + } + } + + [DataContract] + [Serializable] + public class AreaXML_Districts + { + [DataMember] + public int ID { get; set; } + + [DataMember] + public int CID { get; set; } + + [DataMember] + public string DistrictName { get; set; } + + public AreaXML_Districts(int id, int cID, string districtName) + { + this.ID = id; + this.CID = cID; + this.DistrictName = districtName; + } + } + + #endregion +} + diff --git a/src/Basic/Senparc.Ncf.Core/Models/IValidatorEnvironment.cs b/src/Basic/Senparc.Ncf.Core/Models/IValidatorEnvironment.cs index 3af5d7d2a..6ee489d4d 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/IValidatorEnvironment.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/IValidatorEnvironment.cs @@ -1,24 +1,24 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// 作为需要进行视图验证的基础接口(如Controller、PageModel) - /// - public interface IValidatorEnvironment - { - /// - /// Controller 及 PageModel 中的 ModelState 对象 - /// - ModelStateDictionary ModelState { get; } - - /// - /// HttpContext - /// - HttpContext HttpContext { get; } - } -} +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Models +{ + /// + /// As a basic interface that requires view verification (such as Controller, PageModel) + /// + public interface IValidatorEnvironment + { + /// + /// ModelState objects in Controller and PageModel + /// + ModelStateDictionary ModelState { get; } + + /// + /// HttpContext + /// + HttpContext HttpContext { get; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/DatabaseConfigurationFactory.cs b/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/DatabaseConfigurationFactory.cs index 180a97260..aedf7b509 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/DatabaseConfigurationFactory.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/DatabaseConfigurationFactory.cs @@ -3,7 +3,7 @@ namespace Senparc.Ncf.Core.Models { /// - /// 多数据库配置工厂 + ///Multi-database configuration factory /// public class DatabaseConfigurationFactory { @@ -12,7 +12,7 @@ public class DatabaseConfigurationFactory DatabaseConfigurationFactory() { } /// - /// DatabaseConfigurationFactory 的全局单例 + ///Global singleton of DatabaseConfigurationFactory /// public static DatabaseConfigurationFactory Instance { @@ -31,7 +31,7 @@ static Nested() { } #endregion - //TODO:如果是分布式,需要存储到缓存中 + //TODO: If it is distributed, it needs to be stored in the cache. private IDatabaseConfiguration _currentDatabaseConfiguration; @@ -53,7 +53,7 @@ public IDatabaseConfiguration Current ///// - ///// 给 design time(设计时)操作数据库(如migration)使用。指定当前正在操作的 XNCF 数据库信息(如果是直接继承自 DbContext 的类,需要模拟此参数) + ///// Used for design time (design time) operation database (such as migration). Specify the XNCF database information currently being operated (if it is a class directly inherited from DbContext, this parameter needs to be simulated) ///// //public XncfDatabaseData CurrentXncfDatabaseData { get; set; } } diff --git a/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/Helpers/NcfDatabaseMigrationHelper.cs b/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/Helpers/NcfDatabaseMigrationHelper.cs index c5397d687..c82ea81a7 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/Helpers/NcfDatabaseMigrationHelper.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/Helpers/NcfDatabaseMigrationHelper.cs @@ -1,48 +1,48 @@ -using Senparc.CO2NET.Extensions; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// NCF 数据库帮助类 - /// - public static class NcfDatabaseMigrationHelper - { - /// - /// 系统表的数据库前缀,使用此前缀,将使用 __EFMigrationsHistory 表储存迁移记录,取代模块自定义表 - /// - public const string SYSTEM_UNIQUE_PREFIX = "SYSTEM"; - - /// - /// 获取 EF Code First MigrationHistory 数据库表名 - /// - /// - public static string GetDatabaseMigrationHistoryTableName(string databaseUniquePrefix) - { - if (!databaseUniquePrefix.IsNullOrWhiteSpace()) - { - if (databaseUniquePrefix == SYSTEM_UNIQUE_PREFIX) - { - return "__EFMigrationsHistory"; - } - return "__" + databaseUniquePrefix + "_EFMigrationsHistory"; - } - else - { - //也可以抛出异常 - return "__" + "Unknown" + "_EFMigrationsHistory"; - } - } - - /// - /// 获取 EF Code First MigrationHistory 数据库表名 - /// - /// - public static string GetDatabaseMigrationHistoryTableName(IXncfDatabase xncfDatabaseRegister) - { - return GetDatabaseMigrationHistoryTableName(xncfDatabaseRegister.DatabaseUniquePrefix); - } - } -} +using Senparc.CO2NET.Extensions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Models +{ + /// + ///NCF database help class + /// + public static class NcfDatabaseMigrationHelper + { + /// + /// The database prefix of the system table. Using this prefix, the __EFMigrationsHistory table will be used to store migration records instead of the module custom table. + /// + public const string SYSTEM_UNIQUE_PREFIX = "SYSTEM"; + + /// + /// Get EF Code First MigrationHistory database table name + /// + /// + public static string GetDatabaseMigrationHistoryTableName(string databaseUniquePrefix) + { + if (!databaseUniquePrefix.IsNullOrWhiteSpace()) + { + if (databaseUniquePrefix == SYSTEM_UNIQUE_PREFIX) + { + return "__EFMigrationsHistory"; + } + return "__" + databaseUniquePrefix + "_EFMigrationsHistory"; + } + else + { + //Exceptions can also be thrown + return "__" + "Unknown" + "_EFMigrationsHistory"; + } + } + + /// + /// Get EF Code First MigrationHistory database table name + /// + /// + public static string GetDatabaseMigrationHistoryTableName(IXncfDatabase xncfDatabaseRegister) + { + return GetDatabaseMigrationHistoryTableName(xncfDatabaseRegister.DatabaseUniquePrefix); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/IDatabaseConfiguration.cs b/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/IDatabaseConfiguration.cs index 2bb5dfae3..06d44143f 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/IDatabaseConfiguration.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/IDatabaseConfiguration.cs @@ -1,73 +1,73 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using System; -using System.Data.Common; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// 数据库配置接口 - /// 官方推荐的数据库提供程序:https://docs.microsoft.com/zh-cn/ef/core/providers/?tabs=dotnet-core-cli - /// DbContextOptionsBuilder 的类型,如:SQL Server 使用 SqlServerDbContextOptionsBuilder - /// - public interface IDatabaseConfiguration : IDatabaseConfiguration - where TBuilder : RelationalDbContextOptionsBuilder - where TExtension : RelationalOptionsExtension, new() - { - - } - - /// - /// 数据库配置接口 - /// - public interface IDatabaseConfiguration - { - /// - /// 数据库类型 - /// - MultipleDatabaseType MultipleDatabaseType { get; } - - /// - /// 操作 DbContextOptions 的基础方法 - /// - Action DbContextOptionsActionBase { get; } - - /// - /// 操作 DbContextOptions 的扩展方法 - /// - Action DbContextOptionsActionExtension { get; } - - /// - /// 设定 UseSLite、UseSQLServer 等方法 - /// - Action> SetUseDatabase { get; } - - /// - /// 使用数据库,如: - /// var builder = new DbContextOptionsBuilder<TDbContext>(); builder.UseSqlServer(sqlConnection, DbContextOptionsAction); - /// - /// DbContextOptionsBuilder - /// 连接字符串 - /// 额外配置操作 - /// IXncfDatabase 信息(仅在针对 XNCF 进行数据库迁移时有效) - void UseDatabase(DbContextOptionsBuilder builder, /*IRelationalDbContextOptionsBuilderInfrastructure optionsBuilder,*/ string connectionString, - XncfDatabaseData xncfDatabaseData = null, Action dbContextOptionsAction = null); - - /// - /// 备份数据库方法 - /// 如果返回null,则在方法内部完成备份程序 - /// - /// - /// - string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath); - - /// - /// 删除指定表Sql - /// 如果返回null,则在方法内部完成删除操作 - /// - /// - /// - /// - string GetDropTableSql(DbContext dbContext, string tableName); - } +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using System; +using System.Data.Common; + +namespace Senparc.Ncf.Core.Models +{ + /// + ///Database configuration interface + /// Officially recommended database provider: https://docs.microsoft.com/zh-cn/ef/core/providers/?tabs=dotnet-core-cli + /// The type of DbContextOptionsBuilder, such as: SQL Server uses SqlServerDbContextOptionsBuilder + /// + public interface IDatabaseConfiguration : IDatabaseConfiguration + where TBuilder : RelationalDbContextOptionsBuilder + where TExtension : RelationalOptionsExtension, new() + { + + } + + /// + ///Database configuration interface + /// + public interface IDatabaseConfiguration + { + /// + /// database type + /// + MultipleDatabaseType MultipleDatabaseType { get; } + + /// + /// Basic methods for operating DbContextOptions + /// + Action DbContextOptionsActionBase { get; } + + /// + /// Extension method to operate DbContextOptions + /// + Action DbContextOptionsActionExtension { get; } + + /// + /// Set methods such as UseSLite and UseSQLServer + /// + Action> SetUseDatabase { get; } + + /// + /// Use a database, such as: + /// var builder = new DbContextOptionsBuilder<TDbContext>(); builder.UseSqlServer(sqlConnection, DbContextOptionsAction); + /// + /// DbContextOptionsBuilder + /// Connection string + /// Additional configuration operations + /// IXncfDatabase information (valid only when doing database migration for XNCF) + void UseDatabase(DbContextOptionsBuilder builder, /*IRelationalDbContextOptionsBuilderInfrastructure optionsBuilder,*/ string connectionString, + XncfDatabaseData xncfDatabaseData = null, Action dbContextOptionsAction = null); + + /// + /// Backup database method + /// If null is returned, the backup procedure is completed inside the method + /// + /// + /// + string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath); + + /// + /// Delete the specified table Sql + /// If null is returned, the deletion operation is completed inside the method + /// + /// + /// + /// + string GetDropTableSql(DbContext dbContext, string tableName); + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/MultipleDatabaseType.cs b/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/MultipleDatabaseType.cs index da1ab6311..374f08a37 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/MultipleDatabaseType.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/MultipleDatabaseType.cs @@ -1,24 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// 多数据库系统中,特定的某个数据库类型 - /// - public enum MultipleDatabaseType - { - Sqlite, - SqlServer, - MySql, - InMemory, - AzureCosmos, - Oracle, - PostgreSQL, - Dm, - Other = 99999, - //UnitTest = 100000, - //TODO:更多 - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Models +{ + /// + /// In a multi-database system, a specific database type + /// + public enum MultipleDatabaseType + { + Sqlite, + SqlServer, + MySql, + InMemory, + AzureCosmos, + Oracle, + PostgreSQL, + Dm, + Other = 99999, + //UnitTest = 100000, + //TODO:More + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/XncfModules/IXncfDatabase.cs b/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/XncfModules/IXncfDatabase.cs index ae32be293..078407be8 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/XncfModules/IXncfDatabase.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/XncfModules/IXncfDatabase.cs @@ -1,52 +1,52 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.Extensions.DependencyInjection; -using Senparc.Ncf.Core.Exceptions; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// XNCF 模块数据库配置 - /// - public interface IXncfDatabase - { - /// - /// 数据库表全局唯一的前缀,务必避免和其他模块重复 - /// - string DatabaseUniquePrefix { get; } - /// - /// 创建数据库模型 - /// - void OnModelCreating(ModelBuilder modelBuilder); - - ///// - ///// 设置数据库,主要提供给使用 - ///// - ///// - ///// MigrationsAssembly 的程序集名称,如果为 null,为默认使用当前 XncfDatabaseDbContextType 所在的程序集 - //void DbContextOptionsAction(IRelationalDbContextOptionsBuilderInfrastructure dbContextOptionsAction, - // string assemblyName = null); - - ///// - ///// XncfDatabaseDbContext 类型 - ///// - //Type XncfDatabaseDbContextType { get; } - - /// - /// 添加数据库模块 - /// - /// - void AddXncfDatabaseModule(IServiceCollection services); - - - /// - /// 尝试获取 当前配置下的数据库上下文类型,如果未找到对应数据库,会抛出异常:) - /// - /// - /// - Type TryGetXncfDatabaseDbContextType { get; } - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; +using Senparc.Ncf.Core.Exceptions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Models +{ + /// + ///XNCF module database configuration + /// + public interface IXncfDatabase + { + /// + /// Globally unique prefix for database tables, be sure to avoid duplication with other modules + /// + string DatabaseUniquePrefix { get; } + /// + ///Create database model + /// + void OnModelCreating(ModelBuilder modelBuilder); + + ///// + ///// Set up the database, mainly provided for use + ///// + ///// + ///// The assembly name of MigrationsAssembly. If it is null, the assembly where the current XncfDatabaseDbContextType is located will be used by default + //void DbContextOptionsAction(IRelationalDbContextOptionsBuilderInfrastructure dbContextOptionsAction, + // string assemblyName = null); + + ///// + ///// XncfDatabaseDbContext type + ///// + //Type XncfDatabaseDbContextType { get; } + + /// + ///Add database module + /// + /// + void AddXncfDatabaseModule(IServiceCollection services); + + + /// + /// Try to obtain the database context type under the current configuration. If the corresponding database is not found, an exception will be thrown: ) + /// + /// + /// + Type TryGetXncfDatabaseDbContextType { get; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/XncfModules/XncfDatabaseData.cs b/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/XncfModules/XncfDatabaseData.cs index 9f127df49..49508e271 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/XncfModules/XncfDatabaseData.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/MultipleMigrationDbContext/XncfModules/XncfDatabaseData.cs @@ -1,33 +1,33 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// XNCF 数据库模块信息,仅在单独操作特定 XNCF 数据库模块时有用,其他情况下对象可能为 null - /// - public class XncfDatabaseData - { - public XncfDatabaseData(IXncfDatabase xncfDatabaseRegister, string assemblyName) - { - XncfDatabaseRegister = xncfDatabaseRegister; - AssemblyName = assemblyName; - } - - /// - /// DbContext 类型 - /// - public IXncfDatabase XncfDatabaseRegister { get; set; } - - /// - /// 指定程序集名称 - /// - public string AssemblyName { get; set; } - - /// - /// Migration History 表名 - /// - public string DatabaseMigrationHistoryTableName => NcfDatabaseMigrationHelper.GetDatabaseMigrationHistoryTableName(XncfDatabaseRegister.DatabaseUniquePrefix); - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Models +{ + /// + /// XNCF database module information, only useful when operating a specific XNCF database module alone, the object may be null in other cases + /// + public class XncfDatabaseData + { + public XncfDatabaseData(IXncfDatabase xncfDatabaseRegister, string assemblyName) + { + XncfDatabaseRegister = xncfDatabaseRegister; + AssemblyName = assemblyName; + } + + /// + ///DbContext type + /// + public IXncfDatabase XncfDatabaseRegister { get; set; } + + /// + ///Specify the assembly name + /// + public string AssemblyName { get; set; } + + /// + ///Migration History table name + /// + public string DatabaseMigrationHistoryTableName => NcfDatabaseMigrationHelper.GetDatabaseMigrationHistoryTableName(XncfDatabaseRegister.DatabaseUniquePrefix); + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/NcfDbData/NcfDbData.cs b/src/Basic/Senparc.Ncf.Core/Models/NcfDbData/NcfDbData.cs index cba648f5a..a7e31f1f6 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/NcfDbData/NcfDbData.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/NcfDbData/NcfDbData.cs @@ -1,31 +1,31 @@ -using Microsoft.EntityFrameworkCore; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// INcfDbData - /// - public interface INcfDbData - { - /// - /// 强制手动更改DetectChange - /// - bool ManualDetectChangeObject { get; set; } - DbContext BaseDataContext { get; } - void CloseConnection(); - } - - /// - /// NcfDbData,NCF 的数据库上下文封装,基础类 - /// - public abstract class NcfDbData : INcfDbData - { - /// - /// 强制手动更改DetectChange - /// - public virtual bool ManualDetectChangeObject { get; set; } - public abstract DbContext BaseDataContext { get; } - - public abstract void CloseConnection(); - } -} +using Microsoft.EntityFrameworkCore; + +namespace Senparc.Ncf.Core.Models +{ + /// + /// INcfDbData + /// + public interface INcfDbData + { + /// + /// Force manual change of DetectChange + /// + bool ManualDetectChangeObject { get; set; } + DbContext BaseDataContext { get; } + void CloseConnection(); + } + + /// + /// NcfDbData, NCF database context encapsulation, base class + /// + public abstract class NcfDbData : INcfDbData + { + /// + /// Force manual change of DetectChange + /// + public virtual bool ManualDetectChangeObject { get; set; } + public abstract DbContext BaseDataContext { get; } + + public abstract void CloseConnection(); + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/SenparcCoreSetting.Core.cs b/src/Basic/Senparc.Ncf.Core/Models/SenparcCoreSetting.Core.cs index 4e07bf348..4f71e9360 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/SenparcCoreSetting.Core.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/SenparcCoreSetting.Core.cs @@ -1,69 +1,69 @@ -using Senparc.CO2NET; -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.Core.MultiTenant; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// 全局可调整配置的设置 - /// - public partial record class SenparcCoreSetting - { - /// - /// 网站是否开启 Debug 标记 - /// - public bool IsDebug { get; set; } - /// - /// 是否是测试站 - /// - public bool IsTestSite { get; set; } - - /// - /// 对应:AppData/Database/SenparcConfig.config 中,所需要使用的数据库连接的 <SenparcConfig> 节点的 Name - /// - public string DatabaseName { get; set; } - - /// - /// 通过配置指定数据库类型。必须因为 MultipleDatabaseType 枚举类型,请注意大小写。 - /// - public MultipleDatabaseType? DatabaseType { get; set; } - - /// - /// 缓存类型 - /// - public CacheType CacheType { get; set; } - - #region 多租户 - - /// - /// 是否启用多租户,默认为 false - /// - public bool EnableMultiTenant { get; set; } - /// - /// 区分租户的规则 - /// - public TenantRule TenantRule { get; set; } = TenantRule.Default; - - #endregion - - /// - /// MemcachedAddresses - /// - public string MemcachedAddresses { get; set; } - - /// - /// 缓存中的请求暂存日志缓存时间(分钟),0 则不缓存 - /// - public int RequestTempLogCacheMinutes { get; set; } - - /// - /// 密码加密加强选项,此值在首个账号生成后不修改,否则会导致所有密码失效 - /// - public string PasswordSaltToken { get; set; } - - /// - /// MCP 访问令牌,用于 SSE 连接鉴权 - /// - public string McpAccessToken { get; set; } - } -} +using Senparc.CO2NET; +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.Core.MultiTenant; + +namespace Senparc.Ncf.Core.Models +{ + /// + /// Globally adjustable configuration settings + /// + public partial record class SenparcCoreSetting + { + /// + /// Whether the website has the Debug flag turned on + /// + public bool IsDebug { get; set; } + /// + /// Is it a test station? + /// + public bool IsTestSite { get; set; } + + /// + /// Corresponding: In AppData/Database/SenparcConfig.config, the Name of the <SenparcConfig> node of the database connection that needs to be used + /// + public string DatabaseName { get; set; } + + /// + /// Specify the database type through configuration. Required because of the MultipleDatabaseType enumeration type, please note the case. + /// + public MultipleDatabaseType? DatabaseType { get; set; } + + /// + /// cache type + /// + public CacheType CacheType { get; set; } + + #region 多租户 + + /// + /// Whether to enable multi-tenancy, the default is false + /// + public bool EnableMultiTenant { get; set; } + /// + /// Rules for distinguishing tenants + /// + public TenantRule TenantRule { get; set; } = TenantRule.Default; + + #endregion + + /// + /// MemcachedAddresses + /// + public string MemcachedAddresses { get; set; } + + /// + /// Request temporary log cache time in cache (minutes), 0 means no caching + /// + public int RequestTempLogCacheMinutes { get; set; } + + /// + /// Password encryption strengthening option, this value will not be modified after the first account is generated, otherwise all passwords will become invalid. + /// + public string PasswordSaltToken { get; set; } + + /// + ///MCP access token, used for SSE connection authentication + /// + public string McpAccessToken { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/SenparcEntities/ISenparcEntitiesDbContext.cs b/src/Basic/Senparc.Ncf.Core/Models/SenparcEntities/ISenparcEntitiesDbContext.cs index 3dc147bf1..5ee830874 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/SenparcEntities/ISenparcEntitiesDbContext.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/SenparcEntities/ISenparcEntitiesDbContext.cs @@ -1,33 +1,33 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Internal; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.Models -{ - /// - /// ISenparcEntities - /// - public interface ISenparcEntitiesDbContext : IDisposable /*, IInfrastructure,*/ /*IDbContextDependencies,*/ /*IDbSetCache*/ /*IDbQueryCache, *//*,IDbContextPoolable*/ - { - /// - /// 当前上下文中租户信息 - /// - MultiTenant.RequestTenantInfo TenantInfo { get; set; } - - void SetGlobalQuery(ModelBuilder builder) where T : EntityBase; - - /// - /// 重置合并状态 - /// - void ResetMigrate(); - - /// - /// 执行 EF Core 的合并操作(等价于 update-database) - /// 出于安全考虑,每次执行 Migrate() 方法之前,必须先执行 ResetMigrate() 开启允许 Migrate 执行的状态。 - /// - void Migrate(); - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Internal; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.Models +{ + /// + /// ISenparcEntities + /// + public interface ISenparcEntitiesDbContext : IDisposable /*, IInfrastructure,*/ /*IDbContextDependencies,*/ /*IDbSetCache*/ /*IDbQueryCache, *//*,IDbContextPoolable*/ + { + /// + /// Tenant information in the current context + /// + MultiTenant.RequestTenantInfo TenantInfo { get; set; } + + void SetGlobalQuery(ModelBuilder builder) where T : EntityBase; + + /// + ///Reset merge status + /// + void ResetMigrate(); + + /// + /// Perform the merge operation of EF Core (equivalent to update-database) + /// For security reasons, before each execution of the Migrate() method, ResetMigrate() must be executed to enable the state that allows Migrate execution. + /// + void Migrate(); + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Models/SenparcEntities/SenparcEntitiesDbContextBase.cs b/src/Basic/Senparc.Ncf.Core/Models/SenparcEntities/SenparcEntitiesDbContextBase.cs index 84437c4af..a04a3556b 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/SenparcEntities/SenparcEntitiesDbContextBase.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/SenparcEntities/SenparcEntitiesDbContextBase.cs @@ -19,7 +19,7 @@ namespace Senparc.Ncf.Core.Models { /// - /// SenparcEntities 的基类 + /// Base class for SenparcEntities /// public abstract class SenparcEntitiesDbContextBase : DbContext, ISenparcEntitiesDbContext { @@ -29,7 +29,7 @@ public abstract class SenparcEntitiesDbContextBase : DbContext, ISenparcEntities private RequestTenantInfo _tenantInfo; /// - /// SenparcDI 储存的 GlobalServiceCollection 生成的 ServiceProvider + /// ServiceProvider generated by GlobalServiceCollection stored in SenparcDI /// protected virtual IServiceProvider ServiceProvider { @@ -49,7 +49,7 @@ protected virtual IServiceProvider ServiceProvider } /// - /// 当前上下文中租户信息 + /// Tenant information in the current context /// public RequestTenantInfo TenantInfo { @@ -68,7 +68,7 @@ public RequestTenantInfo TenantInfo } /// - /// 自动添加多租户Id + /// Automatically add multi-tenant ID /// private void AddTenandId() { @@ -86,7 +86,7 @@ private void AddTenandId() { if (!(entity is IIgnoreMulitTenant) && (entity is IMultiTenancy multiTenantEntity)) { - ////如果未设置,则进行设定 + ////If not set, set it //requestTenantInfo = requestTenantInfo ?? MultiTenantHelper.TryGetAndCheckRequestTenantInfo(ServiceProvider, "SenparcEntitiesDbContextBase.AddTenandId()", this); multiTenantEntity.TenantId = requestTenantInfo.Id; } @@ -97,7 +97,7 @@ private void AddTenandId() public bool? _enableMultiTenant; /// - /// 是否启用多租户,默认读取 SiteConfig.SenparcCoreSetting.EnableMultiTenant + /// Whether to enable multi-tenancy, the default is to read SiteConfig.SenparcCoreSetting.EnableMultiTenant /// public bool EnableMultiTenant { @@ -131,8 +131,8 @@ public SenparcEntitiesDbContextBase(DbContextOptions options, IServiceProvider s #region Migration 迁移相关方法 /// - /// 执行 EF Core 的合并操作(等价于 update-database) - /// 出于安全考虑,每次执行 Migrate() 方法之前,必须先执行 ResetMigrate() 开启允许 Migrate 执行的状态。 + /// Perform the merge operation of EF Core (equivalent to update-database) + /// For security reasons, before each execution of the Migrate() method, ResetMigrate() must be executed to enable the state that allows Migrate execution. /// public virtual void Migrate() { @@ -150,7 +150,7 @@ public virtual void Migrate() } /// - /// 重置合并状态 + ///Reset merge status /// public virtual void ResetMigrate() { @@ -160,7 +160,7 @@ public virtual void ResetMigrate() #endregion /// - /// 【核心】 将当前动态模块的 DbContext 对象注入到 DbContext + /// [Core] Inject the DbContext object of the current dynamic module into the DbContext /// /// protected override void OnModelCreating(ModelBuilder modelBuilder) @@ -177,7 +177,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) Console.WriteLine($"------|{new string('-', maxLength)}"); for (int i = 0; i < types.Count; i++) { - //设置需要添加的全局查询 + //Set the global query that needs to be added var entityType = types[i]; Console.WriteLine($" {(i + 1).ToString().PadRight(4)}| {entityType.Name}"); @@ -194,14 +194,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) } /// - /// 设置全局查询 + ///Set global query /// private static readonly MethodInfo SetGlobalQueryMethodInfo = typeof(SenparcEntitiesBase) .GetMethods(BindingFlags.Public | BindingFlags.Instance) .Single(t => t.IsGenericMethod && t.Name == nameof(SetGlobalQuery) /*"SetGlobalQuery"*/); /// - /// 全局查询 + /// Global query /// /// /// @@ -209,22 +209,22 @@ public virtual void SetGlobalQuery(ModelBuilder builder) where T : EntityBase { var entityBuilder = builder.Entity();//.HasQueryFilter(z => !z.Flag); - //多租户 + //multi-tenant //Console.WriteLine($"\t DbContext:{this.GetHashCode()} \tSetGlobalQuery<{typeof(T).Name}> this.EnableMultiTenant:" + this.EnableMultiTenant + $" / SiteConfig.SenparcCoreSetting.EnableMultiTenant:{SiteConfig.SenparcCoreSetting.EnableMultiTenant}"); if (this.EnableMultiTenant && typeof(IMultiTenancy).IsAssignableFrom(typeof(T)) && !(typeof(IIgnoreMulitTenant).IsAssignableFrom(typeof(T)))) { - //多租户 + 软删除 + //Multi-tenancy + soft delete entityBuilder.HasQueryFilter(z => z.TenantId == TenantInfo.Id && !z.Flag); } else { - //仅软删除 + //Soft delete only entityBuilder.HasQueryFilter(z => !z.Flag); } } /// - /// 设置当前 DbContext 是否启用上下文 + /// Set whether the current DbContext enables context /// /// public void SetMultiTenantEnable(bool enable) @@ -234,7 +234,7 @@ public void SetMultiTenantEnable(bool enable) } /// - /// 多租户状态重置为 SiteConfig.SenparcCoreSetting.EnableMultiTenant + ///Multi-tenant status reset to SiteConfig.SenparcCoreSetting.EnableMultiTenant /// public void ResetMultiTenantEnable() { @@ -260,7 +260,7 @@ public override Task SaveChangesAsync(bool acceptAllChangesOnSuccess, Cance //public override Task SaveChangesAsync(CancellationToken cancellationToken = default) //{ - // return base.SaveChangesAsync(cancellationToken);//底层引用的就是 SaveChangesAsync,所以不用处理 + // return base.SaveChangesAsync(cancellationToken);//The underlying reference is SaveChangesAsync, so there is no need to process it //} } } diff --git a/src/Basic/Senparc.Ncf.Core/Models/VD/CommonVD.cs b/src/Basic/Senparc.Ncf.Core/Models/VD/CommonVD.cs index 222878f1d..d4a73f6d9 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/VD/CommonVD.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/VD/CommonVD.cs @@ -1,94 +1,94 @@ -using Senparc.Ncf.Core.Models; -using System; - -namespace Senparc.Ncf.Core.Models.VD -{ - /// - /// 地区数据(省、市、区) - /// - public class Common_AreaListVD : Base_AreaXmlVD - { - /// - /// ID,Name前缀 - /// - public string Prefixes { get; set; } - - public string UserProvince { get; set; } - - public string UserCity { get; set; } - - public string UserDistrict { get; set; } - - public AreaXML_Provinces TopProvince { get; set; } - - public AreaXML_Cities TopCity { get; set; } - - public AreaXML_Districts TopDistrict { get; set; } - - public bool HideDistrict { get; set; } - - public bool ShowProvinceAllButton { get; set; } - - public bool ShowCityAllButton { get; set; } - - public bool ShowDistrictAllButton { get; set; } - - public Common_AreaListVD() { } - - public Common_AreaListVD(Base_AreaXmlVD areaInfo, string userProvince, string userCity, string userDistrict) - : this(null, areaInfo, userProvince, userCity, userDistrict) - { } - - public Common_AreaListVD(string prefixes, Base_AreaXmlVD areaInfo, string userProvince, string userCity, string userDistrict, AreaXML_Provinces topProvince = null, AreaXML_Cities topCity = null, AreaXML_Districts topDistrict = null, bool hideDistrict = false, bool showProvinceAllButton = false, bool showCityAllButton = false) - { - Prefixes = prefixes; - - Provinces = areaInfo.Provinces; - Cities = areaInfo.Cities; - Districts = areaInfo.Districts; - - UserProvince = userProvince; - UserCity = userCity; - UserDistrict = userDistrict; - - //如果首项为空,则ID设为-1 - topProvince = topProvince ?? new AreaXML_Provinces(-1, "", "", ""); - topCity = topCity ?? new AreaXML_Cities(-1, 0, "", "", "", 0); - topDistrict = topDistrict ?? new AreaXML_Districts(-1, 0, ""); - - TopProvince = topProvince; - TopCity = topCity; - TopDistrict = topDistrict; - - HideDistrict = hideDistrict; - ShowProvinceAllButton = showProvinceAllButton; - ShowCityAllButton = showCityAllButton; - } - } - - - public class Common_AccountTypeListVD - { - public decimal AgentAreaRate { get; set; } - - public int AccountId { get; set; } - - //public List UserTypeList { get; set; } - - public int AccountTypeId { get; set; } - - public decimal AccountTypePrice { get; set; } - - public DateTime AccountTypeExpireTime { get; set; } - - public bool ShowUpgradeButton { get; set; } - - public bool AccountTypeOnOffer { get; set; } - - public string PostAction { get; set; } - - public string PostController { get; set; } - - public int MaxAppCount { get; set; } - } +using Senparc.Ncf.Core.Models; +using System; + +namespace Senparc.Ncf.Core.Models.VD +{ + /// + ///Regional data (province, city, district) + /// + public class Common_AreaListVD : Base_AreaXmlVD + { + /// + ///ID,Name prefix + /// + public string Prefixes { get; set; } + + public string UserProvince { get; set; } + + public string UserCity { get; set; } + + public string UserDistrict { get; set; } + + public AreaXML_Provinces TopProvince { get; set; } + + public AreaXML_Cities TopCity { get; set; } + + public AreaXML_Districts TopDistrict { get; set; } + + public bool HideDistrict { get; set; } + + public bool ShowProvinceAllButton { get; set; } + + public bool ShowCityAllButton { get; set; } + + public bool ShowDistrictAllButton { get; set; } + + public Common_AreaListVD() { } + + public Common_AreaListVD(Base_AreaXmlVD areaInfo, string userProvince, string userCity, string userDistrict) + : this(null, areaInfo, userProvince, userCity, userDistrict) + { } + + public Common_AreaListVD(string prefixes, Base_AreaXmlVD areaInfo, string userProvince, string userCity, string userDistrict, AreaXML_Provinces topProvince = null, AreaXML_Cities topCity = null, AreaXML_Districts topDistrict = null, bool hideDistrict = false, bool showProvinceAllButton = false, bool showCityAllButton = false) + { + Prefixes = prefixes; + + Provinces = areaInfo.Provinces; + Cities = areaInfo.Cities; + Districts = areaInfo.Districts; + + UserProvince = userProvince; + UserCity = userCity; + UserDistrict = userDistrict; + + //If the first item is empty, the ID is set to -1 + topProvince = topProvince ?? new AreaXML_Provinces(-1, "", "", ""); + topCity = topCity ?? new AreaXML_Cities(-1, 0, "", "", "", 0); + topDistrict = topDistrict ?? new AreaXML_Districts(-1, 0, ""); + + TopProvince = topProvince; + TopCity = topCity; + TopDistrict = topDistrict; + + HideDistrict = hideDistrict; + ShowProvinceAllButton = showProvinceAllButton; + ShowCityAllButton = showCityAllButton; + } + } + + + public class Common_AccountTypeListVD + { + public decimal AgentAreaRate { get; set; } + + public int AccountId { get; set; } + + //public List UserTypeList { get; set; } + + public int AccountTypeId { get; set; } + + public decimal AccountTypePrice { get; set; } + + public DateTime AccountTypeExpireTime { get; set; } + + public bool ShowUpgradeButton { get; set; } + + public bool AccountTypeOnOffer { get; set; } + + public string PostAction { get; set; } + + public string PostController { get; set; } + + public int MaxAppCount { get; set; } + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Core/Models/VD/Razor/PageModelBase.cs b/src/Basic/Senparc.Ncf.Core/Models/VD/Razor/PageModelBase.cs index 04fcd0f38..99fe454cd 100644 --- a/src/Basic/Senparc.Ncf.Core/Models/VD/Razor/PageModelBase.cs +++ b/src/Basic/Senparc.Ncf.Core/Models/VD/Razor/PageModelBase.cs @@ -1,151 +1,151 @@ -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.Routing; -//using Microsoft.Data.SqlClient; -using Senparc.CO2NET; -using Senparc.CO2NET.Extensions; -using Senparc.Ncf.Core.Cache; -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Core.Models.VD; -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using System.Data.Common; -using Senparc.Ncf.Core.Config; - -namespace Senparc.Ncf.Core.Models.VD -{ - public interface IPageModelBase : IBaseUiVD, IValidatorEnvironment - { - RouteData RouteData { get; set; } - } - - - public class PageModelBase : PageModel, IPageModelBase - { - public FullSystemConfig FullSystemConfig { get; set; } - - public MetaCollection MetaCollection { get; set; } - - public string UserName { get; set; } - - public bool IsAdmin { get; set; } - - public new RouteData RouteData { get => base.RouteData; set => throw new NotImplementedException(); } - - //public RouteData RouteData { get; set; } - //另外一种写法: - //public RouteData GetRouteData() - //{ - // return base.RouteData; - //} - - //public void SetRouteData(RouteData value) - //{ - // throw new NotImplementedException(); - //} - - - public string CurrentMenu { get; set; } - - public List MessagerList { get; set; } - - //public FullAccount FullAccount { get; set; } - - public DateTime PageStartTime { get; set; } - - public DateTime PageEndTime { get; set; } - - //protected void SetupTraceInfo() - //{ - // ViewData["TraceIdent"] = HttpContext.TraceIdentifier; - // //ViewData["NumLogs"] = HttpRequestLog.GetHttpRequestLog(HttpContext.TraceIdentifier).RequestLogs.Count; - //} - - - public override void OnPageHandlerSelected(PageHandlerSelectedContext context) - { - base.OnPageHandlerSelected(context); - } - - public override Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) - { - //获取缓存系统信息 - try - { - if (SiteConfig.IsInstalling) - { - FullSystemConfig = null; - throw new NcfUninstallException("系统进入安装状态", false); - } - else - { - var fullSystemConfigCache = context.HttpContext.RequestServices.GetService(); - FullSystemConfig = fullSystemConfigCache.Data; - } - - } - catch (/*SqlException*/ DbException) - { - //如数据库未创建 - context.Result = new RedirectResult("/Install"); - - return Task.CompletedTask; - - } - catch (NcfUninstallException) - { - //需要进行安装 - context.Result = new RedirectResult("/Install"); - return Task.CompletedTask; - } - - return base.OnPageHandlerExecutionAsync(context, next); - } - - - /// - /// 检查是否在特定 Scheme 下已登录 - /// - /// Scheme 名称 - /// - public async Task CheckLoginedAsync(string authenticationScheme) - { - var authenticate = await HttpContext.AuthenticateAsync(authenticationScheme); - return authenticate.Succeeded; - } - - public void SetMessager(MessageType messageType, string messageText, bool showClose = true) - { - TempData["Messager"] = new Messager(messageType, messageText).ToJson(); - } - - public IActionResult Ok(T data, bool succed, string msg = "操作成功!") - { - AjaxReturnModel returnModel = new AjaxReturnModel(data); - returnModel.Success = succed; - returnModel.Msg = msg; - return new JsonResult(returnModel); - } - - public IActionResult Ok(bool isSuccess, string message) - { - if (!isSuccess) - { - return new OkObjectResult(new AjaxReturnModel() { Success = isSuccess, Msg = message }); - } - return new OkObjectResult(new AjaxReturnModel() { Success = isSuccess, Msg = message }); - } - - public IActionResult Ok(object data) - { - return new OkObjectResult(new AjaxReturnModel(data) { Success = true }); - } - } - -} +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.Routing; +//using Microsoft.Data.SqlClient; +using Senparc.CO2NET; +using Senparc.CO2NET.Extensions; +using Senparc.Ncf.Core.Cache; +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Core.Models.VD; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using System.Data.Common; +using Senparc.Ncf.Core.Config; + +namespace Senparc.Ncf.Core.Models.VD +{ + public interface IPageModelBase : IBaseUiVD, IValidatorEnvironment + { + RouteData RouteData { get; set; } + } + + + public class PageModelBase : PageModel, IPageModelBase + { + public FullSystemConfig FullSystemConfig { get; set; } + + public MetaCollection MetaCollection { get; set; } + + public string UserName { get; set; } + + public bool IsAdmin { get; set; } + + public new RouteData RouteData { get => base.RouteData; set => throw new NotImplementedException(); } + + //public RouteData RouteData { get; set; } + //Another way to write it: + //public RouteData GetRouteData() + //{ + // return base.RouteData; + //} + + //public void SetRouteData(RouteData value) + //{ + // throw new NotImplementedException(); + //} + + + public string CurrentMenu { get; set; } + + public List MessagerList { get; set; } + + //public FullAccount FullAccount { get; set; } + + public DateTime PageStartTime { get; set; } + + public DateTime PageEndTime { get; set; } + + //protected void SetupTraceInfo() + //{ + // ViewData["TraceIdent"] = HttpContext.TraceIdentifier; + // //ViewData["NumLogs"] = HttpRequestLog.GetHttpRequestLog(HttpContext.TraceIdentifier).RequestLogs.Count; + //} + + + public override void OnPageHandlerSelected(PageHandlerSelectedContext context) + { + base.OnPageHandlerSelected(context); + } + + public override Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) + { + //Get cache system information + try + { + if (SiteConfig.IsInstalling) + { + FullSystemConfig = null; + throw new NcfUninstallException("系统进入安装状态", false); + } + else + { + var fullSystemConfigCache = context.HttpContext.RequestServices.GetService(); + FullSystemConfig = fullSystemConfigCache.Data; + } + + } + catch (/*SqlException*/ DbException) + { + //If the database is not created + context.Result = new RedirectResult("/Install"); + + return Task.CompletedTask; + + } + catch (NcfUninstallException) + { + //Requires installation + context.Result = new RedirectResult("/Install"); + return Task.CompletedTask; + } + + return base.OnPageHandlerExecutionAsync(context, next); + } + + + /// + /// Check if you are logged in under a specific Scheme + /// + /// Scheme name + /// + public async Task CheckLoginedAsync(string authenticationScheme) + { + var authenticate = await HttpContext.AuthenticateAsync(authenticationScheme); + return authenticate.Succeeded; + } + + public void SetMessager(MessageType messageType, string messageText, bool showClose = true) + { + TempData["Messager"] = new Messager(messageType, messageText).ToJson(); + } + + public IActionResult Ok(T data, bool succed, string msg = "操作成功!") + { + AjaxReturnModel returnModel = new AjaxReturnModel(data); + returnModel.Success = succed; + returnModel.Msg = msg; + return new JsonResult(returnModel); + } + + public IActionResult Ok(bool isSuccess, string message) + { + if (!isSuccess) + { + return new OkObjectResult(new AjaxReturnModel() { Success = isSuccess, Msg = message }); + } + return new OkObjectResult(new AjaxReturnModel() { Success = isSuccess, Msg = message }); + } + + public IActionResult Ok(object data) + { + return new OkObjectResult(new AjaxReturnModel(data) { Success = true }); + } + } + +} diff --git a/src/Basic/Senparc.Ncf.Core/MultiTenant/IIgnoreMulitTenant.cs b/src/Basic/Senparc.Ncf.Core/MultiTenant/IIgnoreMulitTenant.cs index 3e06d2d8b..f6b73ee21 100644 --- a/src/Basic/Senparc.Ncf.Core/MultiTenant/IIgnoreMulitTenant.cs +++ b/src/Basic/Senparc.Ncf.Core/MultiTenant/IIgnoreMulitTenant.cs @@ -1,13 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.MultiTenant -{ - /// - /// 忽略多租户数据库属性 - /// - public interface IIgnoreMulitTenant - { - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.MultiTenant +{ + /// + /// Ignore multi-tenant database properties + /// + public interface IIgnoreMulitTenant + { + } +} diff --git a/src/Basic/Senparc.Ncf.Core/MultiTenant/MultiTenantHelper.cs b/src/Basic/Senparc.Ncf.Core/MultiTenant/MultiTenantHelper.cs index 0317fa8c8..274b59792 100644 --- a/src/Basic/Senparc.Ncf.Core/MultiTenant/MultiTenantHelper.cs +++ b/src/Basic/Senparc.Ncf.Core/MultiTenant/MultiTenantHelper.cs @@ -1,47 +1,47 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Senparc.Ncf.Core.Config; -using Senparc.Ncf.Core.Exceptions; -using Senparc.Ncf.Core.Models; -using System; - -namespace Senparc.Ncf.Core.MultiTenant -{ - /// - /// MultiTenantHelper - /// - public static class MultiTenantHelper - { - /// - /// 获取 RequestTenantInfo,并且检查其是否匹配 - /// - /// - /// 引用此方法的方法(用于异常提示) - /// - /// 如果 RequestTenantInfo.MatchSuccess 为 false,则抛出异常 - /// - public static RequestTenantInfo TryGetAndCheckRequestTenantInfo(IServiceProvider serviceProvider, string referenceMethod, DbContext dbContext = null) - { - var requestTenantInfo = serviceProvider.GetRequiredService(); - //Console.WriteLine($"{referenceMethod} requestTenantInfo:" + requestTenantInfo.GetHashCode()); - - if (!SiteConfig.IsInstalling) - { - //如果未设置,则进行设定 - //if (!requestTenantInfo.TriedMatching) - // { - // throw new NcfUninstallException("TriedMatching 为 false,推测系统未进行安装。如果在调试状态下(F5)看到此消息请忽略,继续执行。", null); - // } - - if (!requestTenantInfo.MatchSuccess) - { - var errorMsg = SiteConfig.SenparcCoreSetting.EnableMultiTenant ? $"当前多租户状态已经启用,但在 {referenceMethod} 调用过程中发现未捕捉到正确的 RequestTenantInfo.TenantId" : "当前多租户状态未开启"; - - throw new NcfTenantException(errorMsg, new NcfDatabaseException($"本次请求没有匹配到正确的多租户 RequestTenantInfo 信息。Id:{requestTenantInfo.Id},Name:{requestTenantInfo.Name}", - DatabaseConfigurationFactory.Instance.Current.GetType(), dbContext?.GetType())); - } - } - return requestTenantInfo; - } - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Senparc.Ncf.Core.Config; +using Senparc.Ncf.Core.Exceptions; +using Senparc.Ncf.Core.Models; +using System; + +namespace Senparc.Ncf.Core.MultiTenant +{ + /// + /// MultiTenantHelper + /// + public static class MultiTenantHelper + { + /// + /// Get the RequestTenantInfo and check if it matches + /// + /// + /// Method that references this method (used for exception messages) + /// + /// Thrown when RequestTenantInfo.MatchSuccess is false + /// + public static RequestTenantInfo TryGetAndCheckRequestTenantInfo(IServiceProvider serviceProvider, string referenceMethod, DbContext dbContext = null) + { + var requestTenantInfo = serviceProvider.GetRequiredService(); + //Console.WriteLine($"{referenceMethod} requestTenantInfo:" + requestTenantInfo.GetHashCode()); + + if (!SiteConfig.IsInstalling) + { + //If not set, set it + //if (!requestTenantInfo.TriedMatching) + // { + // throw new NcfUninstallException("TriedMatching is false, it is assumed that the system has not been installed. If you see this message in debugging mode (F5), please ignore it and continue execution.", null); + // } + + if (!requestTenantInfo.MatchSuccess) + { + var errorMsg = SiteConfig.SenparcCoreSetting.EnableMultiTenant ? $"当前多租户状态已经启用,但在 {referenceMethod} 调用过程中发现未捕捉到正确的 RequestTenantInfo.TenantId" : "当前多租户状态未开启"; + + throw new NcfTenantException(errorMsg, new NcfDatabaseException($"本次请求没有匹配到正确的多租户 RequestTenantInfo 信息。Id:{requestTenantInfo.Id},Name:{requestTenantInfo.Name}", + DatabaseConfigurationFactory.Instance.Current.GetType(), dbContext?.GetType())); + } + } + return requestTenantInfo; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/MultiTenant/RequestTenantInfo.cs b/src/Basic/Senparc.Ncf.Core/MultiTenant/RequestTenantInfo.cs index 520145ef6..4518d9251 100644 --- a/src/Basic/Senparc.Ncf.Core/MultiTenant/RequestTenantInfo.cs +++ b/src/Basic/Senparc.Ncf.Core/MultiTenant/RequestTenantInfo.cs @@ -1,50 +1,50 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.MultiTenant -{ - /// - /// 某一个请求对应的租户信息 - /// - public class RequestTenantInfo - { - public int Id { get; set; } - /// - /// 唯一名称 - /// - public string Name { get; set; } - /// - /// 匹配条件 - /// - public string TenantKey { get; set; } - /// - /// 初始化开始时间 - /// - public DateTime BeginTime { get; } - /// - /// 是否匹配成功 - /// - public bool MatchSuccess { get; private set; } - - /// - /// 是否已经尝试过匹配 - /// - public bool TriedMatching { get; private set; } - - public RequestTenantInfo() - { - BeginTime = SystemTime.Now.DateTime; - } - - /// - /// 尝试匹配 - /// - /// 是否成功 - public void TryMatch(bool success) - { - TriedMatching = true; - MatchSuccess = success; - } - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.MultiTenant +{ + /// + /// Tenant information for a specific request + /// + public class RequestTenantInfo + { + public int Id { get; set; } + /// + /// Unique name + /// + public string Name { get; set; } + /// + /// match condition + /// + public string TenantKey { get; set; } + /// + ///Initialization start time + /// + public DateTime BeginTime { get; } + /// + /// Whether the match is successful + /// + public bool MatchSuccess { get; private set; } + + /// + /// Whether matching has already been attempted + /// + public bool TriedMatching { get; private set; } + + public RequestTenantInfo() + { + BeginTime = SystemTime.Now.DateTime; + } + + /// + /// try to match + /// + /// Whether it was successful + public void TryMatch(bool success) + { + TriedMatching = true; + MatchSuccess = success; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/MultiTenant/TenantRule.cs b/src/Basic/Senparc.Ncf.Core/MultiTenant/TenantRule.cs index a2c9d9c2d..3ab59e894 100644 --- a/src/Basic/Senparc.Ncf.Core/MultiTenant/TenantRule.cs +++ b/src/Basic/Senparc.Ncf.Core/MultiTenant/TenantRule.cs @@ -1,29 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.MultiTenant -{ - /// - /// 租户规则 - /// - public enum TenantRule - { - /// - /// 以域名区分 - /// - DomainName = 0, - /// - /// Request 请求中的 Header 信息 - /// - RequestHeader = 1, - /// - /// 以登录输入的租户名称区分 - /// - LoginInput = 2, - /// - /// 默认 - /// - Default = LoginInput - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.MultiTenant +{ + /// + ///tenant rules + /// + public enum TenantRule + { + /// + /// Distinguish by domain name + /// + DomainName = 0, + /// + /// Request Header information in the request + /// + RequestHeader = 1, + /// + /// Distinguish by tenant name entered at login + /// + LoginInput = 2, + /// + /// default + /// + Default = LoginInput + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Properties/BindableBase.cs b/src/Basic/Senparc.Ncf.Core/Properties/BindableBase.cs index 75ab878ee..02a035cce 100644 --- a/src/Basic/Senparc.Ncf.Core/Properties/BindableBase.cs +++ b/src/Basic/Senparc.Ncf.Core/Properties/BindableBase.cs @@ -1,48 +1,48 @@ -using Senparc.Ncf.Core.Annotations; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; - -namespace Senparc.Ncf.Core.Entities -{ - /// - /// 用于实现INotifyPropertyChanged - /// - [Serializable] - public abstract class BindableBase : INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged; - - [NotifyPropertyChangedInvocator] - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - var eventHandler = this.PropertyChanged; - if (eventHandler != null) - { - eventHandler(this, new PropertyChangedEventArgs(propertyName)); - } - - //PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));//需要VS2015+或新编译器支持 - } - - /// - /// 设置属性 - /// - /// - /// - /// - /// - /// - protected bool SetProperty(ref T storage, T value, [CallerMemberName] String propertyName = null) - { - if (object.Equals(storage, value)) return false; - - storage = value; - this.OnPropertyChanged(propertyName); - return true; - } - } -} +using Senparc.Ncf.Core.Annotations; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Senparc.Ncf.Core.Entities +{ + /// + /// Used to implement INotifyPropertyChanged + /// + [Serializable] + public abstract class BindableBase : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + [NotifyPropertyChangedInvocator] + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + var eventHandler = this.PropertyChanged; + if (eventHandler != null) + { + eventHandler(this, new PropertyChangedEventArgs(propertyName)); + } + + //PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));//Requires VS2015+ or new compiler support + } + + /// + ///Set properties + /// + /// + /// + /// + /// + /// + protected bool SetProperty(ref T storage, T value, [CallerMemberName] String propertyName = null) + { + if (object.Equals(storage, value)) return false; + + storage = value; + this.OnPropertyChanged(propertyName); + return true; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Utility/CacheEntity/FullEntityCache.cs b/src/Basic/Senparc.Ncf.Core/Utility/CacheEntity/FullEntityCache.cs index c1d044ed0..6817d4d82 100644 --- a/src/Basic/Senparc.Ncf.Core/Utility/CacheEntity/FullEntityCache.cs +++ b/src/Basic/Senparc.Ncf.Core/Utility/CacheEntity/FullEntityCache.cs @@ -1,44 +1,44 @@ -using Senparc.Ncf.Log; -using System; - -namespace Senparc.Ncf.Core.Utility -{ - public static class FullEntityCache - { - public static void SetFullEntityCache(TFullEntity fullEntity, TEntity entity) - { - if (fullEntity == null || entity == null) - { - throw new Exception("参数不可以为NULL"); - } - - try - { - var fullEntityType = fullEntity.GetType(); - var entityType = entity.GetType(); - var props = fullEntityType.GetProperties(); - foreach (var p in props) - { - //获得当前属性的特性 - AutoSetCacheAttribute m = Attribute.GetCustomAttribute(p, typeof(AutoSetCacheAttribute)) as AutoSetCacheAttribute; - if (m != null) - { - //允许自动复制 - //获取原始实体值 - var entityProp = entityType.GetProperty(p.Name); - if (entityProp == null || entityProp.PropertyType != p.PropertyType) - { - throw new Exception("原始实体没有相同类型和名称的属性存在:" + p.Name); - } - p.SetValue(fullEntity, entityProp.GetValue(entity, null), null); - } - } - } - catch (Exception ex) - { - LogUtility.SystemLogger.Debug("跟踪Cache错误:" + ex.Message, ex); - throw ex; - } - } - } +using Senparc.Ncf.Log; +using System; + +namespace Senparc.Ncf.Core.Utility +{ + public static class FullEntityCache + { + public static void SetFullEntityCache(TFullEntity fullEntity, TEntity entity) + { + if (fullEntity == null || entity == null) + { + throw new Exception("参数不可以为NULL"); + } + + try + { + var fullEntityType = fullEntity.GetType(); + var entityType = entity.GetType(); + var props = fullEntityType.GetProperties(); + foreach (var p in props) + { + //Get the characteristics of the current attribute + AutoSetCacheAttribute m = Attribute.GetCustomAttribute(p, typeof(AutoSetCacheAttribute)) as AutoSetCacheAttribute; + if (m != null) + { + //Allow automatic copying + //Get original entity value + var entityProp = entityType.GetProperty(p.Name); + if (entityProp == null || entityProp.PropertyType != p.PropertyType) + { + throw new Exception("原始实体没有相同类型和名称的属性存在:" + p.Name); + } + p.SetValue(fullEntity, entityProp.GetValue(entity, null), null); + } + } + } + catch (Exception ex) + { + LogUtility.SystemLogger.Debug("跟踪Cache错误:" + ex.Message, ex); + throw ex; + } + } + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Core/Utility/CheckCodeHandle.cs b/src/Basic/Senparc.Ncf.Core/Utility/CheckCodeHandle.cs index 0222bbb63..055e19f92 100644 --- a/src/Basic/Senparc.Ncf.Core/Utility/CheckCodeHandle.cs +++ b/src/Basic/Senparc.Ncf.Core/Utility/CheckCodeHandle.cs @@ -1,145 +1,145 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Caching.Memory; -using Senparc.CO2NET.Cache; -using System; -using System.Collections.Generic; - -namespace Senparc.Ncf.Core.Utility -{ - /// - /// 验证码用途(分类) - /// - public enum CheckCodeKind - { - Reg = 0, - Login = 1, - SMS = 2 - } - - public class CheckCodeHandle - { - private CheckCodeKind _checkCodeKind; - private const string COOKIE_NAME_PREFIX = "senparccheckcodeverify_"; - private string _cookieName; - private HttpContext _httpContext; - private IMemoryCache _cache;//TODO: _cache需要赋值 - private string _checkCodeSalt = "_senparccheckcode"; - private Dictionary checkedCodeCollection - { - get - { - var cacheStrategy = CacheStrategyFactory.GetObjectCacheStrategyInstance(); - if (cacheStrategy.Get("SenparcCheckCodeSuccessCollection") == null) - { - cacheStrategy.Set("SenparcCheckCodeSuccessCollection", new Dictionary(), TimeSpan.FromMinutes(20)); - } - return _cache.Get>("SenparcCheckCodeSuccessCollection"); - } - } - - public CheckCodeHandle(CheckCodeKind checkCodeKind, HttpContext httpContext) - { - _checkCodeKind = checkCodeKind; - _cookieName = COOKIE_NAME_PREFIX + (int)checkCodeKind; - _httpContext = httpContext; - - } - - /// - /// 获取验证码并记录cookie - /// - /// - /// - public string GenerateCheckCode() - { - //int number; - char code; - string checkcode = string.Empty; - System.Random random = new Random(); - - //string[] str = new string[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" }; - string str1 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";//abcdefghijklmnopqrstuvwxyz - - for (int i = 0; i < 4; i++)//原始值:5 - { - - //code = (char)str[random.Next(0, 61)]; - code = str1[random.Next(0, str1.Length - 1)]; - - checkcode += code.ToString(); - - //number = random.Next(); - //if (number % 2 == 0 - // code = (char)('0' + (char)(number % 10)); - //else - // code = (char)('0' + (char)(number % 10)); - //checkcode += code.ToString(); - } - - this.SaveToCookie(checkcode); - - //base.Response.Write("" + checkcode.ToString()); - //Response.Cookies.Add(new HttpCookie("checkcode", checkcode)); - return checkcode; - } - - public void SaveToCookie(string checkCode) - { - DateTimeOffset to = new DateTimeOffset(DateTime.Now.AddDays(1)); - var verifyCode = GenerateVerifyCode(checkCode, null); - var value = $"checkcode={verifyCode}"; - _httpContext.Response.Cookies.Append(this._cookieName, value, new CookieOptions() { Expires = to });//确定写入cookie中 - } - - public bool ValidateCheckCode(string inputCheckCode) - { - if (string.IsNullOrEmpty(inputCheckCode)) - { - return false; - } - - //验证码验证 - var myCookie = _httpContext.Request.Cookies[this._cookieName]; - - if (myCookie == null || !myCookie.StartsWith("checkcode=")) - { - return false; - } - - string checkCodeVerify = myCookie.Split('=')[1]; - if (string.IsNullOrEmpty(checkCodeVerify) || checkCodeVerify.Length < 32) - { - return false; - } - if (checkedCodeCollection.ContainsKey(checkCodeVerify)) - { - return false;//如果已经参与过验证,则不能再次使用 - } - var verifyCode = GenerateVerifyCode(inputCheckCode.Trim(), checkCodeVerify.Substring(checkCodeVerify.Length - 32, 32)); - if (verifyCode.ToUpper() != checkCodeVerify.ToUpper()) - { - return false; - } - checkedCodeCollection.Add(checkCodeVerify, inputCheckCode); - return true; - - //_httpContext.Response.Write(""); - } - - public string GenerateVerifyCode(string inputCheckCode, string key) - { - if (string.IsNullOrEmpty(key)) - { - key = Guid.NewGuid().ToString().Replace("-", ""); - } - string verifyFormat = "{0}{1}{2}"; - string checkcodeEncoding = MD5.GetMD5Code(inputCheckCode.ToUpper(), key.ToUpper() + _checkCodeSalt).Replace("-", ""); - string verifyCode = string.Format(verifyFormat, - ((int)_checkCodeKind).ToString(), - checkcodeEncoding, - key//密匙 - ); - return verifyCode; - } - } -} +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Caching.Memory; +using Senparc.CO2NET.Cache; +using System; +using System.Collections.Generic; + +namespace Senparc.Ncf.Core.Utility +{ + /// + /// Verification code usage category + /// + public enum CheckCodeKind + { + Reg = 0, + Login = 1, + SMS = 2 + } + + public class CheckCodeHandle + { + private CheckCodeKind _checkCodeKind; + private const string COOKIE_NAME_PREFIX = "senparccheckcodeverify_"; + private string _cookieName; + private HttpContext _httpContext; + private IMemoryCache _cache;//TODO: _cache needs assignment + private string _checkCodeSalt = "_senparccheckcode"; + private Dictionary checkedCodeCollection + { + get + { + var cacheStrategy = CacheStrategyFactory.GetObjectCacheStrategyInstance(); + if (cacheStrategy.Get("SenparcCheckCodeSuccessCollection") == null) + { + cacheStrategy.Set("SenparcCheckCodeSuccessCollection", new Dictionary(), TimeSpan.FromMinutes(20)); + } + return _cache.Get>("SenparcCheckCodeSuccessCollection"); + } + } + + public CheckCodeHandle(CheckCodeKind checkCodeKind, HttpContext httpContext) + { + _checkCodeKind = checkCodeKind; + _cookieName = COOKIE_NAME_PREFIX + (int)checkCodeKind; + _httpContext = httpContext; + + } + + /// + /// Get verification code and record cookie + /// + /// + /// + public string GenerateCheckCode() + { + //int number; + char code; + string checkcode = string.Empty; + System.Random random = new Random(); + + //string[] str = new string[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" }; + string str1 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";//abcdefghijklmnopqrstuvwxyz + + for (int i = 0; i < 4; i++)//Original value: 5 + { + + //code = (char)str[random.Next(0, 61)]; + code = str1[random.Next(0, str1.Length - 1)]; + + checkcode += code.ToString(); + + //number = random.Next(); + //if (number % 2 == 0 + // code = (char)('0' + (char)(number % 10)); + //else + // code = (char)('0' + (char)(number % 10)); + //checkcode += code.ToString(); + } + + this.SaveToCookie(checkcode); + + //base.Response.Write("" + checkcode.ToString()); + //Response.Cookies.Add(new HttpCookie("checkcode", checkcode)); + return checkcode; + } + + public void SaveToCookie(string checkCode) + { + DateTimeOffset to = new DateTimeOffset(DateTime.Now.AddDays(1)); + var verifyCode = GenerateVerifyCode(checkCode, null); + var value = $"checkcode={verifyCode}"; + _httpContext.Response.Cookies.Append(this._cookieName, value, new CookieOptions() { Expires = to });//Confirm to write to cookie + } + + public bool ValidateCheckCode(string inputCheckCode) + { + if (string.IsNullOrEmpty(inputCheckCode)) + { + return false; + } + + //Verification code + var myCookie = _httpContext.Request.Cookies[this._cookieName]; + + if (myCookie == null || !myCookie.StartsWith("checkcode=")) + { + return false; + } + + string checkCodeVerify = myCookie.Split('=')[1]; + if (string.IsNullOrEmpty(checkCodeVerify) || checkCodeVerify.Length < 32) + { + return false; + } + if (checkedCodeCollection.ContainsKey(checkCodeVerify)) + { + return false;//If you have already participated in verification, you cannot use it again. + } + var verifyCode = GenerateVerifyCode(inputCheckCode.Trim(), checkCodeVerify.Substring(checkCodeVerify.Length - 32, 32)); + if (verifyCode.ToUpper() != checkCodeVerify.ToUpper()) + { + return false; + } + checkedCodeCollection.Add(checkCodeVerify, inputCheckCode); + return true; + + //_httpContext.Response.Write(""); + } + + public string GenerateVerifyCode(string inputCheckCode, string key) + { + if (string.IsNullOrEmpty(key)) + { + key = Guid.NewGuid().ToString().Replace("-", ""); + } + string verifyFormat = "{0}{1}{2}"; + string checkcodeEncoding = MD5.GetMD5Code(inputCheckCode.ToUpper(), key.ToUpper() + _checkCodeSalt).Replace("-", ""); + string verifyCode = string.Format(verifyFormat, + ((int)_checkCodeKind).ToString(), + checkcodeEncoding, + key//Key + ); + return verifyCode; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Utility/CommonWebParts.cs b/src/Basic/Senparc.Ncf.Core/Utility/CommonWebParts.cs index 73ffbb28a..dad8ae0b5 100644 --- a/src/Basic/Senparc.Ncf.Core/Utility/CommonWebParts.cs +++ b/src/Basic/Senparc.Ncf.Core/Utility/CommonWebParts.cs @@ -1,212 +1,212 @@ -using System; -using System.Text; -using System.IO; -using System.Runtime.Serialization.Json; -using System.Text.RegularExpressions; -using Microsoft.AspNetCore.Http; - -namespace Senparc.Ncf.Core.Utility -{ - public static class CommonWebParts - { - //#region Convert HTML code public static string exHTML(string ntext) - /////// - /////// Convert HTML code -- TNT2 - /////// Implemented: newline, space - /////// - ////public static string exHTML(string ntext) - ////{ - //// ntext = ntext.ToString().Replace(" ", " ").Replace(Convert.ToString((char)13), "
"); - //// return ntext; - ////} - - ///// - ///// Convert HTML code -- TNT2 - ///// Implemented: newline, space - ///// - //public static string ExHTML(this string ntext) - //{ - // ntext = ntext.ToString().Replace(" ", " ").Replace(Convert.ToString((char)13), "
"); - // return ntext; - //} - - //public static string ExHtml(string ntext) - //{ - // ntext = ntext.ToString().Replace(" ", " ").Replace(Convert.ToString((char)13), "
"); - // return ntext; - //} - //#endregion - - /// - /// Remove all HTML tags - /// - /// - /// - public static string DelHtml(this string str) { - if (!string.IsNullOrEmpty(str)) { - str = Regex.Replace(str, "<[^>]*>", "").Replace("\r\n", ""); - } - return str; - } - - - /// - /// Get Form value, for Handler usage - /// - /// Form key - /// HttpContext - /// - public static string GetFormValue(string key, HttpContext context) - { - return context.Request.Form[key].ToString(); - } - - - /// - /// Get formatted file name - /// - /// File format (in Config.UpLoadFileFormat) - /// Current file name (path can be included) - /// - public static string GetFormattedUpLoadFileName(string fileFormat, string currentFileName) - { - return string.Format(fileFormat, currentFileName, Path.GetExtension(currentFileName)); - } - - #region Currency uppercase conversion - /// - /// Currency uppercase conversion - /// - /// Currency amount - /// - public static string CmycurD(decimal num) - { - string str1 = "零壹贰叁肆伍陆柒捌玖"; //Chinese characters corresponding to 0-9 - string str2 = "万仟佰拾亿仟佰拾万仟佰拾元角分"; //Chinese characters for digit places - string str3 = ""; //Value extracted from original num - string str4 = ""; //String representation of number - string str5 = ""; //Uppercase RMB amount format - int i; //Loop variable - int j; //Length of num*100 string - string ch1 = ""; //Chinese reading of digit - string ch2 = ""; //Chinese reading of digit place - int nzero = 0; //Count consecutive zero values - int temp; //Value extracted from original num - num = Math.Round(Math.Abs(num), 2); //Take absolute value and round to 2 decimals - str4 = ((long)(num * 100)).ToString(); //Multiply num by 100 and convert to string - j = str4.Length; //Find highest digit - if (j > 15) { return "溢出"; } - str2 = str2.Substring(15 - j); //Extract corresponding part of str2, e.g. 200.55 -> j=5 -> str2=佰拾元角分 - //Loop through digits to convert - for (i = 0; i < j; i++) - { - str3 = str4.Substring(i, 1); //Get one digit to convert - temp = Convert.ToInt32(str3); //Convert to number - if (i != (j - 3) && i != (j - 7) && i != (j - 11) && i != (j - 15)) - { - //When current digit place is not yuan/ten-thousand/hundred-million/trillion - if (str3 == "0") - { - ch1 = ""; - ch2 = ""; - nzero = nzero + 1; - } - else - { - if (str3 != "0" && nzero != 0) - { - ch1 = "零" + str1.Substring(temp * 1, 1); - ch2 = str2.Substring(i, 1); - nzero = 0; - } - else - { - ch1 = str1.Substring(temp * 1, 1); - ch2 = str2.Substring(i, 1); - nzero = 0; - } - } - } - else - { - //Current place is a key place: trillion/hundred-million/ten-thousand/yuan - if (str3 != "0" && nzero != 0) - { - ch1 = "零" + str1.Substring(temp * 1, 1); - ch2 = str2.Substring(i, 1); - nzero = 0; - } - else - { - if (str3 != "0" && nzero == 0) - { - ch1 = str1.Substring(temp * 1, 1); - ch2 = str2.Substring(i, 1); - nzero = 0; - } - else - { - if (str3 == "0" && nzero >= 3) - { - ch1 = ""; - ch2 = ""; - nzero = nzero + 1; - } - else - { - if (j >= 11) - { - ch1 = ""; - nzero = nzero + 1; - } - else - { - ch1 = ""; - ch2 = str2.Substring(i, 1); - nzero = nzero + 1; - } - } - } - } - } - if (i == (j - 11) || i == (j - 3)) - { - //If this place is hundred-million or yuan, it must be written - ch2 = str2.Substring(i, 1); - } - str5 = str5 + ch1 + ch2; - if (i == j - 1 && str3 == "0") - { - //If the last digit (fen) is 0, append "整" - str5 = str5 + '整'; - } - } - if (num == 0) - { - str5 = "零元整"; - } - return str5; - } - - /// - /// Currency uppercase conversion - /// - /// Currency amount - /// - public static string CmycurD(string numstr) - { - try - { - decimal num = Convert.ToDecimal(numstr); - return CmycurD(num); - } - catch - { - return "Non-numeric format!"; - } - } - - #endregion - - } -} +using System; +using System.Text; +using System.IO; +using System.Runtime.Serialization.Json; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Http; + +namespace Senparc.Ncf.Core.Utility +{ + public static class CommonWebParts + { + //#region Convert HTML code public static string exHTML(string ntext) + /////// + /////// Convert HTML code -- TNT2 + /////// Implemented: newline, space + /////// + ////public static string exHTML(string ntext) + ////{ + //// ntext = ntext.ToString().Replace(" ", " ").Replace(Convert.ToString((char)13), "
"); + //// return ntext; + ////} + + ///// + ///// Convert HTML code -- TNT2 + ///// Implemented: newline, space + ///// + //public static string ExHTML(this string ntext) + //{ + // ntext = ntext.ToString().Replace(" ", " ").Replace(Convert.ToString((char)13), "
"); + // return ntext; + //} + + //public static string ExHtml(string ntext) + //{ + // ntext = ntext.ToString().Replace(" ", " ").Replace(Convert.ToString((char)13), "
"); + // return ntext; + //} + //#endregion + + /// + /// Remove all HTML tags + /// + /// + /// + public static string DelHtml(this string str) { + if (!string.IsNullOrEmpty(str)) { + str = Regex.Replace(str, "<[^>]*>", "").Replace("\r\n", ""); + } + return str; + } + + + /// + /// Get Form value, for Handler usage + /// + /// Form key + /// HttpContext + /// + public static string GetFormValue(string key, HttpContext context) + { + return context.Request.Form[key].ToString(); + } + + + /// + /// Get formatted file name + /// + /// File format (in Config.UpLoadFileFormat) + /// Current file name (path can be included) + /// + public static string GetFormattedUpLoadFileName(string fileFormat, string currentFileName) + { + return string.Format(fileFormat, currentFileName, Path.GetExtension(currentFileName)); + } + + #region Currency uppercase conversion + /// + /// Currency uppercase conversion + /// + /// Currency amount + /// + public static string CmycurD(decimal num) + { + string str1 = "零壹贰叁肆伍陆柒捌玖"; //Chinese characters corresponding to 0-9 + string str2 = "万仟佰拾亿仟佰拾万仟佰拾元角分"; //Chinese characters for digit places + string str3 = ""; //Value extracted from original num + string str4 = ""; //String representation of number + string str5 = ""; //Uppercase RMB amount format + int i; //Loop variable + int j; //Length of num*100 string + string ch1 = ""; //Chinese reading of digit + string ch2 = ""; //Chinese reading of digit place + int nzero = 0; //Count consecutive zero values + int temp; //Value extracted from original num + num = Math.Round(Math.Abs(num), 2); //Take absolute value and round to 2 decimals + str4 = ((long)(num * 100)).ToString(); //Multiply num by 100 and convert to string + j = str4.Length; //Find highest digit + if (j > 15) { return "溢出"; } + str2 = str2.Substring(15 - j); //Extract corresponding part of str2, e.g. 200.55 -> j=5 -> str2=100 yuan angle minutes + //Loop through digits to convert + for (i = 0; i < j; i++) + { + str3 = str4.Substring(i, 1); //Get one digit to convert + temp = Convert.ToInt32(str3); //Convert to number + if (i != (j - 3) && i != (j - 7) && i != (j - 11) && i != (j - 15)) + { + //When current digit place is not yuan/ten-thousand/hundred-million/trillion + if (str3 == "0") + { + ch1 = ""; + ch2 = ""; + nzero = nzero + 1; + } + else + { + if (str3 != "0" && nzero != 0) + { + ch1 = "零" + str1.Substring(temp * 1, 1); + ch2 = str2.Substring(i, 1); + nzero = 0; + } + else + { + ch1 = str1.Substring(temp * 1, 1); + ch2 = str2.Substring(i, 1); + nzero = 0; + } + } + } + else + { + //Current place is a key place: trillion/hundred-million/ten-thousand/yuan + if (str3 != "0" && nzero != 0) + { + ch1 = "零" + str1.Substring(temp * 1, 1); + ch2 = str2.Substring(i, 1); + nzero = 0; + } + else + { + if (str3 != "0" && nzero == 0) + { + ch1 = str1.Substring(temp * 1, 1); + ch2 = str2.Substring(i, 1); + nzero = 0; + } + else + { + if (str3 == "0" && nzero >= 3) + { + ch1 = ""; + ch2 = ""; + nzero = nzero + 1; + } + else + { + if (j >= 11) + { + ch1 = ""; + nzero = nzero + 1; + } + else + { + ch1 = ""; + ch2 = str2.Substring(i, 1); + nzero = nzero + 1; + } + } + } + } + } + if (i == (j - 11) || i == (j - 3)) + { + //If this place is hundred-million or yuan, it must be written + ch2 = str2.Substring(i, 1); + } + str5 = str5 + ch1 + ch2; + if (i == j - 1 && str3 == "0") + { + //If the last digit (fen) is 0, append "whole" + str5 = str5 + '整'; + } + } + if (num == 0) + { + str5 = "零元整"; + } + return str5; + } + + /// + /// Currency uppercase conversion + /// + /// Currency amount + /// + public static string CmycurD(string numstr) + { + try + { + decimal num = Convert.ToDecimal(numstr); + return CmycurD(num); + } + catch + { + return "Non-numeric format!"; + } + } + + #endregion + + } +} diff --git a/src/Basic/Senparc.Ncf.Core/Utility/XmlDataContext.cs b/src/Basic/Senparc.Ncf.Core/Utility/XmlDataContext.cs index 5b7e627fb..214d43475 100644 --- a/src/Basic/Senparc.Ncf.Core/Utility/XmlDataContext.cs +++ b/src/Basic/Senparc.Ncf.Core/Utility/XmlDataContext.cs @@ -176,7 +176,7 @@ private string GetXmlFullApplicationPath(string entityName) ///// - ///// 重设密码 + /////Reset password ///// //public List ResetPasswordCodes //{ @@ -387,7 +387,7 @@ public bool Delete(TEntity entity) where TEntity : class //XmlDataContext ctx = new XmlDataContext(_context); //AutoSendEmail error = new AutoSendEmail //{ - // Subject = "发送出错记录", + // Subject = "Sending error record", // Body = e.Message + "\r\n" + e.StackTrace, // LastSendTime = DateTime.Now, // UserName = "System", @@ -436,13 +436,13 @@ public bool Delete(TEntity entity) where TEntity : class } /// - /// 仅保留指定项目 + /// Keep only specified items /// - /// 保留不删除的项目数量 + /// Number of items to retain without deletion public void RetainItems(int retainItemCount) where TEntity : class, new() { string entityName = typeof(TEntity).Name; - XElement xml = this.GetXElement(GetXmlFullApplicationPath(entityName));//载入文档 + XElement xml = this.GetXElement(GetXmlFullApplicationPath(entityName));//Load document var elements = xml.Elements(entityName); if (elements.Count() >= retainItemCount) @@ -453,14 +453,14 @@ public bool Delete(TEntity entity) where TEntity : class } /// - /// 尝试创建数据库 + ///try to create database /// /// public void TryCreateDataBase() { string entityName = typeof(TEntity).Name; - //判断文件是否存在,如果不存在则新建 + //Determine whether the file exists, if not, create a new one var filePath = GetXmlFullApplicationPath(entityName); if (!File.Exists(filePath)) { @@ -474,7 +474,7 @@ public void TryCreateDataBase() #region XML DataBase格式 ///// - ///// 重设密码 + /////Reset password ///// //public partial class ResetPasswordCode //{ diff --git a/src/Basic/Senparc.Ncf.Core/Validator/Validator.cs b/src/Basic/Senparc.Ncf.Core/Validator/Validator.cs index b824cf5b9..a80445a5a 100644 --- a/src/Basic/Senparc.Ncf.Core/Validator/Validator.cs +++ b/src/Basic/Senparc.Ncf.Core/Validator/Validator.cs @@ -293,7 +293,7 @@ public static ValidatorContainer IsValidateUserInfoName(this ValidatorCont } } - string invalidateUserName = "`~!@#$%^&*()+-=;':\",./<>?|\\";//TODO:可以用正则判断 + string invalidateUserName = "`~!@#$%^&*()+-=;':\",./<>?|\\";//TODO: You can use regular rules to judge foreach (var item in invalidateUserName) { if (userName.Contains(item)) @@ -325,7 +325,7 @@ public static ValidatorContainer IsMobile(this ValidatorContainer conta //return Regex(container, @"^1[358]\d{9}$", "Please enter a valid phone number in {0}!", stopWhileFail); //return Regex(container, @"^1[358]\d{9}$", "Please enter a valid phone number!", stopWhileFail); - //电信手机号码正则 string dianxin = @"^1[3578][01379]\d{8}$"; Regex dReg = new Regex(dianxin); //联通手机号正则 string liantong = @"^1[34578][01256]\d{8}$"; Regex tReg = new Regex(liantong); //移动手机号正则 string yidong = @"^(134[012345678]\d{7}|1[34578][012356789]\d{8})$"; Regex yReg = new Regex(yidong); + //Telecom mobile phone number regular string dianxin = @"^1[3578][01379]\d{8}$"; Regex dReg = new Regex(dianxin); //China Unicom mobile phone number regular string liantong = @"^1[34578][01256]\d{8}$"; Regex tReg = new Regex(liantong); //Mobile mobile phone number regular string yidong = @"^(134[012345678]\d{7}|1[34578][012356789]\d{8})$"; Regex yReg = new Regex(yidong); return Regex(container, @"^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$", "Please enter a valid phone number!", stopWhileFail); } @@ -531,7 +531,7 @@ public static ValidatorContainer IsNumber(this ValidatorContainer conta } /// - /// 校验验证码,建议Validator构造函数中的nullOrEmptyable为false + /// Validate verification code. It is recommended to set nullOrEmptyable to false in Validator constructor. /// /// /// diff --git a/src/Basic/Senparc.Ncf.Core/VersionManager.cs b/src/Basic/Senparc.Ncf.Core/VersionManager.cs index b5fa65dd8..542037703 100644 --- a/src/Basic/Senparc.Ncf.Core/VersionManager.cs +++ b/src/Basic/Senparc.Ncf.Core/VersionManager.cs @@ -11,7 +11,7 @@ namespace Senparc.Ncf.Core public class VersionManager { /// - /// 返回版本信息 + /// Return version information /// /// public static string GetVersionNote(string ncfVersion = null, string note = null, bool showOpenSourceInfo = true) @@ -51,7 +51,7 @@ public static string GetVersionNote(string ncfVersion = null, string note = null { sb.AppendLine(" Open source template: https://github.com/NeuCharFramework/NCF"); sb.AppendLine(" Open source template: https://gitee.com/NeuCharFramework/NCF"); - //sb.AppendLine(" 基础模块源码:https://github.com/NeuCharFramework/NcfPackageSources"); + //sb.AppendLine("Basic module source code: https://github.com/NeuCharFramework/NcfPackageSources"); sb.AppendLine(" Documentation: https://doc.ncf.pub/"); } }) diff --git a/src/Basic/Senparc.Ncf.Core/WebApi/NcfWebApiHelper.cs b/src/Basic/Senparc.Ncf.Core/WebApi/NcfWebApiHelper.cs index eeeae36ad..22b502203 100644 --- a/src/Basic/Senparc.Ncf.Core/WebApi/NcfWebApiHelper.cs +++ b/src/Basic/Senparc.Ncf.Core/WebApi/NcfWebApiHelper.cs @@ -1,75 +1,75 @@ -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Senparc.CO2NET; -using Senparc.CO2NET.Extensions; -using Senparc.CO2NET.WebApi; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Core.WebApi -{ - public static class NcfWebApiHelper - { - /// - /// 获取 XNCF 及 [ApiBind] 特性生成的 web API 地址 - /// - /// XNCF 程序集名称(通常和 XNCF 的名称相同) - /// AppService 名称或者 Controller 名称([ApiBind] 中称之为 category) - /// 方法名称或定义的接口名称([ApiBind] 中称之为 name) - /// URL 中的静态后缀,一般情况下请留空 - /// - public static string GetNcfApiClientPath(string xncfName, string appServiceName, string methodName, string showStaticApiState = null) - { - var globalName = ApiBindAttribute.GetGlobalName(xncfName, $"{appServiceName}.{methodName}"); - - var indexOfApiGroupDot = globalName.IndexOf("."); - var apiName = globalName.Substring(indexOfApiGroupDot + 1, globalName.Length - indexOfApiGroupDot - 1); - - var apiPath = WebApiEngine.GetApiPath(xncfName, appServiceName, apiName, showStaticApiState); - return apiPath; - } - - - /// - /// 获取 Aspire 使用的统一默认 XNCF 名称 - /// - /// - /// - /// - public static string GetXncfProjectName(string name, string uniqueCode = null) - { - name = name.Replace(".", "-").Replace("_", "-"); - if (!uniqueCode.IsNullOrEmpty()) - { - name += $"_{uniqueCode}"; - } - return name; - } - - /// - /// 获取 Aspire 使用的统一默认 XNCF 名称 - /// - /// - /// - /// - public static string GetXncfProjectName(Type projectType, string uniqueCode = null) - { - var name = projectType.Name; - return GetXncfProjectName(name, uniqueCode); - } - - - /// - /// 获取 Aspire 使用的统一默认 XNCF 名称 - /// - /// - /// - public static string GetXncfProjectName(string uniqueCode = null) - { - var name = typeof(T).Name; - return GetXncfProjectName(name, uniqueCode); - } - } -} +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Senparc.CO2NET; +using Senparc.CO2NET.Extensions; +using Senparc.CO2NET.WebApi; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Core.WebApi +{ + public static class NcfWebApiHelper + { + /// + /// Get the web API address generated by XNCF and [ApiBind] features + /// + /// XNCF assembly name (usually the same name as XNCF) + /// AppService name or Controller name (called category in [ApiBind]) + /// Method name or defined API name (called `name` in [ApiBind]) + /// Static suffix in URL; usually leave empty + /// + public static string GetNcfApiClientPath(string xncfName, string appServiceName, string methodName, string showStaticApiState = null) + { + var globalName = ApiBindAttribute.GetGlobalName(xncfName, $"{appServiceName}.{methodName}"); + + var indexOfApiGroupDot = globalName.IndexOf("."); + var apiName = globalName.Substring(indexOfApiGroupDot + 1, globalName.Length - indexOfApiGroupDot - 1); + + var apiPath = WebApiEngine.GetApiPath(xncfName, appServiceName, apiName, showStaticApiState); + return apiPath; + } + + + /// + /// Get the unified default XNCF name used by Aspire + /// + /// + /// + /// + public static string GetXncfProjectName(string name, string uniqueCode = null) + { + name = name.Replace(".", "-").Replace("_", "-"); + if (!uniqueCode.IsNullOrEmpty()) + { + name += $"_{uniqueCode}"; + } + return name; + } + + /// + /// Get the unified default XNCF name used by Aspire + /// + /// + /// + /// + public static string GetXncfProjectName(Type projectType, string uniqueCode = null) + { + var name = projectType.Name; + return GetXncfProjectName(name, uniqueCode); + } + + + /// + /// Get the unified default XNCF name used by Aspire + /// + /// + /// + public static string GetXncfProjectName(string uniqueCode = null) + { + var name = typeof(T).Name; + return GetXncfProjectName(name, uniqueCode); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Core/WorkContext/AdminWorkContext.cs b/src/Basic/Senparc.Ncf.Core/WorkContext/AdminWorkContext.cs index 5b4434c41..316e7b2ae 100644 --- a/src/Basic/Senparc.Ncf.Core/WorkContext/AdminWorkContext.cs +++ b/src/Basic/Senparc.Ncf.Core/WorkContext/AdminWorkContext.cs @@ -1,27 +1,27 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Core.WorkContext -{ - /// - /// 管理员区域上下文 - /// - public class AdminWorkContext - { - /// - /// 姓名 - /// - public string UserName { get; set; } - - /// - /// 用户Id - /// - public int AdminUserId { get; set; } - - /// - /// 拥有的角色 - /// - public IEnumerable RoleCodes { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Core.WorkContext +{ + /// + /// Admin area context + /// + public class AdminWorkContext + { + /// + /// Name + /// + public string UserName { get; set; } + + /// + /// User Id + /// + public int AdminUserId { get; set; } + + /// + /// Owned roles + /// + public IEnumerable RoleCodes { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Database.InMemory/InMemoryDatabaseConfiguration.cs b/src/Basic/Senparc.Ncf.Database.InMemory/InMemoryDatabaseConfiguration.cs index f62548267..cd3eb24dc 100644 --- a/src/Basic/Senparc.Ncf.Database.InMemory/InMemoryDatabaseConfiguration.cs +++ b/src/Basic/Senparc.Ncf.Database.InMemory/InMemoryDatabaseConfiguration.cs @@ -1,60 +1,60 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.Extensions.DependencyInjection; -using Senparc.CO2NET.Extensions; -using Senparc.Ncf.Core.Models; -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.Globalization; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Database.InMemory -{ - /// - /// SQLite 数据库配置 - /// - public class InMemoryDatabaseConfiguration : DatabaseConfigurationBase - { - public override MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.InMemory; - - //private static DbConnection CreateInMemoryDatabase(string connStr) - //{ - // var connection = new InMemoryConnection(connStr); - // connection.Open(); - // return connection; - //} - - public override Action> SetUseDatabase => - (optionsBuilder, connectionString, xncfDatabaseData, actionBase) => - { - //其他更多配置 - - //执行 UseInMemory(必须) - //optionsBuilder.UseInMemory(CreateInMemoryDatabase(connectionString), actionBase); - - optionsBuilder.UseInMemoryDatabase(connectionString); - }; - - public override Action DbContextOptionsActionExtension => (builder, xncfDatabaseData) => - { - //更多数据库操作独立配置(非必须) - }; - - - public override string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) - { - //不需要用到 - return null; - } - - public override string GetDropTableSql(DbContext dbContext, string tableName) - { - //不需要用到 - return null; - } - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.DependencyInjection; +using Senparc.CO2NET.Extensions; +using Senparc.Ncf.Core.Models; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Globalization; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Database.InMemory +{ + /// + ///SQLite database configuration + /// + public class InMemoryDatabaseConfiguration : DatabaseConfigurationBase + { + public override MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.InMemory; + + //private static DbConnection CreateInMemoryDatabase(string connStr) + //{ + // var connection = new InMemoryConnection(connStr); + // connection.Open(); + // return connection; + //} + + public override Action> SetUseDatabase => + (optionsBuilder, connectionString, xncfDatabaseData, actionBase) => + { + //Other more configurations + + //Execute UseInMemory (required) + //optionsBuilder.UseInMemory(CreateInMemoryDatabase(connectionString), actionBase); + + optionsBuilder.UseInMemoryDatabase(connectionString); + }; + + public override Action DbContextOptionsActionExtension => (builder, xncfDatabaseData) => + { + //More independent configuration of database operations (not required) + }; + + + public override string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) + { + //No need to use + return null; + } + + public override string GetDropTableSql(DbContext dbContext, string tableName) + { + //No need to use + return null; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Database.InMemory/InMemoryDbContextOptionsBuilderForNcf.cs b/src/Basic/Senparc.Ncf.Database.InMemory/InMemoryDbContextOptionsBuilderForNcf.cs index 6ddcf3797..d3d49846a 100644 --- a/src/Basic/Senparc.Ncf.Database.InMemory/InMemoryDbContextOptionsBuilderForNcf.cs +++ b/src/Basic/Senparc.Ncf.Database.InMemory/InMemoryDbContextOptionsBuilderForNcf.cs @@ -1,102 +1,102 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; -using Microsoft.EntityFrameworkCore; - -namespace Microsoft.EntityFrameworkCore.Infrastructure -{ - public class InMemoryDbContextOptionsBuilderForNcf : RelationalDbContextOptionsBuilder - { - // - // 摘要: - // Clones the configuration in this builder. - // - // 返回结果: - // The cloned configuration. - protected virtual DbContextOptionsBuilder OptionsBuilder { get; } - - //DbContextOptionsBuilder IInMemoryDbContextOptionsBuilderInfrastructure.OptionsBuilder => OptionsBuilder; - - // - // 摘要: - // Initializes a new instance of the Microsoft.EntityFrameworkCore.Infrastructure.InMemoryDbContextOptionsBuilder - // class. - // - // 参数: - // optionsBuilder: - // The options builder. - public InMemoryDbContextOptionsBuilderForNcf(DbContextOptionsBuilder optionsBuilder) - : base(optionsBuilder) - { - OptionsBuilder = optionsBuilder; - } - - // - // 摘要: - // Enables nullability check for all properties across all entities within the in-memory - // database. - // - // 参数: - // nullChecksEnabled: - // If true, then nullability check is enforced. - // - // 返回结果: - // The same builder instance so that multiple calls can be chained. - // - // 言论: - // See Using DbContextOptions, and The EF Core in-memory database provider for more - // information and examples. - public virtual InMemoryDbContextOptionsBuilderForNcf EnableNullChecks(bool nullChecksEnabled = true) - { - InMemoryOptionsExtension inMemoryOptionsExtension = OptionsBuilder.Options.FindExtension() ?? new InMemoryOptionsExtension(); - inMemoryOptionsExtension = inMemoryOptionsExtension.WithNullabilityCheckEnabled(nullChecksEnabled); - ((IDbContextOptionsBuilderInfrastructure)OptionsBuilder).AddOrUpdateExtension(inMemoryOptionsExtension); - return this; - } - - // - // 摘要: - // Returns a string that represents the current object. - // - // 返回结果: - // A string that represents the current object. - [EditorBrowsable(EditorBrowsableState.Never)] - public override string? ToString() - { - return base.ToString(); - } - - // - // 摘要: - // Determines whether the specified object is equal to the current object. - // - // 参数: - // obj: - // The object to compare with the current object. - // - // 返回结果: - // true if the specified object is equal to the current object; otherwise, false. - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object? obj) - { - return base.Equals(obj); - } - - // - // 摘要: - // Serves as the default hash function. - // - // 返回结果: - // A hash code for the current object. - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() - { - return base.GetHashCode(); - } - } -} +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore; + +namespace Microsoft.EntityFrameworkCore.Infrastructure +{ + public class InMemoryDbContextOptionsBuilderForNcf : RelationalDbContextOptionsBuilder + { + // + // summary: + // Clones the configuration in this builder. + // + // Return results: + // The cloned configuration. + protected virtual DbContextOptionsBuilder OptionsBuilder { get; } + + //DbContextOptionsBuilder IInMemoryDbContextOptionsBuilderInfrastructure.OptionsBuilder => OptionsBuilder; + + // + // summary: + // Initializes a new instance of the Microsoft.EntityFrameworkCore.Infrastructure.InMemoryDbContextOptionsBuilder + // class. + // + // parameter: + // optionsBuilder: + // The options builder. + public InMemoryDbContextOptionsBuilderForNcf(DbContextOptionsBuilder optionsBuilder) + : base(optionsBuilder) + { + OptionsBuilder = optionsBuilder; + } + + // + // summary: + // Enables nullability check for all properties across all entities within the in-memory + // database. + // + // parameter: + // nullChecksEnabled: + // If true, then nullability check is enforced. + // + // Return results: + // The same builder instance so that multiple calls can be chained. + // + // Speech: + // See Using DbContextOptions, and The EF Core in-memory database provider for more + // information and examples. + public virtual InMemoryDbContextOptionsBuilderForNcf EnableNullChecks(bool nullChecksEnabled = true) + { + InMemoryOptionsExtension inMemoryOptionsExtension = OptionsBuilder.Options.FindExtension() ?? new InMemoryOptionsExtension(); + inMemoryOptionsExtension = inMemoryOptionsExtension.WithNullabilityCheckEnabled(nullChecksEnabled); + ((IDbContextOptionsBuilderInfrastructure)OptionsBuilder).AddOrUpdateExtension(inMemoryOptionsExtension); + return this; + } + + // + // summary: + // Returns a string that represents the current object. + // + // Return results: + // A string that represents the current object. + [EditorBrowsable(EditorBrowsableState.Never)] + public override string? ToString() + { + return base.ToString(); + } + + // + // summary: + // Determines whether the specified object is equal to the current object. + // + // parameter: + // obj: + // The object to compare with the current object. + // + // Return results: + // true if the specified object is equal to the current object; otherwise, false. + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object? obj) + { + return base.Equals(obj); + } + + // + // summary: + // Serves as the default hash function. + // + // Return results: + // A hash code for the current object. + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() + { + return base.GetHashCode(); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Database.InMemory/InMemoryOptionsExtensionForNcf.cs b/src/Basic/Senparc.Ncf.Database.InMemory/InMemoryOptionsExtensionForNcf.cs index a75769ea7..d1c369add 100644 --- a/src/Basic/Senparc.Ncf.Database.InMemory/InMemoryOptionsExtensionForNcf.cs +++ b/src/Basic/Senparc.Ncf.Database.InMemory/InMemoryOptionsExtensionForNcf.cs @@ -1,242 +1,242 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Reflection; -using System.Text; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json.Linq; - -namespace Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal -{ - public class InMemoryOptionsExtensionForNcf : RelationalOptionsExtension - { - private sealed class ExtensionInfo : DbContextOptionsExtensionInfo - { - private string _logFragment; - - private new InMemoryOptionsExtensionForNcf Extension => (InMemoryOptionsExtensionForNcf)base.Extension; - - public override bool IsDatabaseProvider => true; - - public override string LogFragment - { - get - { - if (_logFragment == null) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.Append("StoreName=").Append(Extension.StoreName).Append(' '); - if (!Extension.IsNullabilityCheckEnabled) - { - stringBuilder.Append("NullabilityChecksEnabled "); - } - - _logFragment = stringBuilder.ToString(); - } - - return _logFragment; - } - } - - public ExtensionInfo(IDbContextOptionsExtension extension) - : base(extension) - { - } - - public override int GetServiceProviderHashCode() - { - HashCode hashCode = default(HashCode); - hashCode.Add(Extension.DatabaseRoot); - hashCode.Add(Extension.IsNullabilityCheckEnabled); - return hashCode.ToHashCode(); - } - - public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) - { - if (other is ExtensionInfo extensionInfo && Extension.DatabaseRoot == extensionInfo.Extension.DatabaseRoot) - { - return Extension.IsNullabilityCheckEnabled == extensionInfo.Extension.IsNullabilityCheckEnabled; - } - - return false; - } - - public override void PopulateDebugInfo(IDictionary debugInfo) - { - debugInfo["InMemoryDatabase:DatabaseRoot"] = (Extension.DatabaseRoot?.GetHashCode() ?? 0).ToString(CultureInfo.InvariantCulture); - debugInfo["InMemoryDatabase:NullabilityChecksEnabled"] = (!Extension.IsNullabilityCheckEnabled).GetHashCode().ToString(CultureInfo.InvariantCulture); - } - } - - private string _storeName; - - private bool _nullabilityCheckEnabled; - - private InMemoryDatabaseRoot _databaseRoot; - - private DbContextOptionsExtensionInfo _info; - - // - // 摘要: - // This is an internal API that supports the Entity Framework Core infrastructure - // and not subject to the same compatibility standards as public APIs. It may be - // changed or removed without notice in any release. You should only use it directly - // in your code with extreme caution and knowing that doing so can result in application - // failures when updating to a new Entity Framework Core release. - public override DbContextOptionsExtensionInfo Info => _info ?? (_info = new ExtensionInfo(this)); - - // - // 摘要: - // This is an internal API that supports the Entity Framework Core infrastructure - // and not subject to the same compatibility standards as public APIs. It may be - // changed or removed without notice in any release. You should only use it directly - // in your code with extreme caution and knowing that doing so can result in application - // failures when updating to a new Entity Framework Core release. - public virtual string StoreName => _storeName; - - // - // 摘要: - // This is an internal API that supports the Entity Framework Core infrastructure - // and not subject to the same compatibility standards as public APIs. It may be - // changed or removed without notice in any release. You should only use it directly - // in your code with extreme caution and knowing that doing so can result in application - // failures when updating to a new Entity Framework Core release. - public virtual bool IsNullabilityCheckEnabled => _nullabilityCheckEnabled; - - // - // 摘要: - // This is an internal API that supports the Entity Framework Core infrastructure - // and not subject to the same compatibility standards as public APIs. It may be - // changed or removed without notice in any release. You should only use it directly - // in your code with extreme caution and knowing that doing so can result in application - // failures when updating to a new Entity Framework Core release. - public virtual InMemoryDatabaseRoot? DatabaseRoot => _databaseRoot; - - // - // 摘要: - // This is an internal API that supports the Entity Framework Core infrastructure - // and not subject to the same compatibility standards as public APIs. It may be - // changed or removed without notice in any release. You should only use it directly - // in your code with extreme caution and knowing that doing so can result in application - // failures when updating to a new Entity Framework Core release. - public InMemoryOptionsExtensionForNcf() - { - } - - // - // 摘要: - // This is an internal API that supports the Entity Framework Core infrastructure - // and not subject to the same compatibility standards as public APIs. It may be - // changed or removed without notice in any release. You should only use it directly - // in your code with extreme caution and knowing that doing so can result in application - // failures when updating to a new Entity Framework Core release. - protected InMemoryOptionsExtensionForNcf(InMemoryOptionsExtensionForNcf copyFrom) - { - _storeName = copyFrom.StoreName; - _databaseRoot = copyFrom._databaseRoot; - } - - // - // 摘要: - // This is an internal API that supports the Entity Framework Core infrastructure - // and not subject to the same compatibility standards as public APIs. It may be - // changed or removed without notice in any release. You should only use it directly - // in your code with extreme caution and knowing that doing so can result in application - // failures when updating to a new Entity Framework Core release. - //protected virtual InMemoryOptionsExtensionForNcf Clone() - //{ - // return new InMemoryOptionsExtensionForNcf(this); - //} - - // - // 摘要: - // This is an internal API that supports the Entity Framework Core infrastructure - // and not subject to the same compatibility standards as public APIs. It may be - // changed or removed without notice in any release. You should only use it directly - // in your code with extreme caution and knowing that doing so can result in application - // failures when updating to a new Entity Framework Core release. - public virtual InMemoryOptionsExtensionForNcf WithStoreName(string storeName) - { - InMemoryOptionsExtensionForNcf inMemoryOptionsExtension = Clone() as InMemoryOptionsExtensionForNcf; - - var fieldInfo = typeof(InMemoryOptionsExtensionForNcf).GetField("_storeName", BindingFlags.NonPublic | BindingFlags.Instance); - if (fieldInfo != null) - { - fieldInfo.SetValue(inMemoryOptionsExtension, storeName); - } - - return inMemoryOptionsExtension; - } - - // - // 摘要: - // This is an internal API that supports the Entity Framework Core infrastructure - // and not subject to the same compatibility standards as public APIs. It may be - // changed or removed without notice in any release. You should only use it directly - // in your code with extreme caution and knowing that doing so can result in application - // failures when updating to a new Entity Framework Core release. - public virtual InMemoryOptionsExtensionForNcf WithNullabilityCheckEnabled(bool nullabilityCheckEnabled) - { - InMemoryOptionsExtensionForNcf inMemoryOptionsExtension = Clone() as InMemoryOptionsExtensionForNcf; - - var fieldInfo = typeof(InMemoryOptionsExtensionForNcf).GetField("_isNullabilityCheckEnabled", BindingFlags.NonPublic | BindingFlags.Instance); - if (fieldInfo != null) - { - fieldInfo.SetValue(inMemoryOptionsExtension, nullabilityCheckEnabled); - } - - return inMemoryOptionsExtension; - } - - // - // 摘要: - // This is an internal API that supports the Entity Framework Core infrastructure - // and not subject to the same compatibility standards as public APIs. It may be - // changed or removed without notice in any release. You should only use it directly - // in your code with extreme caution and knowing that doing so can result in application - // failures when updating to a new Entity Framework Core release. - public virtual InMemoryOptionsExtensionForNcf WithDatabaseRoot(InMemoryDatabaseRoot databaseRoot) - { - InMemoryOptionsExtensionForNcf inMemoryOptionsExtension = Clone() as InMemoryOptionsExtensionForNcf; - - var fieldInfo = typeof(InMemoryOptionsExtensionForNcf).GetField("_databaseRoot", BindingFlags.NonPublic | BindingFlags.Instance); - if (fieldInfo != null) - { - fieldInfo.SetValue(inMemoryOptionsExtension, databaseRoot); - } - - return inMemoryOptionsExtension; - } - - // - // 摘要: - // This is an internal API that supports the Entity Framework Core infrastructure - // and not subject to the same compatibility standards as public APIs. It may be - // changed or removed without notice in any release. You should only use it directly - // in your code with extreme caution and knowing that doing so can result in application - // failures when updating to a new Entity Framework Core release. - public override void ApplyServices(IServiceCollection services) - { - services.AddEntityFrameworkInMemoryDatabase(); - } - - // - // 摘要: - // This is an internal API that supports the Entity Framework Core infrastructure - // and not subject to the same compatibility standards as public APIs. It may be - // changed or removed without notice in any release. You should only use it directly - // in your code with extreme caution and knowing that doing so can result in application - // failures when updating to a new Entity Framework Core release. - public virtual void Validate(IDbContextOptions options) - { - - } - - protected override RelationalOptionsExtension Clone() - { - return new InMemoryOptionsExtensionForNcf(this); - } - } -} +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Text; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json.Linq; + +namespace Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal +{ + public class InMemoryOptionsExtensionForNcf : RelationalOptionsExtension + { + private sealed class ExtensionInfo : DbContextOptionsExtensionInfo + { + private string _logFragment; + + private new InMemoryOptionsExtensionForNcf Extension => (InMemoryOptionsExtensionForNcf)base.Extension; + + public override bool IsDatabaseProvider => true; + + public override string LogFragment + { + get + { + if (_logFragment == null) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.Append("StoreName=").Append(Extension.StoreName).Append(' '); + if (!Extension.IsNullabilityCheckEnabled) + { + stringBuilder.Append("NullabilityChecksEnabled "); + } + + _logFragment = stringBuilder.ToString(); + } + + return _logFragment; + } + } + + public ExtensionInfo(IDbContextOptionsExtension extension) + : base(extension) + { + } + + public override int GetServiceProviderHashCode() + { + HashCode hashCode = default(HashCode); + hashCode.Add(Extension.DatabaseRoot); + hashCode.Add(Extension.IsNullabilityCheckEnabled); + return hashCode.ToHashCode(); + } + + public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) + { + if (other is ExtensionInfo extensionInfo && Extension.DatabaseRoot == extensionInfo.Extension.DatabaseRoot) + { + return Extension.IsNullabilityCheckEnabled == extensionInfo.Extension.IsNullabilityCheckEnabled; + } + + return false; + } + + public override void PopulateDebugInfo(IDictionary debugInfo) + { + debugInfo["InMemoryDatabase:DatabaseRoot"] = (Extension.DatabaseRoot?.GetHashCode() ?? 0).ToString(CultureInfo.InvariantCulture); + debugInfo["InMemoryDatabase:NullabilityChecksEnabled"] = (!Extension.IsNullabilityCheckEnabled).GetHashCode().ToString(CultureInfo.InvariantCulture); + } + } + + private string _storeName; + + private bool _nullabilityCheckEnabled; + + private InMemoryDatabaseRoot _databaseRoot; + + private DbContextOptionsExtensionInfo _info; + + // + // summary: + // This is an internal API that supports the Entity Framework Core infrastructure + // and not subject to the same compatibility standards as public APIs. It may be + // changed or removed without notice in any release. You should only use it directly + // in your code with extreme caution and knowing that doing so can result in application + // failures when updating to a new Entity Framework Core release. + public override DbContextOptionsExtensionInfo Info => _info ?? (_info = new ExtensionInfo(this)); + + // + // summary: + // This is an internal API that supports the Entity Framework Core infrastructure + // and not subject to the same compatibility standards as public APIs. It may be + // changed or removed without notice in any release. You should only use it directly + // in your code with extreme caution and knowing that doing so can result in application + // failures when updating to a new Entity Framework Core release. + public virtual string StoreName => _storeName; + + // + // summary: + // This is an internal API that supports the Entity Framework Core infrastructure + // and not subject to the same compatibility standards as public APIs. It may be + // changed or removed without notice in any release. You should only use it directly + // in your code with extreme caution and knowing that doing so can result in application + // failures when updating to a new Entity Framework Core release. + public virtual bool IsNullabilityCheckEnabled => _nullabilityCheckEnabled; + + // + // summary: + // This is an internal API that supports the Entity Framework Core infrastructure + // and not subject to the same compatibility standards as public APIs. It may be + // changed or removed without notice in any release. You should only use it directly + // in your code with extreme caution and knowing that doing so can result in application + // failures when updating to a new Entity Framework Core release. + public virtual InMemoryDatabaseRoot? DatabaseRoot => _databaseRoot; + + // + // summary: + // This is an internal API that supports the Entity Framework Core infrastructure + // and not subject to the same compatibility standards as public APIs. It may be + // changed or removed without notice in any release. You should only use it directly + // in your code with extreme caution and knowing that doing so can result in application + // failures when updating to a new Entity Framework Core release. + public InMemoryOptionsExtensionForNcf() + { + } + + // + // summary: + // This is an internal API that supports the Entity Framework Core infrastructure + // and not subject to the same compatibility standards as public APIs. It may be + // changed or removed without notice in any release. You should only use it directly + // in your code with extreme caution and knowing that doing so can result in application + // failures when updating to a new Entity Framework Core release. + protected InMemoryOptionsExtensionForNcf(InMemoryOptionsExtensionForNcf copyFrom) + { + _storeName = copyFrom.StoreName; + _databaseRoot = copyFrom._databaseRoot; + } + + // + // summary: + // This is an internal API that supports the Entity Framework Core infrastructure + // and not subject to the same compatibility standards as public APIs. It may be + // changed or removed without notice in any release. You should only use it directly + // in your code with extreme caution and knowing that doing so can result in application + // failures when updating to a new Entity Framework Core release. + //protected virtual InMemoryOptionsExtensionForNcf Clone() + //{ + // return new InMemoryOptionsExtensionForNcf(this); + //} + + // + // summary: + // This is an internal API that supports the Entity Framework Core infrastructure + // and not subject to the same compatibility standards as public APIs. It may be + // changed or removed without notice in any release. You should only use it directly + // in your code with extreme caution and knowing that doing so can result in application + // failures when updating to a new Entity Framework Core release. + public virtual InMemoryOptionsExtensionForNcf WithStoreName(string storeName) + { + InMemoryOptionsExtensionForNcf inMemoryOptionsExtension = Clone() as InMemoryOptionsExtensionForNcf; + + var fieldInfo = typeof(InMemoryOptionsExtensionForNcf).GetField("_storeName", BindingFlags.NonPublic | BindingFlags.Instance); + if (fieldInfo != null) + { + fieldInfo.SetValue(inMemoryOptionsExtension, storeName); + } + + return inMemoryOptionsExtension; + } + + // + // summary: + // This is an internal API that supports the Entity Framework Core infrastructure + // and not subject to the same compatibility standards as public APIs. It may be + // changed or removed without notice in any release. You should only use it directly + // in your code with extreme caution and knowing that doing so can result in application + // failures when updating to a new Entity Framework Core release. + public virtual InMemoryOptionsExtensionForNcf WithNullabilityCheckEnabled(bool nullabilityCheckEnabled) + { + InMemoryOptionsExtensionForNcf inMemoryOptionsExtension = Clone() as InMemoryOptionsExtensionForNcf; + + var fieldInfo = typeof(InMemoryOptionsExtensionForNcf).GetField("_isNullabilityCheckEnabled", BindingFlags.NonPublic | BindingFlags.Instance); + if (fieldInfo != null) + { + fieldInfo.SetValue(inMemoryOptionsExtension, nullabilityCheckEnabled); + } + + return inMemoryOptionsExtension; + } + + // + // summary: + // This is an internal API that supports the Entity Framework Core infrastructure + // and not subject to the same compatibility standards as public APIs. It may be + // changed or removed without notice in any release. You should only use it directly + // in your code with extreme caution and knowing that doing so can result in application + // failures when updating to a new Entity Framework Core release. + public virtual InMemoryOptionsExtensionForNcf WithDatabaseRoot(InMemoryDatabaseRoot databaseRoot) + { + InMemoryOptionsExtensionForNcf inMemoryOptionsExtension = Clone() as InMemoryOptionsExtensionForNcf; + + var fieldInfo = typeof(InMemoryOptionsExtensionForNcf).GetField("_databaseRoot", BindingFlags.NonPublic | BindingFlags.Instance); + if (fieldInfo != null) + { + fieldInfo.SetValue(inMemoryOptionsExtension, databaseRoot); + } + + return inMemoryOptionsExtension; + } + + // + // summary: + // This is an internal API that supports the Entity Framework Core infrastructure + // and not subject to the same compatibility standards as public APIs. It may be + // changed or removed without notice in any release. You should only use it directly + // in your code with extreme caution and knowing that doing so can result in application + // failures when updating to a new Entity Framework Core release. + public override void ApplyServices(IServiceCollection services) + { + services.AddEntityFrameworkInMemoryDatabase(); + } + + // + // summary: + // This is an internal API that supports the Entity Framework Core infrastructure + // and not subject to the same compatibility standards as public APIs. It may be + // changed or removed without notice in any release. You should only use it directly + // in your code with extreme caution and knowing that doing so can result in application + // failures when updating to a new Entity Framework Core release. + public virtual void Validate(IDbContextOptions options) + { + + } + + protected override RelationalOptionsExtension Clone() + { + return new InMemoryOptionsExtensionForNcf(this); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Database.MySql.Backup/MySqlWithBackupDatabaseConfiguration.cs b/src/Basic/Senparc.Ncf.Database.MySql.Backup/MySqlWithBackupDatabaseConfiguration.cs index 2b89be3b6..f75c99997 100644 --- a/src/Basic/Senparc.Ncf.Database.MySql.Backup/MySqlWithBackupDatabaseConfiguration.cs +++ b/src/Basic/Senparc.Ncf.Database.MySql.Backup/MySqlWithBackupDatabaseConfiguration.cs @@ -1,47 +1,47 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Senparc.Ncf.Database; -using Senparc.Ncf.Core.Exceptions; -using System; -using System.Collections.Generic; -using System.Text; -using Pomelo.EntityFrameworkCore.MySql.Infrastructure.Internal; -using Senparc.Ncf.Database.MultipleMigrationDbContext; -using Senparc.Ncf.Core.Models; -using System.Data.Common; -using MySql.Data.MySqlClient; -using Senparc.Ncf.Database.MySql; - -namespace Senparc.Ncf.Database.MySql.Backup -{ - /// - /// MySQL(附带备份) 数据库配置,处于等待官方更新中,目前无效 - /// - public class MySqlWithBackupDatabaseConfiguration : MySqlDatabaseConfiguration - { - public MySqlWithBackupDatabaseConfiguration() { } - - - public override string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) - { - //需要等 Pomelo.EntityFrameworkCore.MySql 5.0才能支持:https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues/1226 - - //string constring = Senparc.Ncf.Core.Config.SenparcDatabaseConnectionConfigs.ClientConnectionString; - //using (var conn = new MySqlClient.MySqlConnection(constring)) - //{ - // using (var cmd = new global:: MySql.Data.MySqlClient.MySqlCommand()) - // { - // using (MySqlBackup mb = new MySqlBackup(cmd)) - // { - // cmd.Connection = conn; - // conn.Open(); - // mb.ExportToFile(backupFilePath); - // conn.Close(); - // } - // } - //} - - return null; - } - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Senparc.Ncf.Database; +using Senparc.Ncf.Core.Exceptions; +using System; +using System.Collections.Generic; +using System.Text; +using Pomelo.EntityFrameworkCore.MySql.Infrastructure.Internal; +using Senparc.Ncf.Database.MultipleMigrationDbContext; +using Senparc.Ncf.Core.Models; +using System.Data.Common; +using MySql.Data.MySqlClient; +using Senparc.Ncf.Database.MySql; + +namespace Senparc.Ncf.Database.MySql.Backup +{ + /// + /// MySQL(Backup included) Database configuration, waiting for official update, currently invalid + /// + public class MySqlWithBackupDatabaseConfiguration : MySqlDatabaseConfiguration + { + public MySqlWithBackupDatabaseConfiguration() { } + + + public override string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) + { + //Need to wait Pomelo.EntityFrameworkCore.MySql 5.0To support: https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues/1226 + + //string constring = Senparc.Ncf.Core.Config.SenparcDatabaseConnectionConfigs.ClientConnectionString; + //using (var conn = new MySqlClient.MySqlConnection(constring)) + //{ + // using (var cmd = new global:: MySql.Data.MySqlClient.MySqlCommand()) + // { + // using (MySqlBackup mb = new MySqlBackup(cmd)) + // { + // cmd.Connection = conn; + // conn.Open(); + // mb.ExportToFile(backupFilePath); + // conn.Close(); + // } + // } + //} + + return null; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Database.MySql/MySqlDatabaseConfiguration.cs b/src/Basic/Senparc.Ncf.Database.MySql/MySqlDatabaseConfiguration.cs index aeac75070..a9af65e20 100644 --- a/src/Basic/Senparc.Ncf.Database.MySql/MySqlDatabaseConfiguration.cs +++ b/src/Basic/Senparc.Ncf.Database.MySql/MySqlDatabaseConfiguration.cs @@ -1,52 +1,52 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Senparc.Ncf.Database; -using Senparc.Ncf.Core.Exceptions; -using System; -using System.Collections.Generic; -using System.Text; -using Pomelo.EntityFrameworkCore.MySql.Infrastructure.Internal; -using Senparc.Ncf.Database.MultipleMigrationDbContext; -using Senparc.Ncf.Core.Models; -using System.Data.Common; - -namespace Senparc.Ncf.Database.MySql -{ - /// - /// MySQL 数据库配置 - /// - public class MySqlDatabaseConfiguration : DatabaseConfigurationBase - { - public MySqlDatabaseConfiguration() { } - - public override MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.MySql; - - public override Action DbContextOptionsActionExtension => (optionsBuilder, xncfDatabaseData) => - { - var typedBuilder = optionsBuilder as MySqlDbContextOptionsBuilder; - typedBuilder.EnableRetryOnFailure( - maxRetryCount: 5, - maxRetryDelay: TimeSpan.FromSeconds(5), - errorNumbersToAdd: new int[] { 2 }); - }; - - public override Action> SetUseDatabase => - (optionsBuilder, connectionString, xncfDatabaseData, actionBase) => - { - optionsBuilder.UseMySql(connectionString, - //ServerVersion 用法:https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/pull/1233 - ServerVersion.AutoDetect(connectionString), - actionBase);//beta6 - }; - - public override string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) - { - throw new NcfDatabaseException("Pomelo.EntityFrameworkCore.MySql 暂时不支持运行时备份,请使用命令行进行备份。Pomelo.EntityFrameworkCore.MySql v5.0 之后可支持。", DatabaseConfigurationFactory.Instance.Current.GetType(), null); - } - - public override string GetDropTableSql(DbContext dbContext, string tableName) - { - return $"DROP TABLE `{tableName}`"; - } - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Senparc.Ncf.Database; +using Senparc.Ncf.Core.Exceptions; +using System; +using System.Collections.Generic; +using System.Text; +using Pomelo.EntityFrameworkCore.MySql.Infrastructure.Internal; +using Senparc.Ncf.Database.MultipleMigrationDbContext; +using Senparc.Ncf.Core.Models; +using System.Data.Common; + +namespace Senparc.Ncf.Database.MySql +{ + /// + ///MySQL database configuration + /// + public class MySqlDatabaseConfiguration : DatabaseConfigurationBase + { + public MySqlDatabaseConfiguration() { } + + public override MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.MySql; + + public override Action DbContextOptionsActionExtension => (optionsBuilder, xncfDatabaseData) => + { + var typedBuilder = optionsBuilder as MySqlDbContextOptionsBuilder; + typedBuilder.EnableRetryOnFailure( + maxRetryCount: 5, + maxRetryDelay: TimeSpan.FromSeconds(5), + errorNumbersToAdd: new int[] { 2 }); + }; + + public override Action> SetUseDatabase => + (optionsBuilder, connectionString, xncfDatabaseData, actionBase) => + { + optionsBuilder.UseMySql(connectionString, + //ServerVersion usage: https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/pull/1233 + ServerVersion.AutoDetect(connectionString), + actionBase);//beta6 + }; + + public override string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) + { + throw new NcfDatabaseException("Pomelo.EntityFrameworkCore.MySql 暂时不支持运行时备份,请使用命令行进行备份。Pomelo.EntityFrameworkCore.MySql v5.0 之后可支持。", DatabaseConfigurationFactory.Instance.Current.GetType(), null); + } + + public override string GetDropTableSql(DbContext dbContext, string tableName) + { + return $"DROP TABLE `{tableName}`"; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Database.Oracle/OracleDatabaseConfiguration.cs b/src/Basic/Senparc.Ncf.Database.Oracle/OracleDatabaseConfiguration.cs index 310dacabc..803cee1e6 100644 --- a/src/Basic/Senparc.Ncf.Database.Oracle/OracleDatabaseConfiguration.cs +++ b/src/Basic/Senparc.Ncf.Database.Oracle/OracleDatabaseConfiguration.cs @@ -1,88 +1,88 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Senparc.Ncf.Database; -using Senparc.Ncf.Core.Exceptions; -using System; -using System.Collections.Generic; -using System.Text; -using Senparc.Ncf.Database.MultipleMigrationDbContext; -using Senparc.Ncf.Core.Models; -using System.Data.Common; -using Oracle.EntityFrameworkCore.Infrastructure; -using Oracle.EntityFrameworkCore.Infrastructure.Internal; -using Senparc.CO2NET.Extensions; - -namespace Senparc.Ncf.Database.Oracle -{ - /// - /// Oracle 数据库配置,Oracle 版本不小于 V12 - /// 注意:如果使用 Oracle 12 以下的版本(11),请直接使用 OracleDatabaseConfigurationForV11。或使用手动方法控制(不推荐):在调用 services.AddDatabase<OracleDatabaseConfiguration>(); 之前,使用 SetUseOracleSQLCompatibility(string useOracleSQLCompatibility) 方法设置版本号,如 11.2,则输入 "11" - /// - public class OracleDatabaseConfiguration : DatabaseConfigurationBase - { - private static string _useOracleSQLCompatibility = null; - - /// - /// 设置 UseOracleSQLCompatibility 的参数,如 11.2g,输入"11",12g,输入"12"(默认为 >= 12,此时可不输入) - /// 注意:此设置应该在执行 .AddDatabase<OracleDatabaseConfiguration>(); 之前执行 - /// - /// - public static void SetUseOracleSQLCompatibility(string useOracleSQLCompatibility) - { - _useOracleSQLCompatibility = useOracleSQLCompatibility; - } - - private static OracleSQLCompatibility _oracleSQLCompatibility = OracleSQLCompatibility.DatabaseVersion19; - - - /// - /// 设置 OracleSQLCompatibility 的参数,默认为 19 - /// 注意:此设置应该在执行 .AddDatabase<OracleDatabaseConfiguration>(); 之前执行 - /// - /// - public static void SetUseOracleSQLCompatibility(OracleSQLCompatibility oracleSQLCompatibility) - { - _oracleSQLCompatibility = oracleSQLCompatibility; - } - - public OracleDatabaseConfiguration() { } - - public override MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.Oracle; - - public override Action DbContextOptionsActionExtension => (optionsBuilder, xncfDatabaseData) => - { - var typedBuilder = optionsBuilder as OracleDbContextOptionsBuilder; - - //This method accepts either a value of "11" or "12" (default). By default, generated SQL is compatible with database version 12 and higher. Customers using Oracle Database version 11.2 should set UseOracleSQLCompatibility("11"). - //https://docs.oracle.com/en/database/oracle/oracle-data-access-components/19.3/odpnt/EFCoreAPI.html#GUID-66247629-2670-44AA-AC55-849C367852AF -//#if NETSTANDARD -// if (!_useOracleSQLCompatibility.IsNullOrEmpty()) -// { -// typedBuilder.UseOracleSQLCompatibility(_useOracleSQLCompatibility); -// } -//#else - typedBuilder.UseOracleSQLCompatibility(_oracleSQLCompatibility); -//#endif - }; - - public override Action> SetUseDatabase => - (optionsBuilder, connectionString, xncfDatabaseData, actionBase) => - { - optionsBuilder.UseOracle(connectionString, actionBase);//beta6 - }; - - public override string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) - { - return $@"Backup Database {dbConnection.Database} To disk='{backupFilePath}'"; - } - - public override string GetDropTableSql(DbContext dbContext, string tableName) - { - //var schma = dbContext.Model.FindEntityType(type).GetSchema(); - //TODO: 增加 schma - return $"DROP TABLE {tableName}"; - } - - - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Senparc.Ncf.Database; +using Senparc.Ncf.Core.Exceptions; +using System; +using System.Collections.Generic; +using System.Text; +using Senparc.Ncf.Database.MultipleMigrationDbContext; +using Senparc.Ncf.Core.Models; +using System.Data.Common; +using Oracle.EntityFrameworkCore.Infrastructure; +using Oracle.EntityFrameworkCore.Infrastructure.Internal; +using Senparc.CO2NET.Extensions; + +namespace Senparc.Ncf.Database.Oracle +{ + /// + ///Oracle database configuration, Oracle version is not less than V12 + /// Note: If you use a version below Oracle 12 (11), please use OracleDatabaseConfigurationForV11 directly. Or use manual method control (not recommended): Before calling services.AddDatabase<OracleDatabaseConfiguration>();, use the SetUseOracleSQLCompatibility(string useOracleSQLCompatibility) method to set the version number, such as 11.2, enter "11" + /// + public class OracleDatabaseConfiguration : DatabaseConfigurationBase + { + private static string _useOracleSQLCompatibility = null; + + /// + /// Set the parameters of UseOracleSQLCompatibility, such as 11.2g, enter "11", 12g, enter "12" (the default is >= 12, you don’t need to enter it at this time) + /// Note: This setting should be performed before executing .AddDatabase<OracleDatabaseConfiguration>(); + /// + /// + public static void SetUseOracleSQLCompatibility(string useOracleSQLCompatibility) + { + _useOracleSQLCompatibility = useOracleSQLCompatibility; + } + + private static OracleSQLCompatibility _oracleSQLCompatibility = OracleSQLCompatibility.DatabaseVersion19; + + + /// + ///Set the parameters of OracleSQLCompatibility, the default is 19 + /// Note: This setting should be performed before executing .AddDatabase<OracleDatabaseConfiguration>(); + /// + /// + public static void SetUseOracleSQLCompatibility(OracleSQLCompatibility oracleSQLCompatibility) + { + _oracleSQLCompatibility = oracleSQLCompatibility; + } + + public OracleDatabaseConfiguration() { } + + public override MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.Oracle; + + public override Action DbContextOptionsActionExtension => (optionsBuilder, xncfDatabaseData) => + { + var typedBuilder = optionsBuilder as OracleDbContextOptionsBuilder; + + //This method accepts either a value of "11" or "12" (default). By default, generated SQL is compatible with database version 12 and higher. Customers using Oracle Database version 11.2 should set UseOracleSQLCompatibility("11"). + //https://docs.oracle.com/en/database/oracle/oracle-data-access-components/19.3/odpnt/EFCoreAPI.html#GUID-66247629-2670-44AA-AC55-849C367852AF +//#if NETSTANDARD +// if (!_useOracleSQLCompatibility.IsNullOrEmpty()) +// { +// typedBuilder.UseOracleSQLCompatibility(_useOracleSQLCompatibility); +// } +//#else + typedBuilder.UseOracleSQLCompatibility(_oracleSQLCompatibility); +//#endif + }; + + public override Action> SetUseDatabase => + (optionsBuilder, connectionString, xncfDatabaseData, actionBase) => + { + optionsBuilder.UseOracle(connectionString, actionBase);//beta6 + }; + + public override string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) + { + return $@"Backup Database {dbConnection.Database} To disk='{backupFilePath}'"; + } + + public override string GetDropTableSql(DbContext dbContext, string tableName) + { + //var schma = dbContext.Model.FindEntityType(type).GetSchema(); + //TODO: Add schma + return $"DROP TABLE {tableName}"; + } + + + } +} diff --git a/src/Basic/Senparc.Ncf.Database.Oracle/OracleDatabaseConfigurationForV11.cs b/src/Basic/Senparc.Ncf.Database.Oracle/OracleDatabaseConfigurationForV11.cs index 921012e19..5aec0b73a 100644 --- a/src/Basic/Senparc.Ncf.Database.Oracle/OracleDatabaseConfigurationForV11.cs +++ b/src/Basic/Senparc.Ncf.Database.Oracle/OracleDatabaseConfigurationForV11.cs @@ -1,30 +1,30 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Senparc.Ncf.Database; -using Senparc.Ncf.Core.Exceptions; -using System; -using System.Collections.Generic; -using System.Text; -using Senparc.Ncf.Database.MultipleMigrationDbContext; -using Senparc.Ncf.Core.Models; -using System.Data.Common; -using Oracle.EntityFrameworkCore.Infrastructure; -using Oracle.EntityFrameworkCore.Infrastructure.Internal; -using Senparc.CO2NET.Extensions; - -namespace Senparc.Ncf.Database.Oracle -{ - /// - /// Oracle V11 数据库配置(包括 V11.2 等版本) - /// 注意:如果使用 Oracle 12 或以上版本,请直接使用 OracleDatabaseConfiguration - /// - public class OracleDatabaseConfigurationForV11 : OracleDatabaseConfiguration - { - public OracleDatabaseConfigurationForV11() : base() - { - OracleDatabaseConfiguration.SetUseOracleSQLCompatibility("11"); - } - - public override MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.Oracle; - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Senparc.Ncf.Database; +using Senparc.Ncf.Core.Exceptions; +using System; +using System.Collections.Generic; +using System.Text; +using Senparc.Ncf.Database.MultipleMigrationDbContext; +using Senparc.Ncf.Core.Models; +using System.Data.Common; +using Oracle.EntityFrameworkCore.Infrastructure; +using Oracle.EntityFrameworkCore.Infrastructure.Internal; +using Senparc.CO2NET.Extensions; + +namespace Senparc.Ncf.Database.Oracle +{ + /// + ///Oracle V11 database configuration (including V11.2 and other versions) + /// Note: If using Oracle 12 or above, please use OracleDatabaseConfiguration directly + /// + public class OracleDatabaseConfigurationForV11 : OracleDatabaseConfiguration + { + public OracleDatabaseConfigurationForV11() : base() + { + OracleDatabaseConfiguration.SetUseOracleSQLCompatibility("11"); + } + + public override MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.Oracle; + } +} diff --git a/src/Basic/Senparc.Ncf.Database.PostgreSQL/PostgreSQLDatabaseConfiguration.cs b/src/Basic/Senparc.Ncf.Database.PostgreSQL/PostgreSQLDatabaseConfiguration.cs index 41fadd46d..b50530e09 100644 --- a/src/Basic/Senparc.Ncf.Database.PostgreSQL/PostgreSQLDatabaseConfiguration.cs +++ b/src/Basic/Senparc.Ncf.Database.PostgreSQL/PostgreSQLDatabaseConfiguration.cs @@ -1,119 +1,119 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal; -using Senparc.Ncf.Database; -using Senparc.Ncf.Core.Exceptions; -using System; -using System.Collections.Generic; -using System.Text; -using Senparc.Ncf.Database.MultipleMigrationDbContext; -using Senparc.Ncf.Core.Models; -using System.Data.Common; -using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; -using Senparc.Ncf.Database.Helpers; -using System.Diagnostics; - -namespace Senparc.Ncf.Database.PostgreSQL -{ - /// - /// PostgreSQL 数据库配置 - /// - public class PostgreSQLDatabaseConfiguration : DatabaseConfigurationBase - { - public PostgreSQLDatabaseConfiguration() { } - - public override MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.PostgreSQL; - - public override Action DbContextOptionsActionExtension => (optionsBuilder, xncfDatabaseData) => - { - var typedBuilder = optionsBuilder as NpgsqlDbContextOptionsBuilder; - - //解决 .NET 6.0 timestamp 问题: https://qiita.com/k-yamamoto/items/a87989569cb6f0415fbb - AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); - - typedBuilder.EnableRetryOnFailure( - maxRetryCount: 5, - maxRetryDelay: TimeSpan.FromSeconds(5), - errorCodesToAdd: new string[] { "2" }); - }; - - public override Action> SetUseDatabase => - (optionsBuilder, connectionString, xncfDatabaseData, actionBase) => - { - optionsBuilder.UseNpgsql(connectionString, actionBase); - }; - - public override string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) - { - //pg_dump.exe --file "F:\\psql.bak" --host "localhost" --port "5432" --username "postgres" --no-password --verbose --format=c --blobs "postgres" - var dic = NcfDatabaseHelper.GetCurrentConnectionInfo(); - - var host = NcfDatabaseHelper.TryGetConnectionValue(dic, "host"); - host = host != null ? $" --host \"{host}\"" : null; - - var port = NcfDatabaseHelper.TryGetConnectionValue(dic, "port"); - port = port != null ? $" --port \"{port}\"" : null; - - var username = NcfDatabaseHelper.TryGetConnectionValue(dic, "username"); - username = username != null ? $" --username \"{username}\"" : null; - - var cmd = $"pg_dump.exe --file \"{backupFilePath}\" {host}{port}{username}"; - - var commandTexts = new List { - cmd, - }; - - Func getNewProcess = () => - { - Process p = new Process(); - p.StartInfo.FileName = "pg_dump.exe"; - p.StartInfo.UseShellExecute = false; - p.StartInfo.RedirectStandardInput = true; - p.StartInfo.RedirectStandardOutput = true; - p.StartInfo.RedirectStandardError = true; - p.StartInfo.CreateNoWindow = true; - p.Start(); - return p; - }; - - Action closeProcess = p => - { - p.WaitForExit(); - p.Close(); - }; - - { - Process cmdProcess = getNewProcess(); - Console.WriteLine(cmd); - cmdProcess.StandardInput.WriteLine(cmd); - cmdProcess.StandardInput.WriteLine("exit");//需要执行exit后才能读取 StandardOutput - var output = cmdProcess.StandardOutput.ReadToEnd(); - Console.WriteLine("pg_dump.exe outupt: " + output); - closeProcess(cmdProcess); - } - - var password = NcfDatabaseHelper.TryGetConnectionValue(dic, "password"); - if (password != null) - { - Process passwordProcess = getNewProcess(); - Console.WriteLine(cmd); - passwordProcess.StandardInput.WriteLine(password); - passwordProcess.StandardInput.WriteLine("exit");//需要执行exit后才能读取 StandardOutput - var output = passwordProcess.StandardOutput.ReadToEnd(); - Console.WriteLine("pg_dump.exe outupt: " + output); - closeProcess(passwordProcess); - } - - return ""; - //return $@"Backup Database {dbConnection.Database} To disk='{backupFilePath}'"; - } - - public override string GetDropTableSql(DbContext dbContext, string tableName) - { - //var schma = dbContext.Model.FindEntityType(type).GetSchema(); - //TODO: 增加 schma - return $"DROP TABLE \"{tableName}\"";//CASCADE - } - - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal; +using Senparc.Ncf.Database; +using Senparc.Ncf.Core.Exceptions; +using System; +using System.Collections.Generic; +using System.Text; +using Senparc.Ncf.Database.MultipleMigrationDbContext; +using Senparc.Ncf.Core.Models; +using System.Data.Common; +using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; +using Senparc.Ncf.Database.Helpers; +using System.Diagnostics; + +namespace Senparc.Ncf.Database.PostgreSQL +{ + /// + /// PostgreSQL database configuration + /// + public class PostgreSQLDatabaseConfiguration : DatabaseConfigurationBase + { + public PostgreSQLDatabaseConfiguration() { } + + public override MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.PostgreSQL; + + public override Action DbContextOptionsActionExtension => (optionsBuilder, xncfDatabaseData) => + { + var typedBuilder = optionsBuilder as NpgsqlDbContextOptionsBuilder; + + //Resolving .NET 6.0 timestamp issues: https://qiita.com/k-yamamoto/items/a87989569cb6f0415fbb + AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); + + typedBuilder.EnableRetryOnFailure( + maxRetryCount: 5, + maxRetryDelay: TimeSpan.FromSeconds(5), + errorCodesToAdd: new string[] { "2" }); + }; + + public override Action> SetUseDatabase => + (optionsBuilder, connectionString, xncfDatabaseData, actionBase) => + { + optionsBuilder.UseNpgsql(connectionString, actionBase); + }; + + public override string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) + { + //pg_dump.exe --file "F:\\psql.bak" --host "localhost" --port "5432" --username "postgres" --no-password --verbose --format=c --blobs "postgres" + var dic = NcfDatabaseHelper.GetCurrentConnectionInfo(); + + var host = NcfDatabaseHelper.TryGetConnectionValue(dic, "host"); + host = host != null ? $" --host \"{host}\"" : null; + + var port = NcfDatabaseHelper.TryGetConnectionValue(dic, "port"); + port = port != null ? $" --port \"{port}\"" : null; + + var username = NcfDatabaseHelper.TryGetConnectionValue(dic, "username"); + username = username != null ? $" --username \"{username}\"" : null; + + var cmd = $"pg_dump.exe --file \"{backupFilePath}\" {host}{port}{username}"; + + var commandTexts = new List { + cmd, + }; + + Func getNewProcess = () => + { + Process p = new Process(); + p.StartInfo.FileName = "pg_dump.exe"; + p.StartInfo.UseShellExecute = false; + p.StartInfo.RedirectStandardInput = true; + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.RedirectStandardError = true; + p.StartInfo.CreateNoWindow = true; + p.Start(); + return p; + }; + + Action closeProcess = p => + { + p.WaitForExit(); + p.Close(); + }; + + { + Process cmdProcess = getNewProcess(); + Console.WriteLine(cmd); + cmdProcess.StandardInput.WriteLine(cmd); + cmdProcess.StandardInput.WriteLine("exit");//You need to execute exit before you can read StandardOutput. + var output = cmdProcess.StandardOutput.ReadToEnd(); + Console.WriteLine("pg_dump.exe outupt: " + output); + closeProcess(cmdProcess); + } + + var password = NcfDatabaseHelper.TryGetConnectionValue(dic, "password"); + if (password != null) + { + Process passwordProcess = getNewProcess(); + Console.WriteLine(cmd); + passwordProcess.StandardInput.WriteLine(password); + passwordProcess.StandardInput.WriteLine("exit");//You need to execute exit before you can read StandardOutput. + var output = passwordProcess.StandardOutput.ReadToEnd(); + Console.WriteLine("pg_dump.exe outupt: " + output); + closeProcess(passwordProcess); + } + + return ""; + //return $@"Backup Database {dbConnection.Database} To disk='{backupFilePath}'"; + } + + public override string GetDropTableSql(DbContext dbContext, string tableName) + { + //var schma = dbContext.Model.FindEntityType(type).GetSchema(); + //TODO: Add schma + return $"DROP TABLE \"{tableName}\"";//CASCADE + } + + } +} diff --git a/src/Basic/Senparc.Ncf.Database.SqlServer/SqlServerDatabaseConfiguration.cs b/src/Basic/Senparc.Ncf.Database.SqlServer/SqlServerDatabaseConfiguration.cs index b8a38a33f..178c1ce97 100644 --- a/src/Basic/Senparc.Ncf.Database.SqlServer/SqlServerDatabaseConfiguration.cs +++ b/src/Basic/Senparc.Ncf.Database.SqlServer/SqlServerDatabaseConfiguration.cs @@ -1,55 +1,55 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; -using Senparc.Ncf.Database; -using Senparc.Ncf.Core.Exceptions; -using System; -using System.Collections.Generic; -using System.Text; -using Senparc.Ncf.Database.MultipleMigrationDbContext; -using Senparc.Ncf.Core.Models; -using System.Data.Common; - -namespace Senparc.Ncf.Database.SqlServer -{ - /// - /// SQL Server 数据库配置 - /// - public class SqlServerDatabaseConfiguration : DatabaseConfigurationBase - { - public SqlServerDatabaseConfiguration() { } - - public override MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.SqlServer; - - public override Action DbContextOptionsActionExtension => (optionsBuilder, xncfDatabaseData) => - { - var typedBuilder = optionsBuilder as SqlServerDbContextOptionsBuilder; - typedBuilder.EnableRetryOnFailure( - maxRetryCount: 5, - maxRetryDelay: TimeSpan.FromSeconds(5), - errorNumbersToAdd: new int[] { 2 }); - }; - - public override Action> SetUseDatabase => - (optionsBuilder, connectionString, xncfDatabaseData, actionBase) => - { - optionsBuilder.UseSqlServer(connectionString, actionBase);//beta6 - }; - - public override string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) - { - return $@"Backup Database {dbConnection.Database} To disk='{backupFilePath}'"; - - //TODO: with DIFFERENTIAL - } - - public override string GetDropTableSql(DbContext dbContext, string tableName) - { - //var schma = dbContext.Model.FindEntityType(type).GetSchema(); - //TODO: 增加 schma - return $"DROP TABLE {tableName}"; - } - - - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; +using Senparc.Ncf.Database; +using Senparc.Ncf.Core.Exceptions; +using System; +using System.Collections.Generic; +using System.Text; +using Senparc.Ncf.Database.MultipleMigrationDbContext; +using Senparc.Ncf.Core.Models; +using System.Data.Common; + +namespace Senparc.Ncf.Database.SqlServer +{ + /// + /// SQL Server database configuration + /// + public class SqlServerDatabaseConfiguration : DatabaseConfigurationBase + { + public SqlServerDatabaseConfiguration() { } + + public override MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.SqlServer; + + public override Action DbContextOptionsActionExtension => (optionsBuilder, xncfDatabaseData) => + { + var typedBuilder = optionsBuilder as SqlServerDbContextOptionsBuilder; + typedBuilder.EnableRetryOnFailure( + maxRetryCount: 5, + maxRetryDelay: TimeSpan.FromSeconds(5), + errorNumbersToAdd: new int[] { 2 }); + }; + + public override Action> SetUseDatabase => + (optionsBuilder, connectionString, xncfDatabaseData, actionBase) => + { + optionsBuilder.UseSqlServer(connectionString, actionBase);//beta6 + }; + + public override string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) + { + return $@"Backup Database {dbConnection.Database} To disk='{backupFilePath}'"; + + //TODO: with DIFFERENTIAL + } + + public override string GetDropTableSql(DbContext dbContext, string tableName) + { + //var schma = dbContext.Model.FindEntityType(type).GetSchema(); + //TODO: Add schma + return $"DROP TABLE {tableName}"; + } + + + } +} diff --git a/src/Basic/Senparc.Ncf.Database.Sqlite/SqliteDatabaseConfiguration.cs b/src/Basic/Senparc.Ncf.Database.Sqlite/SqliteDatabaseConfiguration.cs index e3ce3d287..a9ac68eb5 100644 --- a/src/Basic/Senparc.Ncf.Database.Sqlite/SqliteDatabaseConfiguration.cs +++ b/src/Basic/Senparc.Ncf.Database.Sqlite/SqliteDatabaseConfiguration.cs @@ -1,78 +1,78 @@ -using Microsoft.Data.Sqlite; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Sqlite.Infrastructure.Internal; -using Senparc.CO2NET.Extensions; -using Senparc.Ncf.Core.Models; -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.IO; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Database.Sqlite -{ - /// - /// SQLite 数据库配置 - /// - public class SqliteDatabaseConfiguration : DatabaseConfigurationBase - { - public override MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.Sqlite; - - //private static DbConnection CreateInMemoryDatabase(string connStr) - //{ - // var connection = new SqliteConnection(connStr); - // connection.Open(); - // return connection; - //} - - - internal static string GetLocalConnectionString(string connectionString) - { - // 检查并调整路径 - const string PREFIX = "Filename="; - if (connectionString.StartsWith($"{PREFIX}\\") || connectionString.StartsWith($"{PREFIX}./") || connectionString.StartsWith($"{PREFIX}.\\")) - { - string relativePath = connectionString.Substring(PREFIX.Length); // 去掉 "Filename=" 前缀 - string dbPath = Path.Combine(Senparc.CO2NET.Config.RootDirectoryPath, relativePath.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar)); - connectionString = $"{PREFIX}{dbPath}"; - } - - return connectionString; - } - - public override Action> SetUseDatabase => - (optionsBuilder, connectionString, xncfDatabaseData, actionBase) => - { - //其他更多配置 - - // 检查并调整路径 - connectionString = GetLocalConnectionString(connectionString); - - //执行 UseSqlite(必须) - //optionsBuilder.UseSqlite(CreateInMemoryDatabase(connectionString), actionBase); - - optionsBuilder.UseSqlite(connectionString, actionBase); - }; - - public override Action DbContextOptionsActionExtension => (builder, xncfDatabaseData) => - { - //更多数据库操作独立配置(非必须) - }; - - - public override string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) - { - dbConnection.Open(); - (dbConnection as SqliteConnection).BackupDatabase(new SqliteConnection("data source='" + backupFilePath + "'")); - dbConnection.Close(); - return null; - } - - public override string GetDropTableSql(DbContext dbContext, string tableName) - { - return $"DROP TABLE IF EXISTS {tableName}"; - } - } -} +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Sqlite.Infrastructure.Internal; +using Senparc.CO2NET.Extensions; +using Senparc.Ncf.Core.Models; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Database.Sqlite +{ + /// + ///SQLite database configuration + /// + public class SqliteDatabaseConfiguration : DatabaseConfigurationBase + { + public override MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.Sqlite; + + //private static DbConnection CreateInMemoryDatabase(string connStr) + //{ + // var connection = new SqliteConnection(connStr); + // connection.Open(); + // return connection; + //} + + + internal static string GetLocalConnectionString(string connectionString) + { + // Check and adjust paths + const string PREFIX = "Filename="; + if (connectionString.StartsWith($"{PREFIX}\\") || connectionString.StartsWith($"{PREFIX}./") || connectionString.StartsWith($"{PREFIX}.\\")) + { + string relativePath = connectionString.Substring(PREFIX.Length); // Remove the "Filename=" prefix + string dbPath = Path.Combine(Senparc.CO2NET.Config.RootDirectoryPath, relativePath.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar)); + connectionString = $"{PREFIX}{dbPath}"; + } + + return connectionString; + } + + public override Action> SetUseDatabase => + (optionsBuilder, connectionString, xncfDatabaseData, actionBase) => + { + //Other more configurations + + // Check and adjust paths + connectionString = GetLocalConnectionString(connectionString); + + //Execute UseSqlite (required) + //optionsBuilder.UseSqlite(CreateInMemoryDatabase(connectionString), actionBase); + + optionsBuilder.UseSqlite(connectionString, actionBase); + }; + + public override Action DbContextOptionsActionExtension => (builder, xncfDatabaseData) => + { + //More independent configuration of database operations (not required) + }; + + + public override string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) + { + dbConnection.Open(); + (dbConnection as SqliteConnection).BackupDatabase(new SqliteConnection("data source='" + backupFilePath + "'")); + dbConnection.Close(); + return null; + } + + public override string GetDropTableSql(DbContext dbContext, string tableName) + { + return $"DROP TABLE IF EXISTS {tableName}"; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Database.Sqlite/SqliteMemoryDatabaseConfiguration.cs b/src/Basic/Senparc.Ncf.Database.Sqlite/SqliteMemoryDatabaseConfiguration.cs index 2a90f46f4..c2d8525ef 100644 --- a/src/Basic/Senparc.Ncf.Database.Sqlite/SqliteMemoryDatabaseConfiguration.cs +++ b/src/Basic/Senparc.Ncf.Database.Sqlite/SqliteMemoryDatabaseConfiguration.cs @@ -1,64 +1,64 @@ -using Microsoft.Data.Sqlite; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Sqlite.Infrastructure.Internal; -using Senparc.CO2NET.Extensions; -using Senparc.Ncf.Core.Extensions; -using Senparc.Ncf.Core.Models; -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.IO; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Database.Sqlite -{ - /// - /// SQLite 数据库配置 - /// - public class SqliteMemoryDatabaseConfiguration : DatabaseConfigurationBase - { - public override MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.Sqlite; - - //private static DbConnection CreateInMemoryDatabase(string connStr) - //{ - // var connection = new SqliteConnection(connStr); - // connection.Open(); - // return connection; - //} - - public override Action> SetUseDatabase => - (optionsBuilder, connectionString, xncfDatabaseData, actionBase) => - { - //其他更多配置 - - connectionString = SqliteDatabaseConfiguration.GetLocalConnectionString(connectionString); - - //执行 UseSqlite(必须) - //optionsBuilder.UseSqlite(CreateInMemoryDatabase(connectionString), actionBase); - - optionsBuilder.UseSqlite(connectionString, actionBase); - }; - - - public override Action DbContextOptionsActionExtension => (builder, xncfDatabaseData) => - { - //更多数据库操作独立配置(非必须) - }; - - - public override string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) - { - dbConnection.Open(); - (dbConnection as SqliteConnection).BackupDatabase(new SqliteConnection("data source='" + backupFilePath + "'")); - dbConnection.Close(); - return null; - } - - public override string GetDropTableSql(DbContext dbContext, string tableName) - { - return $"DROP TABLE IF EXISTS {tableName}"; - } - } -} +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Sqlite.Infrastructure.Internal; +using Senparc.CO2NET.Extensions; +using Senparc.Ncf.Core.Extensions; +using Senparc.Ncf.Core.Models; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Database.Sqlite +{ + /// + ///SQLite database configuration + /// + public class SqliteMemoryDatabaseConfiguration : DatabaseConfigurationBase + { + public override MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.Sqlite; + + //private static DbConnection CreateInMemoryDatabase(string connStr) + //{ + // var connection = new SqliteConnection(connStr); + // connection.Open(); + // return connection; + //} + + public override Action> SetUseDatabase => + (optionsBuilder, connectionString, xncfDatabaseData, actionBase) => + { + //Other more configurations + + connectionString = SqliteDatabaseConfiguration.GetLocalConnectionString(connectionString); + + //Execute UseSqlite (required) + //optionsBuilder.UseSqlite(CreateInMemoryDatabase(connectionString), actionBase); + + optionsBuilder.UseSqlite(connectionString, actionBase); + }; + + + public override Action DbContextOptionsActionExtension => (builder, xncfDatabaseData) => + { + //More independent configuration of database operations (not required) + }; + + + public override string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) + { + dbConnection.Open(); + (dbConnection as SqliteConnection).BackupDatabase(new SqliteConnection("data source='" + backupFilePath + "'")); + dbConnection.Close(); + return null; + } + + public override string GetDropTableSql(DbContext dbContext, string tableName) + { + return $"DROP TABLE IF EXISTS {tableName}"; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Database/DatabaseConfiguration/BySettingDatabaseConfiguration.cs b/src/Basic/Senparc.Ncf.Database/DatabaseConfiguration/BySettingDatabaseConfiguration.cs index 180a8dbc4..03f05038c 100644 --- a/src/Basic/Senparc.Ncf.Database/DatabaseConfiguration/BySettingDatabaseConfiguration.cs +++ b/src/Basic/Senparc.Ncf.Database/DatabaseConfiguration/BySettingDatabaseConfiguration.cs @@ -1,39 +1,39 @@ -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.Text; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Senparc.Ncf.Core.Models; - -namespace Senparc.Ncf.Database -{ - /// - /// 使用 appsettigs.json 中的配置进行获取,不作为任何实际使用的数据库配置 - /// - public class BySettingDatabaseConfiguration : IDatabaseConfiguration - { - public MultipleDatabaseType MultipleDatabaseType => throw new NotImplementedException(); - - public Action DbContextOptionsActionBase => throw new NotImplementedException(); - - public Action DbContextOptionsActionExtension => throw new NotImplementedException(); - - public Action> SetUseDatabase => throw new NotImplementedException(); - - public string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) - { - throw new NotImplementedException(); - } - - public string GetDropTableSql(DbContext dbContext, string tableName) - { - throw new NotImplementedException(); - } - - public void UseDatabase(DbContextOptionsBuilder builder, string connectionString, XncfDatabaseData xncfDatabaseData = null, Action dbContextOptionsAction = null) - { - throw new NotImplementedException(); - } - } -} +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Text; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Senparc.Ncf.Core.Models; + +namespace Senparc.Ncf.Database +{ + /// + /// Use the configuration in appsettigs.json to obtain, not as any actual database configuration. + /// + public class BySettingDatabaseConfiguration : IDatabaseConfiguration + { + public MultipleDatabaseType MultipleDatabaseType => throw new NotImplementedException(); + + public Action DbContextOptionsActionBase => throw new NotImplementedException(); + + public Action DbContextOptionsActionExtension => throw new NotImplementedException(); + + public Action> SetUseDatabase => throw new NotImplementedException(); + + public string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) + { + throw new NotImplementedException(); + } + + public string GetDropTableSql(DbContext dbContext, string tableName) + { + throw new NotImplementedException(); + } + + public void UseDatabase(DbContextOptionsBuilder builder, string connectionString, XncfDatabaseData xncfDatabaseData = null, Action dbContextOptionsAction = null) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Database/DatabaseConfiguration/DatabaseConfigurationBase.cs b/src/Basic/Senparc.Ncf.Database/DatabaseConfiguration/DatabaseConfigurationBase.cs index d00da6c83..32cf30f0d 100644 --- a/src/Basic/Senparc.Ncf.Database/DatabaseConfiguration/DatabaseConfigurationBase.cs +++ b/src/Basic/Senparc.Ncf.Database/DatabaseConfiguration/DatabaseConfigurationBase.cs @@ -1,101 +1,101 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Senparc.CO2NET.Extensions; -using Senparc.Ncf.Core.Exceptions; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Database.MultipleMigrationDbContext; -using System; -using System.Data.Common; - -namespace Senparc.Ncf.Database -{ - /// - /// 数据库配置基类 - /// 官方推荐的数据库提供程序:https://docs.microsoft.com/zh-cn/ef/core/providers/?tabs=dotnet-core-cli - /// - public abstract class DatabaseConfigurationBase : IDatabaseConfiguration - where TBuilder : RelationalDbContextOptionsBuilder - where TExtension : RelationalOptionsExtension, new() - { - public abstract MultipleDatabaseType MultipleDatabaseType { get; } - - /// - /// 具有 类型的 DbContextOptionsAction - /// - public virtual Action TypedDbContextOptionsActionBase => DbContextOptionsActionBase; - - public Action DbContextOptionsActionBase => (builder, xncfDatabaseData) => - { - - //获取当前数据库工厂 - var databaseConfigurationFactory = DatabaseConfigurationFactory.Instance; - //获取当前指定的 IXncfDatabase 对应信息(只在针对某个特定的 XNCF 数据库模块进行 add-migration 等情况下有效) - if (xncfDatabaseData != null) - { - var typedBuilder = builder as TBuilder; - - //获取指定 IXncfDatabase 对应于当前数据库类型的 DbContext 的类型 - var dbContextType = MultipleDatabasePool.Instance.GetXncfDbContextType(xncfDatabaseData.XncfDatabaseRegister.GetType()); - - //DbContext的程序集名称(或强制指定生成 add-migration 的程序集名称 - var dbContextAssemblyName = xncfDatabaseData.AssemblyName ?? dbContextType.Assembly.FullName; - //Migration History 的表名 - var databaseMigrationHistoryTableName = NcfDatabaseMigrationHelper.GetDatabaseMigrationHistoryTableName(xncfDatabaseData.XncfDatabaseRegister); - - typedBuilder.MigrationsAssembly(dbContextAssemblyName) - .MigrationsHistoryTable(databaseMigrationHistoryTableName); - } - else - { - // 程序执行时 DatabaseConfigurationFactory.CurrentXncfDatabaseData 允许为 null - } - }; - - public abstract Action DbContextOptionsActionExtension { get; } - - public abstract Action> SetUseDatabase { get; } - - /// - /// 备份数据库方法 - /// 如果返回null,则在方法内部完成备份程序 - /// - /// - /// - /// - public abstract string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath); - /// - /// 删除指定表Sql - /// 如果返回null,则在方法内部完成删除操作 - /// - /// - /// - /// - public abstract string GetDropTableSql(DbContext dbContext, string tableName); - - /// - /// 使用数据库,如: - /// var builder = new DbContextOptionsBuilder<TDbContext>(); builder.UseSqlServer(sqlConnection, DbContextOptionsAction); - /// - /// - /// - /// - /// 额外需要配置的内容 - public void UseDatabase(DbContextOptionsBuilder builder,/*IRelationalDbContextOptionsBuilderInfrastructure optionsBuilder,*/ string connectionString, - XncfDatabaseData xncfDatabaseData = null, - Action dbContextOptionsAction = null) - { - //执行 UseSQLite、UseSQLServer 等操作 - SetUseDatabase(builder, connectionString, xncfDatabaseData, b => - { - //执行基础代码 - DbContextOptionsActionBase(b, xncfDatabaseData); - //执行扩展代码 - DbContextOptionsActionExtension?.Invoke(b, xncfDatabaseData); - //执行外部传入的其他方法 - dbContextOptionsAction?.Invoke(b, xncfDatabaseData); - } - ); - } - - } +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Senparc.CO2NET.Extensions; +using Senparc.Ncf.Core.Exceptions; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Database.MultipleMigrationDbContext; +using System; +using System.Data.Common; + +namespace Senparc.Ncf.Database +{ + /// + /// Database configuration base class + /// Officially recommended database provider: https://docs.microsoft.com/zh-cn/ef/core/providers/?tabs=dotnet-core-cli + /// + public abstract class DatabaseConfigurationBase : IDatabaseConfiguration + where TBuilder : RelationalDbContextOptionsBuilder + where TExtension : RelationalOptionsExtension, new() + { + public abstract MultipleDatabaseType MultipleDatabaseType { get; } + + /// + /// DbContextOptionsAction with type + /// + public virtual Action TypedDbContextOptionsActionBase => DbContextOptionsActionBase; + + public Action DbContextOptionsActionBase => (builder, xncfDatabaseData) => + { + + //Get the current database factory + var databaseConfigurationFactory = DatabaseConfigurationFactory.Instance; + //Get the corresponding information of the currently specified IXncfDatabase (only valid when add-migration is performed for a specific XNCF database module) + if (xncfDatabaseData != null) + { + var typedBuilder = builder as TBuilder; + + //Gets the type of the DbContext corresponding to the current database type for the specified IXncfDatabase + var dbContextType = MultipleDatabasePool.Instance.GetXncfDbContextType(xncfDatabaseData.XncfDatabaseRegister.GetType()); + + //The assembly name of the DbContext (or force specifying the assembly name that generates add-migration + var dbContextAssemblyName = xncfDatabaseData.AssemblyName ?? dbContextType.Assembly.FullName; + //Migration History table name + var databaseMigrationHistoryTableName = NcfDatabaseMigrationHelper.GetDatabaseMigrationHistoryTableName(xncfDatabaseData.XncfDatabaseRegister); + + typedBuilder.MigrationsAssembly(dbContextAssemblyName) + .MigrationsHistoryTable(databaseMigrationHistoryTableName); + } + else + { + // DatabaseConfigurationFactory.CurrentXncfDatabaseData is allowed to be null when the program is executed + } + }; + + public abstract Action DbContextOptionsActionExtension { get; } + + public abstract Action> SetUseDatabase { get; } + + /// + /// Backup database method + /// If null is returned, the backup procedure is completed inside the method + /// + /// + /// + /// + public abstract string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath); + /// + /// Delete the specified table Sql + /// If null is returned, the deletion operation is completed inside the method + /// + /// + /// + /// + public abstract string GetDropTableSql(DbContext dbContext, string tableName); + + /// + /// Use a database, such as: + /// var builder = new DbContextOptionsBuilder<TDbContext>(); builder.UseSqlServer(sqlConnection, DbContextOptionsAction); + /// + /// + /// + /// + /// Additional content that needs to be configured + public void UseDatabase(DbContextOptionsBuilder builder,/*IRelationalDbContextOptionsBuilderInfrastructure optionsBuilder,*/ string connectionString, + XncfDatabaseData xncfDatabaseData = null, + Action dbContextOptionsAction = null) + { + //Perform operations such as UseSQLite, UseSQLServer, etc. + SetUseDatabase(builder, connectionString, xncfDatabaseData, b => + { + //Execute basic code + DbContextOptionsActionBase(b, xncfDatabaseData); + //Execute extension code + DbContextOptionsActionExtension?.Invoke(b, xncfDatabaseData); + //Execute other methods passed in from outside + dbContextOptionsAction?.Invoke(b, xncfDatabaseData); + } + ); + } + + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Database/DatabaseConfiguration/UnitTestDatabaseConfiguration.cs b/src/Basic/Senparc.Ncf.Database/DatabaseConfiguration/UnitTestDatabaseConfiguration.cs index 168719897..6303a7cca 100644 --- a/src/Basic/Senparc.Ncf.Database/DatabaseConfiguration/UnitTestDatabaseConfiguration.cs +++ b/src/Basic/Senparc.Ncf.Database/DatabaseConfiguration/UnitTestDatabaseConfiguration.cs @@ -1,44 +1,44 @@ -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.Text; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Senparc.Ncf.Core.Models; - -namespace Senparc.Ncf.Database -{ - ///// - ///// 使用 appsettigs.json 中的配置进行获取,不作为任何实际使用的数据库配置 - ///// - //public class UnitTestDatabaseConfiguration : IDatabaseConfiguration - //{ - // /// - // /// 单元测试环境下的默认总数据库 - // /// - // public static Type? UnitTestPillarDbContext { get; set; } = null; - - // public MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.UnitTest; - - // public Action DbContextOptionsActionBase => throw new NotImplementedException(); - - // public Action DbContextOptionsActionExtension => throw new NotImplementedException(); - - // public Action> SetUseDatabase => throw new NotImplementedException(); - - // public string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) - // { - // throw new NotImplementedException(); - // } - - // public string GetDropTableSql(DbContext dbContext, string tableName) - // { - // throw new NotImplementedException(); - // } - - // public void UseDatabase(DbContextOptionsBuilder builder, string connectionString, XncfDatabaseData xncfDatabaseData = null, Action dbContextOptionsAction = null) - // { - // throw new NotImplementedException(); - // } - //} -} +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Text; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Senparc.Ncf.Core.Models; + +namespace Senparc.Ncf.Database +{ + ///// + ///// Use the configuration in appsettigs.json to obtain it, not as any actual database configuration. + ///// + //public class UnitTestDatabaseConfiguration : IDatabaseConfiguration + //{ + // /// + // /// The default total database in the unit test environment + // /// + // public static Type? UnitTestPillarDbContext { get; set; } = null; + + // public MultipleDatabaseType MultipleDatabaseType => MultipleDatabaseType.UnitTest; + + // public Action DbContextOptionsActionBase => throw new NotImplementedException(); + + // public Action DbContextOptionsActionExtension => throw new NotImplementedException(); + + // public Action> SetUseDatabase => throw new NotImplementedException(); + + // public string GetBackupDatabaseSql(DbConnection dbConnection, string backupFilePath) + // { + // throw new NotImplementedException(); + // } + + // public string GetDropTableSql(DbContext dbContext, string tableName) + // { + // throw new NotImplementedException(); + // } + + // public void UseDatabase(DbContextOptionsBuilder builder, string connectionString, XncfDatabaseData xncfDatabaseData = null, Action dbContextOptionsAction = null) + // { + // throw new NotImplementedException(); + // } + //} +} diff --git a/src/Basic/Senparc.Ncf.Database/DbContextPools/MultipleDatabasePool.cs b/src/Basic/Senparc.Ncf.Database/DbContextPools/MultipleDatabasePool.cs index d8ca1867e..73b9a9529 100644 --- a/src/Basic/Senparc.Ncf.Database/DbContextPools/MultipleDatabasePool.cs +++ b/src/Basic/Senparc.Ncf.Database/DbContextPools/MultipleDatabasePool.cs @@ -1,236 +1,236 @@ -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; -using Senparc.Ncf.Core.Config; -using Senparc.Ncf.Core.Exceptions; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Database; -using Senparc.Ncf.Database.MultipleMigrationDbContext; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Senparc.Ncf.Database -{ - public class XncfDatabaseDbContextWapper - { - /// - /// - /// - /// 设计时(或运行时进行 Database.Migrate() 操作的)所使用的 XncfDatabaseDbContext 类型 - /// 运行时所使用的 XncfDatabaseDbContext 类型(通常为进行查询时) - public XncfDatabaseDbContextWapper(Type migrationDbContextType, Type runtimeDbContextType) - { - MigrationDbContextType = migrationDbContextType; - RuntimeDbContextType = runtimeDbContextType; - } - - public Type MigrationDbContextType { get; set; } - public Type RuntimeDbContextType { get; set; } - } - - /// - /// 多数据库配置池 - /// Value 为 Dictionary - /// - public class MultipleDatabasePool - : Dictionary> - { - #region 单例 - - MultipleDatabasePool() { } - - /// - /// DatabaseConfigurationFactory 的全局单例 - /// - public static MultipleDatabasePool Instance - { - get - { - return Nested.instance; - } - } - - class Nested - { - static Nested() { } - - internal static readonly MultipleDatabasePool instance = new MultipleDatabasePool(); - } - - #endregion - - /// - /// 单元测试用的 DbContext - /// - public static Type UnitTestPillarDbContext { get; set; } = null; - - /// - /// 添加配置 - /// - /// - /// 实现了 IXncfDatabase 接口的类型 - /// 输出日志表格的宽度 - /// - public string TryAdd(MultipleMigrationDbContextAttribute multiDbContextAttr, Type xncfDatabaseDbContextType, int[] logColumnWidth) - { - var msg = $"| {multiDbContextAttr.XncfDatabaseRegisterType.FullName.PadRight(logColumnWidth[0])}| {xncfDatabaseDbContextType.Name.PadRight(logColumnWidth[1])}| {multiDbContextAttr.MultipleDatabaseType.ToString().PadRight(logColumnWidth[2])}"; - - //查看是否已经包含 MultipleDatabaseType - if (!this.ContainsKey(multiDbContextAttr.MultipleDatabaseType)) - { - //添加 MultipleDatabaseType 对应集合 - this[multiDbContextAttr.MultipleDatabaseType] = new Dictionary(); - } - - //加入配置 - this[multiDbContextAttr.MultipleDatabaseType][multiDbContextAttr.XncfDatabaseRegisterType] = xncfDatabaseDbContextType; - - //同步添加到 XncfDatabaseDbContextPool - XncfDatabaseDbContextPool.Instance.TryAdd(multiDbContextAttr, xncfDatabaseDbContextType); - - return msg; - } - - /// - /// 获取指定 IXncfDatabase 关联的当前数据库上下文(DbContext) - /// - /// 实现了 IXncfDatabase 的实体 - /// - public Type GetXncfDbContextType(IXncfDatabase xncfDatabaseRegister) - { - return GetXncfDbContextType(xncfDatabaseRegister.GetType()); - } - - /// - /// 获取指定 IXncfDatabase 关联的当前数据库上下文(DbContext) - /// - /// 实现了 IXncfDatabase 的具体类型 - /// - public Type GetXncfDbContextType(Type xncfDatabaseRegisterType) - { - //数据库配置工厂 - var databaseConfigurationFactory = DatabaseConfigurationFactory.Instance; - //当前数据库配置 - var currentDatabaseConfiguration = databaseConfigurationFactory.Current; - //当前数据库类型 - MultipleDatabaseType multipleDatabaseType = currentDatabaseConfiguration.MultipleDatabaseType; - - if (multipleDatabaseType == MultipleDatabaseType.InMemory) - { - //单元测试 - return UnitTestPillarDbContext ?? throw new NcfExceptionBase($"当前数据库类型为 {multipleDatabaseType},需要指定 {nameof(UnitTestPillarDbContext)}!"); - } - else if (!this.ContainsKey(multipleDatabaseType)) - { - throw new NcfDatabaseException($"未发现任何支持此数据库类型的 XNCF 模块:{multipleDatabaseType}", currentDatabaseConfiguration.GetType()); - } - - var xncdDatabaseRegisterCollection = this[multipleDatabaseType]; - if (!xncdDatabaseRegisterCollection.ContainsKey(xncfDatabaseRegisterType)) - { - throw new NcfDatabaseException($"{xncfDatabaseRegisterType.FullName} 模块未支持数据库:{multipleDatabaseType}", currentDatabaseConfiguration.GetType()); - } - - return xncdDatabaseRegisterCollection[xncfDatabaseRegisterType]; - } - - - /// - /// 获取指定 DbContext 的数据库实例 - /// - /// 连接字符串,如果为 null,则默认使用 SenparcDatabaseConfigs.ClientConnectionString - /// 额外配置操作 - /// IXncfDatabase 信息(仅在针对 XNCF 进行数据库迁移时有效) - /// ServiceProvider - /// - public T GetDbContext(string connectionString = null, XncfDatabaseData xncfDatabaseData = null, - Action dbContextOptionsAction = null, IServiceProvider serviceProvider = null) where T : DbContext - { - var dbContextType = typeof(T); - DbContextOptionsBuilder dbOptionBuilder; - - var dbOptionBuilderType = dbContextType.GetConstructors().First() - .GetParameters().First().ParameterType; - - if (dbOptionBuilderType.GenericTypeArguments.Length > 0) - { - //带泛型 - //准备创建 DbContextOptionsBuilder 实例,定义类型 - dbOptionBuilderType = typeof(DbContextOptionsBuilder<>); - //dbOptionBuilderType = typeof(RelationalDbContextOptionsBuilder<,>); - //获取泛型对象类型,如:DbContextOptionsBuilder - dbOptionBuilderType = dbOptionBuilderType.MakeGenericType(dbContextType); - - //创建 DbContextOptionsBuilder 实例 - dbOptionBuilder = Activator.CreateInstance(dbOptionBuilderType) as DbContextOptionsBuilder; - } - else - { - //不带泛型 - dbOptionBuilder = new DbContextOptionsBuilder(); - } - - //if (UnitTestDatabaseConfiguration.UnitTestPillarDbContext == null) - { - //不是单元测试,需要读取数据库 - - //获取当前数据库配置 - var currentDatabasConfiguration = DatabaseConfigurationFactory.Instance.Current; - //指定使用当前数据库 - currentDatabasConfiguration.UseDatabase( - dbOptionBuilder, - connectionString ?? (SenparcDatabaseConnectionConfigs.GetClientConnectionString()), - xncfDatabaseData, - dbContextOptionsAction - ); - } - - //实例化 DbContext - T dbContext; - if (serviceProvider == null) - { - dbContext = Activator.CreateInstance(dbContextType, new object[] { dbOptionBuilder.Options }) as T; - } - else - { - dbContext = Activator.CreateInstance(dbContextType, new object[] { dbOptionBuilder.Options, serviceProvider }) as T; - } - - if (dbContext == null) - { - throw new NcfDatabaseException($"未能创建 {dbContextType.FullName} 的实例", DatabaseConfigurationFactory.Instance.Current.GetType(), null); - } - return dbContext; - } - - /// - /// 获取指定 xncfDatabaseRegister 关联的当前数据库实例 - /// - /// 实现了 IXncfDatabase 的具体类型 - /// 连接字符串,如果为 null,则默认使用 SenparcDatabaseConfigs.ClientConnectionString - /// 额外配置操作 - /// IXncfDatabase 信息(仅在针对 XNCF 进行数据库迁移时有效) - /// ServiceProvider - /// - public DbContext GetXncfDbContext(Type xncfDatabaseRegisterType, string connectionString = null, XncfDatabaseData xncfDatabaseData = null, - Action dbContextOptionsAction = null, IServiceProvider serviceProvider = null) - { - if (!typeof(IXncfDatabase).IsAssignableFrom(xncfDatabaseRegisterType)) - { - throw new NcfDatabaseException($"{nameof(xncfDatabaseRegisterType)} 参数:{xncfDatabaseRegisterType.Name} 必须实现 IXncfDatabase 接口", DatabaseConfigurationFactory.Instance.Current.GetType()); - } - - //获取 DbContext 上下文类型 - var dbContextType = GetXncfDbContextType(xncfDatabaseRegisterType); - - return this.GetType().GetMethod(nameof(GetDbContext)) - .MakeGenericMethod(new Type[] { dbContextType }) - .Invoke(this, new object[] { connectionString, xncfDatabaseData, dbContextOptionsAction, serviceProvider }) as DbContext; - - //return GetDbContext(dbContextType, connectionString, xncfDatabaseData, dbContextOptionsAction, serviceProvider); - } - } -} +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; +using Senparc.Ncf.Core.Config; +using Senparc.Ncf.Core.Exceptions; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Database; +using Senparc.Ncf.Database.MultipleMigrationDbContext; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Senparc.Ncf.Database +{ + public class XncfDatabaseDbContextWapper + { + /// + /// + /// + /// The XncfDatabaseDbContext type used at design time (or runtime for Database.Migrate() operations) + /// The XncfDatabaseDbContext type used at runtime (usually when querying) + public XncfDatabaseDbContextWapper(Type migrationDbContextType, Type runtimeDbContextType) + { + MigrationDbContextType = migrationDbContextType; + RuntimeDbContextType = runtimeDbContextType; + } + + public Type MigrationDbContextType { get; set; } + public Type RuntimeDbContextType { get; set; } + } + + /// + ///Multiple database configuration pool + /// Value is Dictionary + /// + public class MultipleDatabasePool + : Dictionary> + { + #region 单例 + + MultipleDatabasePool() { } + + /// + ///Global singleton of DatabaseConfigurationFactory + /// + public static MultipleDatabasePool Instance + { + get + { + return Nested.instance; + } + } + + class Nested + { + static Nested() { } + + internal static readonly MultipleDatabasePool instance = new MultipleDatabasePool(); + } + + #endregion + + /// + /// DbContext for unit testing + /// + public static Type UnitTestPillarDbContext { get; set; } = null; + + /// + ///Add configuration + /// + /// + /// Type that implements the IXncfDatabase interface + /// Width of the output log table + /// + public string TryAdd(MultipleMigrationDbContextAttribute multiDbContextAttr, Type xncfDatabaseDbContextType, int[] logColumnWidth) + { + var msg = $"| {multiDbContextAttr.XncfDatabaseRegisterType.FullName.PadRight(logColumnWidth[0])}| {xncfDatabaseDbContextType.Name.PadRight(logColumnWidth[1])}| {multiDbContextAttr.MultipleDatabaseType.ToString().PadRight(logColumnWidth[2])}"; + + //Check if MultipleDatabaseType is already included + if (!this.ContainsKey(multiDbContextAttr.MultipleDatabaseType)) + { + //Add MultipleDatabaseType corresponding collection + this[multiDbContextAttr.MultipleDatabaseType] = new Dictionary(); + } + + //Add configuration + this[multiDbContextAttr.MultipleDatabaseType][multiDbContextAttr.XncfDatabaseRegisterType] = xncfDatabaseDbContextType; + + //Synchronously added to XncfDatabaseDbContextPool + XncfDatabaseDbContextPool.Instance.TryAdd(multiDbContextAttr, xncfDatabaseDbContextType); + + return msg; + } + + /// + /// Get the current database context (DbContext) associated with the specified IXncfDatabase + /// + /// Entity that implements IXncfDatabase + /// + public Type GetXncfDbContextType(IXncfDatabase xncfDatabaseRegister) + { + return GetXncfDbContextType(xncfDatabaseRegister.GetType()); + } + + /// + /// Get the current database context (DbContext) associated with the specified IXncfDatabase + /// + /// Implements the specific type of IXncfDatabase + /// + public Type GetXncfDbContextType(Type xncfDatabaseRegisterType) + { + //Database configuration factory + var databaseConfigurationFactory = DatabaseConfigurationFactory.Instance; + //Current database configuration + var currentDatabaseConfiguration = databaseConfigurationFactory.Current; + //Current database type + MultipleDatabaseType multipleDatabaseType = currentDatabaseConfiguration.MultipleDatabaseType; + + if (multipleDatabaseType == MultipleDatabaseType.InMemory) + { + //Unit testing + return UnitTestPillarDbContext ?? throw new NcfExceptionBase($"当前数据库类型为 {multipleDatabaseType},需要指定 {nameof(UnitTestPillarDbContext)}!"); + } + else if (!this.ContainsKey(multipleDatabaseType)) + { + throw new NcfDatabaseException($"未发现任何支持此数据库类型的 XNCF 模块:{multipleDatabaseType}", currentDatabaseConfiguration.GetType()); + } + + var xncdDatabaseRegisterCollection = this[multipleDatabaseType]; + if (!xncdDatabaseRegisterCollection.ContainsKey(xncfDatabaseRegisterType)) + { + throw new NcfDatabaseException($"{xncfDatabaseRegisterType.FullName} 模块未支持数据库:{multipleDatabaseType}", currentDatabaseConfiguration.GetType()); + } + + return xncdDatabaseRegisterCollection[xncfDatabaseRegisterType]; + } + + + /// + /// Get the database instance of the specified DbContext + /// + /// Connection string, if null, defaults to SenparcDatabaseConfigs.ClientConnectionString + /// Additional configuration operations + /// IXncfDatabase information (valid only when doing database migration for XNCF) + /// ServiceProvider + /// + public T GetDbContext(string connectionString = null, XncfDatabaseData xncfDatabaseData = null, + Action dbContextOptionsAction = null, IServiceProvider serviceProvider = null) where T : DbContext + { + var dbContextType = typeof(T); + DbContextOptionsBuilder dbOptionBuilder; + + var dbOptionBuilderType = dbContextType.GetConstructors().First() + .GetParameters().First().ParameterType; + + if (dbOptionBuilderType.GenericTypeArguments.Length > 0) + { + //With generics + //Prepare to create a DbContextOptionsBuilder instance and define the type + dbOptionBuilderType = typeof(DbContextOptionsBuilder<>); + //dbOptionBuilderType = typeof(RelationalDbContextOptionsBuilder<,>); + //Get the generic object type, such as: DbContextOptionsBuilder + dbOptionBuilderType = dbOptionBuilderType.MakeGenericType(dbContextType); + + //Create a DbContextOptionsBuilder instance + dbOptionBuilder = Activator.CreateInstance(dbOptionBuilderType) as DbContextOptionsBuilder; + } + else + { + //Without generics + dbOptionBuilder = new DbContextOptionsBuilder(); + } + + //if (UnitTestDatabaseConfiguration.UnitTestPillarDbContext == null) + { + //Not a unit test, needs to read the database + + //Get the current database configuration + var currentDatabasConfiguration = DatabaseConfigurationFactory.Instance.Current; + //Specify to use the current database + currentDatabasConfiguration.UseDatabase( + dbOptionBuilder, + connectionString ?? (SenparcDatabaseConnectionConfigs.GetClientConnectionString()), + xncfDatabaseData, + dbContextOptionsAction + ); + } + + //Instantiate DbContext + T dbContext; + if (serviceProvider == null) + { + dbContext = Activator.CreateInstance(dbContextType, new object[] { dbOptionBuilder.Options }) as T; + } + else + { + dbContext = Activator.CreateInstance(dbContextType, new object[] { dbOptionBuilder.Options, serviceProvider }) as T; + } + + if (dbContext == null) + { + throw new NcfDatabaseException($"未能创建 {dbContextType.FullName} 的实例", DatabaseConfigurationFactory.Instance.Current.GetType(), null); + } + return dbContext; + } + + /// + /// Get the current database instance associated with the specified xncfDatabaseRegister + /// + /// Implements the specific type of IXncfDatabase + /// Connection string, if null, defaults to SenparcDatabaseConfigs.ClientConnectionString + /// Additional configuration operations + /// IXncfDatabase information (valid only when doing database migration for XNCF) + /// ServiceProvider + /// + public DbContext GetXncfDbContext(Type xncfDatabaseRegisterType, string connectionString = null, XncfDatabaseData xncfDatabaseData = null, + Action dbContextOptionsAction = null, IServiceProvider serviceProvider = null) + { + if (!typeof(IXncfDatabase).IsAssignableFrom(xncfDatabaseRegisterType)) + { + throw new NcfDatabaseException($"{nameof(xncfDatabaseRegisterType)} 参数:{xncfDatabaseRegisterType.Name} 必须实现 IXncfDatabase 接口", DatabaseConfigurationFactory.Instance.Current.GetType()); + } + + //Get the DbContext context type + var dbContextType = GetXncfDbContextType(xncfDatabaseRegisterType); + + return this.GetType().GetMethod(nameof(GetDbContext)) + .MakeGenericMethod(new Type[] { dbContextType }) + .Invoke(this, new object[] { connectionString, xncfDatabaseData, dbContextOptionsAction, serviceProvider }) as DbContext; + + //return GetDbContext(dbContextType, connectionString, xncfDatabaseData, dbContextOptionsAction, serviceProvider); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Database/DbContextPools/XncfDatabaseDbContextPool.cs b/src/Basic/Senparc.Ncf.Database/DbContextPools/XncfDatabaseDbContextPool.cs index a3e2be8dc..059bf852c 100644 --- a/src/Basic/Senparc.Ncf.Database/DbContextPools/XncfDatabaseDbContextPool.cs +++ b/src/Basic/Senparc.Ncf.Database/DbContextPools/XncfDatabaseDbContextPool.cs @@ -1,59 +1,59 @@ -using Senparc.Ncf.Core.Exceptions; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Database; -using Senparc.Ncf.Database.MultipleMigrationDbContext; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Database -{ - /// - /// 以 IXncfDatabase Register 类型为 Key 的多数据库上下文(DbContext)配置池 - /// - public class XncfDatabaseDbContextPool : - /*Concurrent*/Dictionary> - { - #region 单例 - - XncfDatabaseDbContextPool() { } - - /// - /// DatabaseConfigurationFactory 的全局单例 - /// - public static XncfDatabaseDbContextPool Instance - { - get - { - return Nested.instance; - } - } - - class Nested - { - static Nested() { } - - internal static readonly XncfDatabaseDbContextPool instance = new XncfDatabaseDbContextPool(); - } - - #endregion - - /// - /// 添加配置 - /// - /// - /// - public void TryAdd(MultipleMigrationDbContextAttribute multiDbContextAttr, Type xncfDatabaseDbContextType) - { - //查看是否已经包含 IDatabaseRegister - if (!this.ContainsKey(multiDbContextAttr.XncfDatabaseRegisterType)) - { - //添加 MultipleDatabaseType 对应集合 - this[multiDbContextAttr.XncfDatabaseRegisterType] = new Dictionary(); - } - //加入配置 - this[multiDbContextAttr.XncfDatabaseRegisterType][multiDbContextAttr.MultipleDatabaseType] = xncfDatabaseDbContextType; - } - } -} +using Senparc.Ncf.Core.Exceptions; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Database; +using Senparc.Ncf.Database.MultipleMigrationDbContext; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Database +{ + /// + /// Multiple database context (DbContext) configuration pool with IXncfDatabase Register type as Key + /// + public class XncfDatabaseDbContextPool : + /*Concurrent*/Dictionary> + { + #region 单例 + + XncfDatabaseDbContextPool() { } + + /// + ///Global singleton of DatabaseConfigurationFactory + /// + public static XncfDatabaseDbContextPool Instance + { + get + { + return Nested.instance; + } + } + + class Nested + { + static Nested() { } + + internal static readonly XncfDatabaseDbContextPool instance = new XncfDatabaseDbContextPool(); + } + + #endregion + + /// + ///Add configuration + /// + /// + /// + public void TryAdd(MultipleMigrationDbContextAttribute multiDbContextAttr, Type xncfDatabaseDbContextType) + { + //Check if IDatabaseRegister is already included + if (!this.ContainsKey(multiDbContextAttr.XncfDatabaseRegisterType)) + { + //Add MultipleDatabaseType corresponding collection + this[multiDbContextAttr.XncfDatabaseRegisterType] = new Dictionary(); + } + //Add configuration + this[multiDbContextAttr.XncfDatabaseRegisterType][multiDbContextAttr.MultipleDatabaseType] = xncfDatabaseDbContextType; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Database/Helpers/NcfDatabaseHelper.cs b/src/Basic/Senparc.Ncf.Database/Helpers/NcfDatabaseHelper.cs index 3ad2df340..cfcf75299 100644 --- a/src/Basic/Senparc.Ncf.Database/Helpers/NcfDatabaseHelper.cs +++ b/src/Basic/Senparc.Ncf.Database/Helpers/NcfDatabaseHelper.cs @@ -1,63 +1,63 @@ -using Microsoft.EntityFrameworkCore; -using Senparc.CO2NET.Trace; -using Senparc.Ncf.Core.Config; -using Senparc.Ncf.Core.Exceptions; -using Senparc.Ncf.Core.Models; -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Database.Helpers -{ - public static class NcfDatabaseHelper - { - /// - /// 获取当前数据库连接信息 - /// - /// - public static Dictionary GetCurrentConnectionInfo() - { - var connectionStr = SenparcDatabaseConnectionConfigs.GetClientConnectionString(); - var list = connectionStr.Split(';', StringSplitOptions.RemoveEmptyEntries).Select(z => - { - var item = z.Split('='); - return new KeyValuePair(item[0], item[1]); - }); - return new Dictionary(list, StringComparer.OrdinalIgnoreCase); - } - - /// - /// 尝试获取数据库连接信息的值,如果获取不到,则返回 null - /// - /// 数据库信息 - /// 名称,如 Database、UserName,不区分大小写 - /// - public static string TryGetConnectionValue(Dictionary dic, string name) - { - if (dic.TryGetValue(name, out var value)) - { - return value; - } - return null; - } - - /// - /// 尝试获取数据库连接信息的值,如果获取不到,则返回 null - /// - /// 名称,如 Database、UserName,不区分大小写 - /// - public static string TryGetConnectionValue(string name) - { - var dic = GetCurrentConnectionInfo(); - if (dic.TryGetValue(name, out var value)) - { - return value; - } - return null; - } - - } -} +using Microsoft.EntityFrameworkCore; +using Senparc.CO2NET.Trace; +using Senparc.Ncf.Core.Config; +using Senparc.Ncf.Core.Exceptions; +using Senparc.Ncf.Core.Models; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Database.Helpers +{ + public static class NcfDatabaseHelper + { + /// + /// Get current database connection information + /// + /// + public static Dictionary GetCurrentConnectionInfo() + { + var connectionStr = SenparcDatabaseConnectionConfigs.GetClientConnectionString(); + var list = connectionStr.Split(';', StringSplitOptions.RemoveEmptyEntries).Select(z => + { + var item = z.Split('='); + return new KeyValuePair(item[0], item[1]); + }); + return new Dictionary(list, StringComparer.OrdinalIgnoreCase); + } + + /// + /// Try to get the value of the database connection information, if not, return null + /// + /// Database information + /// Names, such as Database, UserName, are not case sensitive + /// + public static string TryGetConnectionValue(Dictionary dic, string name) + { + if (dic.TryGetValue(name, out var value)) + { + return value; + } + return null; + } + + /// + /// Try to get the value of the database connection information, if not, return null + /// + /// Names, such as Database, UserName, are not case sensitive + /// + public static string TryGetConnectionValue(string name) + { + var dic = GetCurrentConnectionInfo(); + if (dic.TryGetValue(name, out var value)) + { + return value; + } + return null; + } + + } +} diff --git a/src/Basic/Senparc.Ncf.Database/MultipleMigrationDbContext/IMultipleMigrationDbContext.cs b/src/Basic/Senparc.Ncf.Database/MultipleMigrationDbContext/IMultipleMigrationDbContext.cs index d2800da74..aa88c512a 100644 --- a/src/Basic/Senparc.Ncf.Database/MultipleMigrationDbContext/IMultipleMigrationDbContext.cs +++ b/src/Basic/Senparc.Ncf.Database/MultipleMigrationDbContext/IMultipleMigrationDbContext.cs @@ -1,17 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.Database.MultipleMigrationDbContext -{ - /// - /// 多数据库生成 Migration 的实体类接口 - /// - public interface IMultipleMigrationDbContext - { - /////// - /////// 数据库类型 - /////// - //MultipleDatabaseType MultipleDatabaseType { get; } - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.Database.MultipleMigrationDbContext +{ + /// + /// Entity class interface for multi-database generation Migration + /// + public interface IMultipleMigrationDbContext + { + /////// + /////// Database type + /////// + //MultipleDatabaseType MultipleDatabaseType { get; } + } +} diff --git a/src/Basic/Senparc.Ncf.Database/MultipleMigrationDbContext/MultipleMigrationDbContextAttribute.cs b/src/Basic/Senparc.Ncf.Database/MultipleMigrationDbContext/MultipleMigrationDbContextAttribute.cs index 75ffc8bf2..cfb7c3bf2 100644 --- a/src/Basic/Senparc.Ncf.Database/MultipleMigrationDbContext/MultipleMigrationDbContextAttribute.cs +++ b/src/Basic/Senparc.Ncf.Database/MultipleMigrationDbContext/MultipleMigrationDbContextAttribute.cs @@ -1,53 +1,53 @@ -using Senparc.Ncf.Core.Exceptions; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Database.MultipleMigrationDbContext; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Senparc.Ncf.Database -{ - /// - /// 多数据库生成 Migration 的实体类,无需考虑 - /// - [System.AttributeUsage(AttributeTargets.Class, AllowMultiple = false/*暂时不支持*/)] - public class MultipleMigrationDbContextAttribute : Attribute - { - /// - /// MultipleMigrationDbContext 构造函数。指定多数据库配置。 - /// - /// MultipleDatabaseType 数据库类型 - /// XncfDatabase 注册类类型 - // /// 当运行时使用的统一数据库上下文类型 - public MultipleMigrationDbContextAttribute(MultipleDatabaseType multipleDatabaseType, - Type xncfDatabaseRegisterType/*, Type runtimeDbContextType*/) - { - if (xncfDatabaseRegisterType == null || !xncfDatabaseRegisterType.GetInterfaces().Contains(typeof(IXncfDatabase))) - { - throw new NcfDatabaseException($"xncfDatabaseRegisterType 不能为空,且对应类型必须实现 IXncfDatabase 接口!", null); - } - - MultipleDatabaseType = multipleDatabaseType; - XncfDatabaseRegisterType = xncfDatabaseRegisterType; - //RuntimeDbContextType = runtimeDbContextType; - } - - public MultipleDatabaseType MultipleDatabaseType { get; set; } - public Type XncfDatabaseRegisterType { get; set; } - //public Type RuntimeDbContextType { get; set; } - - IXncfDatabase _xncfDatabaseRegister; - public IXncfDatabase XncfDatabaseRegister - { - get - { - if (_xncfDatabaseRegister == null) - { - _xncfDatabaseRegister = Activator.CreateInstance(XncfDatabaseRegisterType) as IXncfDatabase; - } - return _xncfDatabaseRegister; - } - } - } -} +using Senparc.Ncf.Core.Exceptions; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Database.MultipleMigrationDbContext; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Senparc.Ncf.Database +{ + /// + /// Multiple databases generate Migration entity classes, no need to consider + /// + [System.AttributeUsage(AttributeTargets.Class, AllowMultiple = false/*Not supported at the moment*/)] + public class MultipleMigrationDbContextAttribute : Attribute + { + /// + /// MultipleMigrationDbContext constructor. Specify a multi-database configuration. + /// + /// MultipleDatabaseType database type + /// XncfDatabase registration class type + // /// The unified database context type used when running + public MultipleMigrationDbContextAttribute(MultipleDatabaseType multipleDatabaseType, + Type xncfDatabaseRegisterType/*, Type runtimeDbContextType*/) + { + if (xncfDatabaseRegisterType == null || !xncfDatabaseRegisterType.GetInterfaces().Contains(typeof(IXncfDatabase))) + { + throw new NcfDatabaseException($"xncfDatabaseRegisterType 不能为空,且对应类型必须实现 IXncfDatabase 接口!", null); + } + + MultipleDatabaseType = multipleDatabaseType; + XncfDatabaseRegisterType = xncfDatabaseRegisterType; + //RuntimeDbContextType = runtimeDbContextType; + } + + public MultipleDatabaseType MultipleDatabaseType { get; set; } + public Type XncfDatabaseRegisterType { get; set; } + //public Type RuntimeDbContextType { get; set; } + + IXncfDatabase _xncfDatabaseRegister; + public IXncfDatabase XncfDatabaseRegister + { + get + { + if (_xncfDatabaseRegister == null) + { + _xncfDatabaseRegister = Activator.CreateInstance(XncfDatabaseRegisterType) as IXncfDatabase; + } + return _xncfDatabaseRegister; + } + } + } +} diff --git a/src/Basic/Senparc.Ncf.Database/MySqlIdentityColumWorkaround/Workaround.cs b/src/Basic/Senparc.Ncf.Database/MySqlIdentityColumWorkaround/Workaround.cs index c9a75e4cd..f1a479555 100644 --- a/src/Basic/Senparc.Ncf.Database/MySqlIdentityColumWorkaround/Workaround.cs +++ b/src/Basic/Senparc.Ncf.Database/MySqlIdentityColumWorkaround/Workaround.cs @@ -1,35 +1,35 @@ -using Senparc.Ncf.Core.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Microsoft.EntityFrameworkCore.Metadata -{ - /// - /// 弥补 MySQL 库暂时的缺陷 - /// - public class MySqlHelper - { - /// - /// 获取 MySqlValueGenerationStrateg.IdentityColumn 等枚举 - /// - /// - /// - public static object GetMySqlValueGenerationStrategy(string enumName = "IdentityColumn") - { - var field = Type.GetType("Microsoft.EntityFrameworkCore.Metadata.MySqlValueGenerationStrategy,Pomelo.EntityFrameworkCore.MySql").GetFields().FirstOrDefault(z => z.Name == enumName); - if (field == null) - { - //数据库配置工厂 - var databaseConfigurationFactory = DatabaseConfigurationFactory.Instance; - //当前数据库配置 - var currentDatabaseConfiguration = databaseConfigurationFactory.Current; - //抛出异常 - throw new Senparc.Ncf.Core.Exceptions.NcfDatabaseException("程序集中未找到 MySqlValueGenerationStrategy.IdentityColumn 枚举", currentDatabaseConfiguration.GetType()); - } - var value = field.GetValue(null); - return value; - } - } +using Senparc.Ncf.Core.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Make up for the temporary shortcomings of the MySQL library + /// + public class MySqlHelper + { + /// + /// Get enumerations such as MySqlValueGenerationStrateg.IdentityColumn + /// + /// + /// + public static object GetMySqlValueGenerationStrategy(string enumName = "IdentityColumn") + { + var field = Type.GetType("Microsoft.EntityFrameworkCore.Metadata.MySqlValueGenerationStrategy,Pomelo.EntityFrameworkCore.MySql").GetFields().FirstOrDefault(z => z.Name == enumName); + if (field == null) + { + //Database configuration factory + var databaseConfigurationFactory = DatabaseConfigurationFactory.Instance; + //Current database configuration + var currentDatabaseConfiguration = databaseConfigurationFactory.Current; + //throw an exception + throw new Senparc.Ncf.Core.Exceptions.NcfDatabaseException("程序集中未找到 MySqlValueGenerationStrategy.IdentityColumn 枚举", currentDatabaseConfiguration.GetType()); + } + var value = field.GetValue(null); + return value; + } + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Database/Register.cs b/src/Basic/Senparc.Ncf.Database/Register.cs index 2aa14c23a..849a2c29c 100644 --- a/src/Basic/Senparc.Ncf.Database/Register.cs +++ b/src/Basic/Senparc.Ncf.Database/Register.cs @@ -1,200 +1,200 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Senparc.CO2NET.Extensions; -using Senparc.CO2NET.Helpers; -using Senparc.Ncf.Core.Config; -using Senparc.Ncf.Core.Exceptions; -using Senparc.Ncf.Core.Models; -using System; -using System.Reflection; - -namespace Senparc.Ncf.Database -{ - /// - /// 数据库注册类 - /// - public static class Register - { - ///// - ///// 使用指定数据库 - ///// - ///// - ///// - ///// - //public static IServiceCollection AddDatabase(this IServiceCollection services) - // where TDatabaseConfiguration : IDatabaseConfiguration, new() - //{ - // DatabaseConfigurationFactory.Instance.Current = new TDatabaseConfiguration(); - // return services; - //} - - ///// - ///// 使用指定数据库 - ///// - ///// - ///// - ///// - //public static IServiceCollection AddDatabase(this IServiceCollection services, Type databaseConfigurationType) - //{ - // //必须实现 IDatabaseConfiguration 接口的类型才能进行下一步配置 - // if (typeof(IDatabaseConfiguration).IsAssignableFrom(databaseConfigurationType)) - // { - // throw new NcfDatabaseException($"类型{databaseConfigurationType.Name} 必须实现接口:IDatabaseConfiguration", databaseConfigurationType); - // } - - // var databaseConfiguration = Activator.CreateInstance(databaseConfigurationType, true) as IDatabaseConfiguration; - - // DatabaseConfigurationFactory.Instance.Current = databaseConfiguration; - // return services; - //} - - ///// - ///// 使用指定数据库 - ///// - ///// - ///// - ///// - //public static IServiceCollection AddDatabase(this IServiceCollection services, IDatabaseConfiguration databaseConfiguration) - //{ - // //必须实现 IDatabaseConfiguration 接口的类型才能进行下一步配置 - // if (databaseConfiguration == null) - // { - // throw new NcfDatabaseException($"{nameof(databaseConfiguration)} 参数不能为 null", null); - // } - - // DatabaseConfigurationFactory.Instance.Current = databaseConfiguration; - // return services; - //} - - ///// - ///// 使用指定数据库 - ///// - ///// - ///// DatabaseConfiguration 程序集名称 - ///// DatabaseConfiguration 命名空间 - ///// DatabaseConfiguration 类名 - ///// - //public static IServiceCollection AddDatabase(this IServiceCollection services, string assemblyName, string nameSpace, string className) - //{ - // //TODO:集成到 CO2NET - // string fullName = nameSpace + "." + className;//命名空间.类型名 - // var databaseConfiguration = Assembly.Load(assemblyName).CreateInstance(fullName) as IDatabaseConfiguration;//加载程序集,创建程序集里面的 命名空间.类型名 实例 - // return services.AddDatabase(databaseConfiguration); - //} - - - #region UseNcfDatabase - - /// - /// UseNcfDatabase() 方法是否已经执行完毕 - /// - public static bool UseNcfDatabaseSetted = false; - - /// - /// 使用指定数据库 - /// - /// - /// 可用的 DatabaseConfiguration,非 - /// - public static IApplicationBuilder UseNcfDatabase(this IApplicationBuilder app, IDatabaseConfiguration databaseConfiguration) - { - //必须实现 IDatabaseConfiguration 接口的类型才能进行下一步配置 - if (databaseConfiguration == null) - { - throw new NcfDatabaseException($"{nameof(databaseConfiguration)} 参数不能为 null", null); - } - - DatabaseConfigurationFactory.Instance.Current = databaseConfiguration; - return app; - } - - /// - /// 使用指定数据库 - /// - /// - /// DatabaseConfiguration 程序集名称 - /// DatabaseConfiguration 命名空间 - /// DatabaseConfiguration 类名 - /// - public static IApplicationBuilder UseNcfDatabase(this IApplicationBuilder app, string assemblyName, string nameSpace, string className) - { - //TODO:集成到 CO2NET - string fullName = nameSpace + "." + className;//命名空间.类型名 - var databaseConfiguration = Assembly.Load(assemblyName).CreateInstance(fullName) as IDatabaseConfiguration;//加载程序集,创建程序集里面的 命名空间.类型名 实例 - return app.UseNcfDatabase(databaseConfiguration); - } - - /// - /// 使用指定数据库(必须在 UseXncfModule() 方法之后执行) - /// - /// - /// - /// - public static IApplicationBuilder UseNcfDatabase(this IApplicationBuilder app, Type databaseConfigurationType) - { - //必须实现 IDatabaseConfiguration 接口的类型才能进行下一步配置 - if (!typeof(IDatabaseConfiguration).IsAssignableFrom(databaseConfigurationType)) - { - throw new NcfDatabaseException($"类型{databaseConfigurationType.Name} 必须实现接口:IDatabaseConfiguration", databaseConfigurationType); - } - - //判断是否为默认数据库配置(使用 appsettings.json 文件) - if (databaseConfigurationType == typeof(BySettingDatabaseConfiguration)) - { - var dbType = SiteConfig.SenparcCoreSetting.DatabaseType;//此时还没有设置完成 - - if (dbType == null) - { - throw new NcfDatabaseException($"当程序指定了 {databaseConfigurationType.Name} 后,请在 appsettings.json 中的 {nameof(SiteConfig.SenparcCoreSetting.DatabaseType)} 指定数据库类型!", databaseConfigurationType); - } - - var dbTypeStr = dbType.ToString(); - try - { - var typeAssemblyName = $"Senparc.Ncf.Database.{dbTypeStr}"; - var fullTypeName = $"{typeAssemblyName}.{dbType}DatabaseConfiguration, {typeAssemblyName}"; - databaseConfigurationType = Type.GetType(fullTypeName); - - if (databaseConfigurationType == null) - { - throw new NcfDatabaseException($"找不到 {dbTypeStr} 配置对应的数据库配置类:{fullTypeName}", null); - } - //app.UseNcfDatabase(newDbConfigrationType); - } - catch (Exception ex) - { - throw new NcfDatabaseException($"appsettings.json 中的 {nameof(SiteConfig.SenparcCoreSetting.DatabaseType)} 指定数据库类型错误:{SiteConfig.SenparcCoreSetting.DatabaseType}。内部错误信息:{ex.Message}", databaseConfigurationType, inner: ex); - } - } - //else if (databaseConfigurationType == typeof(DatabaseConfiguration)) - //{ - // //供单元测试使用 - // Console.WriteLine("进入单元测试环境"); - //} - - var databaseConfiguration = (IDatabaseConfiguration)Activator.CreateInstance(databaseConfigurationType); - - DatabaseConfigurationFactory.Instance.Current = databaseConfiguration; - - UseNcfDatabaseSetted = true; - - return app; - } - - - /// - /// 使用指定数据库 - /// - /// - /// - public static IApplicationBuilder UseNcfDatabase(this IApplicationBuilder app) - where TDatabaseConfiguration : IDatabaseConfiguration, new() - { - //添加数据库 - app.UseNcfDatabase(typeof(TDatabaseConfiguration)); - return app; - } - - #endregion - } -} +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Senparc.CO2NET.Extensions; +using Senparc.CO2NET.Helpers; +using Senparc.Ncf.Core.Config; +using Senparc.Ncf.Core.Exceptions; +using Senparc.Ncf.Core.Models; +using System; +using System.Reflection; + +namespace Senparc.Ncf.Database +{ + /// + /// Database registration class + /// + public static class Register + { + ///// + ///// Use the specified database + ///// + ///// + ///// + ///// + //public static IServiceCollection AddDatabase(this IServiceCollection services) + // where TDatabaseConfiguration : IDatabaseConfiguration, new() + //{ + // DatabaseConfigurationFactory.Instance.Current = new TDatabaseConfiguration(); + // return services; + //} + + ///// + ///// Use the specified database + ///// + ///// + ///// + ///// + //public static IServiceCollection AddDatabase(this IServiceCollection services, Type databaseConfigurationType) + //{ + // //The type that must implement the IDatabaseConfiguration interface can be used for the next step of configuration. + // if (typeof(IDatabaseConfiguration).IsAssignableFrom(databaseConfigurationType)) + // { + // throw new NcfDatabaseException($"Type {databaseConfigurationType.Name} must implement the interface: IDatabaseConfiguration", databaseConfigurationType); + // } + + // var databaseConfiguration = Activator.CreateInstance(databaseConfigurationType, true) as IDatabaseConfiguration; + + // DatabaseConfigurationFactory.Instance.Current = databaseConfiguration; + // return services; + //} + + ///// + ///// Use the specified database + ///// + ///// + ///// + ///// + //public static IServiceCollection AddDatabase(this IServiceCollection services, IDatabaseConfiguration databaseConfiguration) + //{ + // //The type that must implement the IDatabaseConfiguration interface can be used for the next step of configuration. + // if (databaseConfiguration == null) + // { + // throw new NcfDatabaseException($"{nameof(databaseConfiguration)} parameter cannot be null", null); + // } + + // DatabaseConfigurationFactory.Instance.Current = databaseConfiguration; + // return services; + //} + + ///// + ///// Use the specified database + ///// + ///// + ///// DatabaseConfiguration assembly name + ///// DatabaseConfiguration namespace + ///// DatabaseConfiguration class name + ///// + //public static IServiceCollection AddDatabase(this IServiceCollection services, string assemblyName, string nameSpace, string className) + //{ + // //TODO: Integrated into CO2NET + // string fullName = nameSpace + "." + className;//Namespace.Type name + // var databaseConfiguration = Assembly.Load(assemblyName).CreateInstance(fullName) as IDatabaseConfiguration;//Load the assembly and create the namespace.type name instance in the assembly + // return services.AddDatabase(databaseConfiguration); + //} + + + #region UseNcfDatabase + + /// + /// Whether the UseNcfDatabase() method has been executed + /// + public static bool UseNcfDatabaseSetted = false; + + /// + /// Use the specified database + /// + /// + /// Available DatabaseConfiguration, not + /// + public static IApplicationBuilder UseNcfDatabase(this IApplicationBuilder app, IDatabaseConfiguration databaseConfiguration) + { + //The type that must implement the IDatabaseConfiguration interface can be used for the next step of configuration. + if (databaseConfiguration == null) + { + throw new NcfDatabaseException($"{nameof(databaseConfiguration)} 参数不能为 null", null); + } + + DatabaseConfigurationFactory.Instance.Current = databaseConfiguration; + return app; + } + + /// + /// Use the specified database + /// + /// + /// DatabaseConfiguration assembly name + /// DatabaseConfiguration namespace + /// DatabaseConfiguration class name + /// + public static IApplicationBuilder UseNcfDatabase(this IApplicationBuilder app, string assemblyName, string nameSpace, string className) + { + //TODO: Integrate into CO2NET + string fullName = nameSpace + "." + className;//namespace.typename + var databaseConfiguration = Assembly.Load(assemblyName).CreateInstance(fullName) as IDatabaseConfiguration;//Load the assembly and create the namespace.typename instance in the assembly + return app.UseNcfDatabase(databaseConfiguration); + } + + /// + /// Use the specified database (must be executed after the UseXncfModule() method) + /// + /// + /// + /// + public static IApplicationBuilder UseNcfDatabase(this IApplicationBuilder app, Type databaseConfigurationType) + { + //The type that must implement the IDatabaseConfiguration interface can be used for the next step of configuration. + if (!typeof(IDatabaseConfiguration).IsAssignableFrom(databaseConfigurationType)) + { + throw new NcfDatabaseException($"类型{databaseConfigurationType.Name} 必须实现接口:IDatabaseConfiguration", databaseConfigurationType); + } + + //Determine whether it is the default database configuration (using the appsettings.json file) + if (databaseConfigurationType == typeof(BySettingDatabaseConfiguration)) + { + var dbType = SiteConfig.SenparcCoreSetting.DatabaseType;//The setting has not been completed yet + + if (dbType == null) + { + throw new NcfDatabaseException($"当程序指定了 {databaseConfigurationType.Name} 后,请在 appsettings.json 中的 {nameof(SiteConfig.SenparcCoreSetting.DatabaseType)} 指定数据库类型!", databaseConfigurationType); + } + + var dbTypeStr = dbType.ToString(); + try + { + var typeAssemblyName = $"Senparc.Ncf.Database.{dbTypeStr}"; + var fullTypeName = $"{typeAssemblyName}.{dbType}DatabaseConfiguration, {typeAssemblyName}"; + databaseConfigurationType = Type.GetType(fullTypeName); + + if (databaseConfigurationType == null) + { + throw new NcfDatabaseException($"找不到 {dbTypeStr} 配置对应的数据库配置类:{fullTypeName}", null); + } + //app.UseNcfDatabase(newDbConfigrationType); + } + catch (Exception ex) + { + throw new NcfDatabaseException($"appsettings.json 中的 {nameof(SiteConfig.SenparcCoreSetting.DatabaseType)} 指定数据库类型错误:{SiteConfig.SenparcCoreSetting.DatabaseType}。内部错误信息:{ex.Message}", databaseConfigurationType, inner: ex); + } + } + //else if (databaseConfigurationType == typeof(DatabaseConfiguration)) + //{ + // //for unit testing + // Console.WriteLine("Enter the unit test environment"); + //} + + var databaseConfiguration = (IDatabaseConfiguration)Activator.CreateInstance(databaseConfigurationType); + + DatabaseConfigurationFactory.Instance.Current = databaseConfiguration; + + UseNcfDatabaseSetted = true; + + return app; + } + + + /// + /// Use the specified database + /// + /// + /// + public static IApplicationBuilder UseNcfDatabase(this IApplicationBuilder app) + where TDatabaseConfiguration : IDatabaseConfiguration, new() + { + //Add database + app.UseNcfDatabase(typeof(TDatabaseConfiguration)); + return app; + } + + #endregion + } +} diff --git a/src/Basic/Senparc.Ncf.Database/SenparcDatabaseConnectionConfigs.cs b/src/Basic/Senparc.Ncf.Database/SenparcDatabaseConnectionConfigs.cs index 71d9eb91a..9407bb2cc 100644 --- a/src/Basic/Senparc.Ncf.Database/SenparcDatabaseConnectionConfigs.cs +++ b/src/Basic/Senparc.Ncf.Database/SenparcDatabaseConnectionConfigs.cs @@ -1,88 +1,88 @@ -using Senparc.CO2NET.Cache; -using Senparc.CO2NET.Extensions; -using Senparc.Ncf.Core.Cache; -using Senparc.Ncf.Core.Exceptions; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Core.Utility; -using Senparc.Ncf.Database; -using Senparc.Ncf.Log; -using System; -using System.Collections.Concurrent; - -namespace Senparc.Ncf.Core.Config -{ - /// - /// 数据库连接信息配置 - /// - public static class SenparcDatabaseConnectionConfigs - { - public const string SENPARC_CONFIG_KEY = "__SENPARC_DATABASE_CONNECTION_CONFIG_KEY"; - public static ConcurrentDictionary Configs - { - get - { - Func> func = () => - { - ConcurrentDictionary configs = new ConcurrentDictionary(); - try - { - XmlDataContext xmlCtx = new XmlDataContext(SiteConfig.SenparcConfigDirctory); - var list = xmlCtx.GetXmlList(); - list.ForEach(z => configs[z.Name] = z); - } - catch (Exception e) - { - Console.WriteLine("=== NCF === 读取数据库配置错误:" + e.ToString()); - LogUtility.WebLogger.ErrorFormat("SenparcConfigs.Configs 读取错误:" + e.Message, e); - } - return configs; - }; - - //ICommonDataCache dataCache = new CommonDataCache(SENPARC_CONFIG_KEY, func); - //return dataCache.Data; - - var cacheData = MethodCache.GetMethodCache(SENPARC_CONFIG_KEY, func, 60 * 999); - return cacheData; - } - } - - /// - /// 主站客户数据库连接字符串(根据当前配置数据库动态获得) - /// - public static string GetClientConnectionString() - { - var databaseName = GetFullDatabaseName(Config.SiteConfig.SenparcCoreSetting.DatabaseName ?? "Client"); - - if (SenparcDatabaseConnectionConfigs.Configs != null && SenparcDatabaseConnectionConfigs.Configs.ContainsKey(databaseName)) - { - return SenparcDatabaseConnectionConfigs.Configs[databaseName].ConnectionStringFull; - } - else - { - var code = 0; - if (SenparcDatabaseConnectionConfigs.Configs == null) - { - code = 101; - } - else if (!SenparcDatabaseConnectionConfigs.Configs.ContainsKey(databaseName)) - { - code = 102; - Console.WriteLine("SenparcDatabaseConnectionConfigs.Configs 配置:" + SenparcDatabaseConnectionConfigs.Configs.ToJson(true)); - } - throw new NcfExceptionBase($"无法找到数据库配置:{databaseName},请在 SenparcConfig.config 中进行配置(ErrCode: {code})!"); - } - } - - /// - /// 获取完整名称 - /// - /// - /// - public static string GetFullDatabaseName(string databaseName) - { - var currentDatabaseType = DatabaseConfigurationFactory.Instance.Current.MultipleDatabaseType; - var fullDatabaseName = $"{databaseName}-{currentDatabaseType}"; - return fullDatabaseName; - } - } -} +using Senparc.CO2NET.Cache; +using Senparc.CO2NET.Extensions; +using Senparc.Ncf.Core.Cache; +using Senparc.Ncf.Core.Exceptions; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Core.Utility; +using Senparc.Ncf.Database; +using Senparc.Ncf.Log; +using System; +using System.Collections.Concurrent; + +namespace Senparc.Ncf.Core.Config +{ + /// + /// Database connection information configuration + /// + public static class SenparcDatabaseConnectionConfigs + { + public const string SENPARC_CONFIG_KEY = "__SENPARC_DATABASE_CONNECTION_CONFIG_KEY"; + public static ConcurrentDictionary Configs + { + get + { + Func> func = () => + { + ConcurrentDictionary configs = new ConcurrentDictionary(); + try + { + XmlDataContext xmlCtx = new XmlDataContext(SiteConfig.SenparcConfigDirctory); + var list = xmlCtx.GetXmlList(); + list.ForEach(z => configs[z.Name] = z); + } + catch (Exception e) + { + Console.WriteLine("=== NCF === 读取数据库配置错误:" + e.ToString()); + LogUtility.WebLogger.ErrorFormat("SenparcConfigs.Configs 读取错误:" + e.Message, e); + } + return configs; + }; + + //ICommonDataCache dataCache = new CommonDataCache(SENPARC_CONFIG_KEY, func); + //return dataCache.Data; + + var cacheData = MethodCache.GetMethodCache(SENPARC_CONFIG_KEY, func, 60 * 999); + return cacheData; + } + } + + /// + /// Main site customer database connection string (obtained dynamically based on the current configuration database) + /// + public static string GetClientConnectionString() + { + var databaseName = GetFullDatabaseName(Config.SiteConfig.SenparcCoreSetting.DatabaseName ?? "Client"); + + if (SenparcDatabaseConnectionConfigs.Configs != null && SenparcDatabaseConnectionConfigs.Configs.ContainsKey(databaseName)) + { + return SenparcDatabaseConnectionConfigs.Configs[databaseName].ConnectionStringFull; + } + else + { + var code = 0; + if (SenparcDatabaseConnectionConfigs.Configs == null) + { + code = 101; + } + else if (!SenparcDatabaseConnectionConfigs.Configs.ContainsKey(databaseName)) + { + code = 102; + Console.WriteLine("SenparcDatabaseConnectionConfigs.Configs 配置:" + SenparcDatabaseConnectionConfigs.Configs.ToJson(true)); + } + throw new NcfExceptionBase($"无法找到数据库配置:{databaseName},请在 SenparcConfig.config 中进行配置(ErrCode: {code})!"); + } + } + + /// + /// get full name + /// + /// + /// + public static string GetFullDatabaseName(string databaseName) + { + var currentDatabaseType = DatabaseConfigurationFactory.Instance.Current.MultipleDatabaseType; + var fullDatabaseName = $"{databaseName}-{currentDatabaseType}"; + return fullDatabaseName; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Log/LogUtility.cs b/src/Basic/Senparc.Ncf.Log/LogUtility.cs index 398a9de9c..1aa99dbac 100644 --- a/src/Basic/Senparc.Ncf.Log/LogUtility.cs +++ b/src/Basic/Senparc.Ncf.Log/LogUtility.cs @@ -1,45 +1,45 @@ -using log4net; - -namespace Senparc.Ncf.Log -{ - public static partial class LogUtility - { - public static object LogLock = new object(); - - public static ILog WebLogger => GetLogger("WebLogger"); - public static ILog Cache => GetLogger("Cache"); - public static ILog OperationQueue => GetLogger("OperationQueue"); - public static ILog EmailLogger => GetLogger("EmailLogger"); - public static ILog SystemLogger => GetLogger("SystemLogger"); - public static ILog AccountPayLog => GetLogger("AccountPayLog"); - public static ILog SmsLogger => GetLogger("SmsLogger"); - public static ILog Account => GetLogger("Account"); - public static ILog AdminUserInfo => GetLogger("AdminUserInfo"); - public static ILog WeixinOAuth => GetLogger("WeixinOAuth"); - public static ILog TrackPageLoadPerformance => GetLogger("TrackPageLoadPerformance"); - public static ILog Weixin => GetLogger("Weixin"); - - - public static int Int { get; set; } - //新建的领域可以在这里继续添加 - - public static ILog GetLogger(string name) - { - lock (LogLock) - { - //var appenders = LogManager.GetRepository().GetAppenders(); - //if (appenders.Count() == 0) - //{ - // //没有载入log成功 - // return log4net.LogManager.GetLogger(name); - //} - - //var fileAppender = appenders.First(appender => appender.Name == "SysLogAppender") as RollingFileAppender; - //fileAppender.File = "App_Data/"; - //fileAppender.ActivateOptions(); - //var repository = LogManager.GetRepository(name) ?? LogManager.CreateRepository(name); - return LogManager.GetLogger("NETCoreRepository", name); - } - } - } -} +using log4net; + +namespace Senparc.Ncf.Log +{ + public static partial class LogUtility + { + public static object LogLock = new object(); + + public static ILog WebLogger => GetLogger("WebLogger"); + public static ILog Cache => GetLogger("Cache"); + public static ILog OperationQueue => GetLogger("OperationQueue"); + public static ILog EmailLogger => GetLogger("EmailLogger"); + public static ILog SystemLogger => GetLogger("SystemLogger"); + public static ILog AccountPayLog => GetLogger("AccountPayLog"); + public static ILog SmsLogger => GetLogger("SmsLogger"); + public static ILog Account => GetLogger("Account"); + public static ILog AdminUserInfo => GetLogger("AdminUserInfo"); + public static ILog WeixinOAuth => GetLogger("WeixinOAuth"); + public static ILog TrackPageLoadPerformance => GetLogger("TrackPageLoadPerformance"); + public static ILog Weixin => GetLogger("Weixin"); + + + public static int Int { get; set; } + //New fields can be added here + + public static ILog GetLogger(string name) + { + lock (LogLock) + { + //var appenders = LogManager.GetRepository().GetAppenders(); + //if (appenders.Count() == 0) + //{ + // //No log loaded successfully + // return log4net.LogManager.GetLogger(name); + //} + + //var fileAppender = appenders.First(appender => appender.Name == "SysLogAppender") as RollingFileAppender; + //fileAppender.File = "App_Data/"; + //fileAppender.ActivateOptions(); + //var repository = LogManager.GetRepository(name) ?? LogManager.CreateRepository(name); + return LogManager.GetLogger("NETCoreRepository", name); + } + } + } +} diff --git a/src/Basic/Senparc.Ncf.Log/NLogExtension.Core.cs b/src/Basic/Senparc.Ncf.Log/NLogExtension.Core.cs index 138472fbf..1ee9323aa 100644 --- a/src/Basic/Senparc.Ncf.Log/NLogExtension.Core.cs +++ b/src/Basic/Senparc.Ncf.Log/NLogExtension.Core.cs @@ -1,22 +1,22 @@ -using NLog; -using Senparc.Core.Extensions; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Log -{ - public static class NLogExtension - { - /// - /// 记录错误信息的扩展方法 - /// - /// - /// - /// - public static void ErrorFormat(this Logger logger, string stringFormat, params object[] args) - { - logger.Error(stringFormat.With(args)); - } - } -} +using NLog; +using Senparc.Core.Extensions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Log +{ + public static class NLogExtension + { + /// + /// Extension method for recording error information + /// + /// + /// + /// + public static void ErrorFormat(this Logger logger, string stringFormat, params object[] args) + { + logger.Error(stringFormat.With(args)); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Mvc.UI/CustomBindingHelperExtentions.cs b/src/Basic/Senparc.Ncf.Mvc.UI/CustomBindingHelperExtentions.cs index 07f91604c..36ca766f1 100644 --- a/src/Basic/Senparc.Ncf.Mvc.UI/CustomBindingHelperExtentions.cs +++ b/src/Basic/Senparc.Ncf.Mvc.UI/CustomBindingHelperExtentions.cs @@ -1,84 +1,84 @@ -using System.Text; -using System.Collections.Specialized; -using Microsoft.AspNetCore.Mvc.ViewFeatures; - -namespace System.Web.Mvc -{ - public static class CustomBindingHelperExtentions - { - //public static void UpdateFrom(this object obj, NameValueCollection values, Expression> encoder, params string[] keys) - //{ - // NameValueCollection encodedValues = new NameValueCollection(); - // Func encodeFunc = encoder.Compile(); - // foreach (string key in values.Keys) - // encodedValues.Add(key, encodeFunc(values[key])); - // if (keys.Length > 0) - // obj.UpdateFrom(encodedValues, keys); - // else - // obj.UpdateFrom(encodedValues); - //} - - - public static bool ContainsKey(this NameValueCollection coll, string keyToFind) - { - bool bResult = false; - foreach (string key in coll) - { - if (key.ToLower().Trim().Equals(keyToFind.ToLower().Trim())) - { - bResult = true; - break; - } - } - return bResult; - } - - /// - /// “上移”“下移”箭头 - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static string UpAndDown(this HtmlHelper helper, int listCount, int currentIndex, - string upFunction, string upClassEnable, string upClassDisable, - string downFunction, string downClassEnable, string downClassDisable) - { - StringBuilder result = new StringBuilder(); - result.Append(""); - result.AppendFormat("");//TODO:Put the img in to the css of the up/down SPAN. ---- BY TNT2 2008.5.30 - } - else - { - result.AppendFormat("{0}\" onclick=\"{1}\" >", upClassEnable, upFunction); - result.AppendFormat(""); - } - result.Append("上移"); - - result.Append(" = listCount) - { - result.Append(downClassDisable + "\" >"); - result.AppendFormat(""); - - } - else - { - result.AppendFormat("{0}\" onclick=\"{1}\" >", downClassEnable, downFunction); - result.AppendFormat(""); - } - result.Append("下移"); - - return result.ToString(); - } - } +using System.Text; +using System.Collections.Specialized; +using Microsoft.AspNetCore.Mvc.ViewFeatures; + +namespace System.Web.Mvc +{ + public static class CustomBindingHelperExtentions + { + //public static void UpdateFrom(this object obj, NameValueCollection values, Expression> encoder, params string[] keys) + //{ + // NameValueCollection encodedValues = new NameValueCollection(); + // Func encodeFunc = encoder.Compile(); + // foreach (string key in values.Keys) + // encodedValues.Add(key, encodeFunc(values[key])); + // if (keys.Length > 0) + // obj.UpdateFrom(encodedValues, keys); + // else + // obj.UpdateFrom(encodedValues); + //} + + + public static bool ContainsKey(this NameValueCollection coll, string keyToFind) + { + bool bResult = false; + foreach (string key in coll) + { + if (key.ToLower().Trim().Equals(keyToFind.ToLower().Trim())) + { + bResult = true; + break; + } + } + return bResult; + } + + /// + /// "Move up" "Move down" arrows + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static string UpAndDown(this HtmlHelper helper, int listCount, int currentIndex, + string upFunction, string upClassEnable, string upClassDisable, + string downFunction, string downClassEnable, string downClassDisable) + { + StringBuilder result = new StringBuilder(); + result.Append(""); + result.AppendFormat("");//TODO:Put the img in to the css of the up/down SPAN. ---- BY TNT2 2008.5.30 + } + else + { + result.AppendFormat("{0}\" onclick=\"{1}\" >", upClassEnable, upFunction); + result.AppendFormat(""); + } + result.Append("上移"); + + result.Append(" = listCount) + { + result.Append(downClassDisable + "\" >"); + result.AppendFormat(""); + + } + else + { + result.AppendFormat("{0}\" onclick=\"{1}\" >", downClassEnable, downFunction); + result.AppendFormat(""); + } + result.Append("下移"); + + return result.ToString(); + } + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Mvc.UI/HtmlExtension.Core.cs b/src/Basic/Senparc.Ncf.Mvc.UI/HtmlExtension.Core.cs index 19ffe65d2..df3a07ff5 100644 --- a/src/Basic/Senparc.Ncf.Mvc.UI/HtmlExtension.Core.cs +++ b/src/Basic/Senparc.Ncf.Mvc.UI/HtmlExtension.Core.cs @@ -1,128 +1,128 @@ -using System; -using System.Collections.Generic; -using Senparc.Ncf.Core.Utility; -using System.Linq.Expressions; -using Microsoft.AspNetCore.Html; -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Microsoft.AspNetCore.Mvc.Rendering; - -namespace Senparc.Ncf.Mvc.UI -{ - public static partial class HtmlExtension - { - //#region CustomSelect - ///// - ///// 返回由枚举类型生成的自定义样式下拉选项 - ///// - //public static HtmlString CustomDropDownListFormEnum(this HtmlHelper htmlHelper, string name, Type enumType, object selectedValue = null, string optionLabel = null, object htmlAttributes = null, bool useDescription = false, bool addBlankOption = false, string blankOptionText = null) - //{ - // var selectedList = GetSelectListFromEnum(enumType, selectedValue, useDescription, addBlankOption, blankOptionText); - // StringBuilder result = new StringBuilder(); - // foreach (var item in selectedList) - // { - // result.AppendFormat(""); - // } - // return htmlHelper.DropDownList(name, selectedList, optionLabel, htmlAttributes); - //} - //#endregion - - #region DropDownList - - /// - /// 返回由枚举类型生成的下拉选项 - /// - public static IHtmlContent DropDownListFormEnumFor(this HtmlHelper htmlHelper, Expression> expression, Type enumType, object selectedValue = null, string optionLabel = null, object htmlAttributes = null, bool useDescription = false, bool addBlankOption = false) - { - var selectedList = GetSelectListFromEnum(enumType, selectedValue, useDescription, addBlankOption); - return htmlHelper.DropDownListFor(expression, selectedList, optionLabel, htmlAttributes); - } - /// - /// 返回由枚举类型生成的下拉选项 - /// - public static IHtmlContent DropDownListFormEnum(this HtmlHelper htmlHelper, string name, Type enumType, object selectedValue = null, string optionLabel = null, object htmlAttributes = null, bool useDescription = false, bool addBlankOption = false, string blankOptionText = null) - { - var selectedList = GetSelectListFromEnum(enumType, selectedValue, useDescription, addBlankOption, blankOptionText); - return htmlHelper.DropDownList(name, selectedList, optionLabel, htmlAttributes); - } - - public static HtmlString GetDescriptionForEnum(this HtmlHelper htmlHelper, Type enumType, int item) - { - return new HtmlString(enumType.GetDescriptionForEnum(item)); - } - - private static SelectList GetSelectListFromEnum(Type enumType, object selectedValue, bool useDescription = false, bool addBlankOption = false, string blankOptionText = null) - { - var dic = Extensions.GetDictionaryForEnums(enumType, useDescription, addBlankOption, blankOptionText); - return GetSelectListFromDictionary(dic, selectedValue); - } - - private static SelectList GetSelectListFromDictionary(Dictionary dic, object selectedValue) - { - return new SelectList(dic, "Key", "Value", selectedValue); - } - #endregion - - - #region Link - //public static IHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, bool requireAbsoluteUrl) - //{ - // return htmlHelper.ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(), new RouteValueDictionary(), requireAbsoluteUrl); - //} - - // more of these - - //public static IHtmlContent ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes, bool requireAbsoluteUrl) - //{ - // if (requireAbsoluteUrl) - // { - // HttpContextBase currentContext = new HttpContextWrapper(HttpContext.Current); - // RouteData routeData = RouteTable.Routes.GetRouteData(currentContext); - - // routeData.Values["controller"] = controllerName; - // routeData.Values["action"] = actionName; - - // DomainRoute domainRoute = routeData.Route as DomainRoute; - // if (domainRoute != null) - // { - // DomainData domainData = domainRoute.GetDomainData(new RequestContext(currentContext, routeData), routeData.Values); - // return htmlHelper.ActionLink(linkText, actionName, controllerName, domainData.Protocol, domainData.HostName, domainData.Fragment, routeData.Values, null); - // } - // } - // return htmlHelper.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes); - //} - - //#region 直接引用代码(仅测试):http://www.veryhuo.com/a/view/7590.html - - //public static IHtmlContent ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, bool requireAbsoluteUrl) - //{ - // return htmlHelper.ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(), new RouteValueDictionary(), requireAbsoluteUrl); - //} - - //// more of these - - //public static IHtmlContent ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary htmlAttributes, bool requireAbsoluteUrl) - //{ - // if (requireAbsoluteUrl) - // { - // HttpContextBase currentContext = new HttpContextWrapper(HttpContext.Current); - // RouteData routeData = RouteTable.Routes.GetRouteData(currentContext); - - // routeData.Values["controller"] = controllerName; - // routeData.Values["action"] = actionName; - - // DomainRoute domainRoute = routeData.Route as DomainRoute; - // if (domainRoute != null) - // { - // DomainData domainData = domainRoute.GetDomainData(new RequestContext(currentContext, routeData), routeData.Values); - // return htmlHelper.ActionLink(linkText, actionName, controllerName, domainData.Protocol, domainData.HostName, domainData.Fragment, routeData.Values, null); - // } - // } - // return htmlHelper.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes); - //} - - //#endregion - - #endregion - - } -} +using System; +using System.Collections.Generic; +using Senparc.Ncf.Core.Utility; +using System.Linq.Expressions; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Mvc.Rendering; + +namespace Senparc.Ncf.Mvc.UI +{ + public static partial class HtmlExtension + { + //#region CustomSelect + ///// + ///// Returns the custom style drop-down option generated by the enumeration type + ///// + //public static HtmlString CustomDropDownListFormEnum(this HtmlHelper htmlHelper, string name, Type enumType, object selectedValue = null, string optionLabel = null, object htmlAttributes = null, bool useDescription = false, bool addBlankOption = false, string blankOptionText = null) + //{ + // var selectedList = GetSelectListFromEnum(enumType, selectedValue, useDescription, addBlankOption, blankOptionText); + // StringBuilder result = new StringBuilder(); + // foreach (var item in selectedList) + // { + // result.AppendFormat(""); + // } + // return htmlHelper.DropDownList(name, selectedList, optionLabel, htmlAttributes); + //} + //#endregion + + #region DropDownList + + /// + /// Returns the drop-down options generated by the enumeration type + /// + public static IHtmlContent DropDownListFormEnumFor(this HtmlHelper htmlHelper, Expression> expression, Type enumType, object selectedValue = null, string optionLabel = null, object htmlAttributes = null, bool useDescription = false, bool addBlankOption = false) + { + var selectedList = GetSelectListFromEnum(enumType, selectedValue, useDescription, addBlankOption); + return htmlHelper.DropDownListFor(expression, selectedList, optionLabel, htmlAttributes); + } + /// + /// Returns the drop-down options generated by the enumeration type + /// + public static IHtmlContent DropDownListFormEnum(this HtmlHelper htmlHelper, string name, Type enumType, object selectedValue = null, string optionLabel = null, object htmlAttributes = null, bool useDescription = false, bool addBlankOption = false, string blankOptionText = null) + { + var selectedList = GetSelectListFromEnum(enumType, selectedValue, useDescription, addBlankOption, blankOptionText); + return htmlHelper.DropDownList(name, selectedList, optionLabel, htmlAttributes); + } + + public static HtmlString GetDescriptionForEnum(this HtmlHelper htmlHelper, Type enumType, int item) + { + return new HtmlString(enumType.GetDescriptionForEnum(item)); + } + + private static SelectList GetSelectListFromEnum(Type enumType, object selectedValue, bool useDescription = false, bool addBlankOption = false, string blankOptionText = null) + { + var dic = Extensions.GetDictionaryForEnums(enumType, useDescription, addBlankOption, blankOptionText); + return GetSelectListFromDictionary(dic, selectedValue); + } + + private static SelectList GetSelectListFromDictionary(Dictionary dic, object selectedValue) + { + return new SelectList(dic, "Key", "Value", selectedValue); + } + #endregion + + + #region Link + //public static IHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, bool requireAbsoluteUrl) + //{ + // return htmlHelper.ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(), new RouteValueDictionary(), requireAbsoluteUrl); + //} + + // more of these + + //public static IHtmlContent ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes, bool requireAbsoluteUrl) + //{ + // if (requireAbsoluteUrl) + // { + // HttpContextBase currentContext = new HttpContextWrapper(HttpContext.Current); + // RouteData routeData = RouteTable.Routes.GetRouteData(currentContext); + + // routeData.Values["controller"] = controllerName; + // routeData.Values["action"] = actionName; + + // DomainRoute domainRoute = routeData.Route as DomainRoute; + // if (domainRoute != null) + // { + // DomainData domainData = domainRoute.GetDomainData(new RequestContext(currentContext, routeData), routeData.Values); + // return htmlHelper.ActionLink(linkText, actionName, controllerName, domainData.Protocol, domainData.HostName, domainData.Fragment, routeData.Values, null); + // } + // } + // return htmlHelper.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes); + //} + + //#region Directly quote the code (testing only): http://www.veryhuo.com/a/view/7590.html + + //public static IHtmlContent ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, bool requireAbsoluteUrl) + //{ + // return htmlHelper.ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(), new RouteValueDictionary(), requireAbsoluteUrl); + //} + + //// more of these + + //public static IHtmlContent ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary htmlAttributes, bool requireAbsoluteUrl) + //{ + // if (requireAbsoluteUrl) + // { + // HttpContextBase currentContext = new HttpContextWrapper(HttpContext.Current); + // RouteData routeData = RouteTable.Routes.GetRouteData(currentContext); + + // routeData.Values["controller"] = controllerName; + // routeData.Values["action"] = actionName; + + // DomainRoute domainRoute = routeData.Route as DomainRoute; + // if (domainRoute != null) + // { + // DomainData domainData = domainRoute.GetDomainData(new RequestContext(currentContext, routeData), routeData.Values); + // return htmlHelper.ActionLink(linkText, actionName, controllerName, domainData.Protocol, domainData.HostName, domainData.Fragment, routeData.Values, null); + // } + // } + // return htmlHelper.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes); + //} + + //#endregion + + #endregion + + } +} diff --git a/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/CurrentBsMenuExtensions.cs b/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/CurrentBsMenuExtensions.cs index 58a700cd0..fc11e4c7f 100644 --- a/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/CurrentBsMenuExtensions.cs +++ b/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/CurrentBsMenuExtensions.cs @@ -1,44 +1,44 @@ -using Microsoft.AspNetCore.Mvc.Rendering; -using Senparc.CO2NET.Extensions; -using Senparc.Ncf.Core.Models.VD; - -namespace System.Web.Mvc -{ - public static class CurrentBsMenuExtensions - { - /// - /// Bootstrap当前菜单 - /// - /// - /// - /// - public static string CurrentBsMenu(this IHtmlHelper htmlHelper, string menuName) - { - if (htmlHelper.ViewData.Model is IBaseUiVD) - { - IBaseUiVD model = htmlHelper.ViewData.Model as IBaseUiVD; - if (!model.CurrentMenu.IsNullOrEmpty()) - { - var parentMenuMane = model.CurrentMenu.Split('.')[0]; - if (model.CurrentMenu.Equals(menuName, StringComparison.OrdinalIgnoreCase) - || parentMenuMane.Equals(menuName, StringComparison.OrdinalIgnoreCase)) - { - return "active"; - } - else - { - return ""; - } - } - else - { - return ""; - } - } - else - { - return ""; - } - } - } -} +using Microsoft.AspNetCore.Mvc.Rendering; +using Senparc.CO2NET.Extensions; +using Senparc.Ncf.Core.Models.VD; + +namespace System.Web.Mvc +{ + public static class CurrentBsMenuExtensions + { + /// + ///Bootstrap current menu + /// + /// + /// + /// + public static string CurrentBsMenu(this IHtmlHelper htmlHelper, string menuName) + { + if (htmlHelper.ViewData.Model is IBaseUiVD) + { + IBaseUiVD model = htmlHelper.ViewData.Model as IBaseUiVD; + if (!model.CurrentMenu.IsNullOrEmpty()) + { + var parentMenuMane = model.CurrentMenu.Split('.')[0]; + if (model.CurrentMenu.Equals(menuName, StringComparison.OrdinalIgnoreCase) + || parentMenuMane.Equals(menuName, StringComparison.OrdinalIgnoreCase)) + { + return "active"; + } + else + { + return ""; + } + } + else + { + return ""; + } + } + else + { + return ""; + } + } + } +} diff --git a/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/GridViewActionExtension.cs b/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/GridViewActionExtension.cs index c6af3bdf0..856489629 100644 --- a/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/GridViewActionExtension.cs +++ b/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/GridViewActionExtension.cs @@ -1,66 +1,66 @@ -using System.Collections.Generic; -using Microsoft.AspNetCore.Mvc.ViewFeatures; - -namespace System.Web.Mvc -{ - public class GridViewActionItemTemplateModel - { - public string Header { get; set; } - public Action ItemTemplate { get; set; } - public Action Footer { get; set; } - public object HtmlAttributes { get; set; } - //public Action EditTemplate { get; set; } - - - public GridViewActionItemTemplateModel() { } - - public GridViewActionItemTemplateModel(string header, Action itemTemplate, Action footer, object htmlAttributes) - { - this.Header = header; - this.ItemTemplate = itemTemplate; - this.Footer = footer; - this.HtmlAttributes = htmlAttributes.ToAttributeList(); - } - - public GridViewActionItemTemplateModel(string header, Action itemTemplate, object htmlAttributes) - : this(header, itemTemplate, null, htmlAttributes) - { } - - public GridViewActionItemTemplateModel(Action itemTemplate) - : this(null, itemTemplate, null) - { } - - //public GridViewItemTemplateModel(Func itemTemplate, Action editTemplate) - //{ - // this.ItemTemplate = itemTemplate; - // this.EditTemplate = editTemplate; - //} - } - - public static class GridViewActionExtension - { - /// - /// GridView - /// - /// - /// - /// 数据源 - /// Table的属性 - /// 没有数据时显示。没有数据时,Footer不显示 - /// HTML代码换行(供调试状态下使用)。如果为false,自动生成的代码将不换行 - /// 模板数据 - /// - public static string GridViewAction(this HtmlHelper helper, IEnumerable dataSource, - object htmlAttributes, string emptyTemplete, bool htmlCodeFormat, params GridViewActionItemTemplateModel[] itemTempletes) - { - foreach (var item in dataSource) - { - foreach (var temp in itemTempletes) - { - temp.ItemTemplate(item, 0); - } - } - return "ddd"; - } - } -} +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc.ViewFeatures; + +namespace System.Web.Mvc +{ + public class GridViewActionItemTemplateModel + { + public string Header { get; set; } + public Action ItemTemplate { get; set; } + public Action Footer { get; set; } + public object HtmlAttributes { get; set; } + //public Action EditTemplate { get; set; } + + + public GridViewActionItemTemplateModel() { } + + public GridViewActionItemTemplateModel(string header, Action itemTemplate, Action footer, object htmlAttributes) + { + this.Header = header; + this.ItemTemplate = itemTemplate; + this.Footer = footer; + this.HtmlAttributes = htmlAttributes.ToAttributeList(); + } + + public GridViewActionItemTemplateModel(string header, Action itemTemplate, object htmlAttributes) + : this(header, itemTemplate, null, htmlAttributes) + { } + + public GridViewActionItemTemplateModel(Action itemTemplate) + : this(null, itemTemplate, null) + { } + + //public GridViewItemTemplateModel(Func itemTemplate, Action editTemplate) + //{ + // this.ItemTemplate = itemTemplate; + // this.EditTemplate = editTemplate; + //} + } + + public static class GridViewActionExtension + { + /// + /// GridView + /// + /// + /// + /// Data source + /// Table attributes + /// Displayed when there is no data. When there is no data, Footer does not display + /// HTML code line wrapping (for use in debugging state). If false, automatically generated code will not wrap. + /// Template data + /// + public static string GridViewAction(this HtmlHelper helper, IEnumerable dataSource, + object htmlAttributes, string emptyTemplete, bool htmlCodeFormat, params GridViewActionItemTemplateModel[] itemTempletes) + { + foreach (var item in dataSource) + { + foreach (var temp in itemTempletes) + { + temp.ItemTemplate(item, 0); + } + } + return "ddd"; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/GridViewExtension.cs b/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/GridViewExtension.cs index 18f49bf47..68af39528 100644 --- a/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/GridViewExtension.cs +++ b/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/GridViewExtension.cs @@ -1,253 +1,253 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Reflection; -using System.Linq.Expressions; -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Microsoft.AspNetCore.Html; - -namespace System.Web.Mvc -{ - public class GridViewItemTemplateModel - { - public string Header { get; set; } - public Func ItemTemplate { get; set; } - public Func, string> Footer { get; set; } - public object HtmlAttributes { get; set; } - //public Action EditTemplate { get; set; } - - - public GridViewItemTemplateModel() { } - - public GridViewItemTemplateModel(string header, Func itemTemplate, Func, string> footer, object htmlAttributes) - { - this.Header = header; - this.ItemTemplate = itemTemplate; - this.Footer = footer; - this.HtmlAttributes = htmlAttributes.ToAttributeList(); - } - - public GridViewItemTemplateModel(string header, Func itemTemplate, object htmlAttributes = null) - : this(header, itemTemplate, null, htmlAttributes) - { } - - public GridViewItemTemplateModel(Func itemTemplate, object htmlAttributes = null) - : this(null, itemTemplate, htmlAttributes) - { } - - //public GridViewItemTemplateModel(Func itemTemplate, Action editTemplate) - //{ - // this.ItemTemplate = itemTemplate; - // this.EditTemplate = editTemplate; - //} - } - - public static class GridViewExtension - { - /// - /// GridView - /// - /// - /// - /// 数据源 - /// Table的属性 - /// 没有数据时显示。没有数据时,Footer不显示 - /// HTML代码换行(供调试状态下使用)。如果为false,自动生成的代码将不换行 - /// 模板数据 - /// - public static HtmlString GridView(this HtmlHelper helper, IEnumerable dataSource, - object htmlAttributes, string emptyTemplete, bool htmlCodeFormat, params GridViewItemTemplateModel[] itemTempletes) where T : class - { - string tableFormat = "{1}
Error page URLError typeError page parent page URL
"; - - string headFormat = "{0}"; - string headUnitFormat = "{0}"; - - string bodyFormat = "{0}"; - - string rowFormat = "{0}"; - string UnitFormat = "{1}";//属性,内容 - - string footFormat = "{0}"; - - string emptyFormat = "{0}"; - - StringBuilder table = new StringBuilder();//整个Table - - //thead - StringBuilder thead = new StringBuilder();//thead - StringBuilder theadUints = new StringBuilder();//thead内单元格 - - //body - StringBuilder tbody = new StringBuilder();//tbody - StringBuilder tbodyRows = new StringBuilder();//行 - - //tfoot - StringBuilder tfoot = new StringBuilder();//tfoot - StringBuilder tfootUints = new StringBuilder();//tfoot内的单元格 - - StringBuilder empty = new StringBuilder(); - - - /*******************/ - /** 构造Table开始 **/ - /*******************/ - - //thead - foreach (var itemTemplete in itemTempletes) - { - theadUints.AppendFormat(headUnitFormat, itemTemplete.Header == null ? "" : itemTemplete.Header); - } - thead.AppendFormat(headFormat, string.Format(rowFormat, theadUints.ToString()));//head行 - - - /* tbody 开始 */ - string[] colAttributes = itemTempletes.Select(z => z.HtmlAttributes.ToAttributeList()).ToArray(); - - dataSource = dataSource ?? new List();//如果数据为空,则创建一个空的记录,用于获取Count - int dataSourceCount = dataSource.Count(); - if (dataSourceCount > 0) - { - int rowIndex = 0; - foreach (var data in dataSource) - { - StringBuilder units = new StringBuilder();//tbody一行内的单元格 - foreach (var itemTemplete in itemTempletes) - { - string itemResult = itemTemplete.ItemTemplate(data, rowIndex) == null - ? "" - : itemTemplete.ItemTemplate(data, rowIndex).ToString();//本行模板数据 - - //判断body单元格是否自动绑定 - if (itemResult.StartsWith("$")) - { - //自动绑定 - string bindDataName = itemResult.Replace("$", ""); - var pro = data.GetType().GetProperty(bindDataName); - itemResult = data.GetType().GetProperty(bindDataName).GetValue(data, null).ToString(); - } - - //单元格属性 - - units.AppendFormat(UnitFormat, itemTemplete.HtmlAttributes != null ? itemTemplete.HtmlAttributes.ToString() : "", itemResult);//单元格 - } - tbodyRows.AppendFormat(rowFormat, units.ToString());//行 - rowIndex++; - } - tbody.AppendFormat(bodyFormat, tbodyRows.ToString()); //整个tbody - /* tbody 结束 */ - - - //tfoot - foreach (var itemTemplete in itemTempletes) - { - string result = ""; - - if (itemTemplete.Footer != null) - result = itemTemplete.Footer(dataSource); - - tfootUints.AppendFormat(UnitFormat, "", result);//footer没有? - } - tfoot.AppendFormat(footFormat, string.Format(rowFormat, tfootUints.ToString()));//foot行 - } - else - { - empty.AppendFormat(emptyFormat, emptyTemplete);//空数据 - } - - - //整合head,body,foot - StringBuilder tableData = new StringBuilder(); - tableData.Append(thead.ToString()).Append(tbody.ToString()).Append(tfoot.ToString()); - - - //属性 - string setHash = htmlAttributes.ToAttributeList(); - string attributeList = string.Empty; - if (setHash != null) - attributeList = setHash; - - table.AppendFormat(tableFormat, attributeList, tableData.ToString());//整合整个Table,包括属性 - table.Append(empty.ToString());//空数据 - - - /*******************/ - /** 构造Table结束 **/ - /*******************/ - - string tableHtml = string.Empty; - - if (htmlCodeFormat) - return new HtmlString(table.Replace("><", ">\r\n<").ToString()); - else - return new HtmlString(table.ToString()); - - } - - - /// - /// 自动绑定GridView - /// - /// - /// - /// - /// - /// - public static string GridView(this HtmlHelper helper, IEnumerable dataSource, object htmlAttributes) - { - List>> itemTempletes = new List>>(); - PropertyInfo[] propertys = typeof(T).GetProperties().Where(z => z.PropertyType.IsValueType).ToArray();//过滤为值类型 - string[] header = propertys.Select(z => z.Name).ToArray(); - - - //属性 - var setHash = htmlAttributes.ToAttributeList(); - - string attributeList = string.Empty; - if (setHash != null) - attributeList = setHash; - - StringBuilder table = new StringBuilder(); - table.AppendFormat("", attributeList); - foreach (var item in header) - { - table.AppendFormat("", item); - } - table.Append(""); - - foreach (var item in dataSource) - { - var members = item.GetType().GetMembers(); - table.Append(""); - foreach (var pro in propertys) - { - table.AppendFormat("", pro.GetValue(item, null)); - } - table.Append(""); - } - table.Append("
{0}
{0}
"); - - - return table.ToString(); - //return GridView(helper, dataSource, header, null, htmlAttributes, itemTempletes.ToArray()); - } - - //public static string GridView(this HtmlHelper helper, IEnumerable dataSource, - // string[] header, string[] footer, - // object htmlAttributes, params Expression>[] itemTempletes) - //{ - // List>> items = new List>>(); - - // //foreach (var item in itemTempletes) - // //{ - // // items.Add(); - // //} - - // //return GridView(helper, dataSource, header, footer, htmlAttributes, itemTempletes.Select( - // // z => z.Compile().); - - // return GridView(helper, dataSource, header, footer, htmlAttributes, items.ToArray()); - - //} - } -} +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; +using System.Linq.Expressions; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Html; + +namespace System.Web.Mvc +{ + public class GridViewItemTemplateModel + { + public string Header { get; set; } + public Func ItemTemplate { get; set; } + public Func, string> Footer { get; set; } + public object HtmlAttributes { get; set; } + //public Action EditTemplate { get; set; } + + + public GridViewItemTemplateModel() { } + + public GridViewItemTemplateModel(string header, Func itemTemplate, Func, string> footer, object htmlAttributes) + { + this.Header = header; + this.ItemTemplate = itemTemplate; + this.Footer = footer; + this.HtmlAttributes = htmlAttributes.ToAttributeList(); + } + + public GridViewItemTemplateModel(string header, Func itemTemplate, object htmlAttributes = null) + : this(header, itemTemplate, null, htmlAttributes) + { } + + public GridViewItemTemplateModel(Func itemTemplate, object htmlAttributes = null) + : this(null, itemTemplate, htmlAttributes) + { } + + //public GridViewItemTemplateModel(Func itemTemplate, Action editTemplate) + //{ + // this.ItemTemplate = itemTemplate; + // this.EditTemplate = editTemplate; + //} + } + + public static class GridViewExtension + { + /// + /// GridView + /// + /// + /// + /// Data source + /// Table attributes + /// Displayed when there is no data. When there is no data, Footer does not display + /// HTML code line wrapping (for use in debugging state). If false, automatically generated code will not wrap. + /// Template data + /// + public static HtmlString GridView(this HtmlHelper helper, IEnumerable dataSource, + object htmlAttributes, string emptyTemplete, bool htmlCodeFormat, params GridViewItemTemplateModel[] itemTempletes) where T : class + { + string tableFormat = "{1}"; + + string headFormat = "{0}"; + string headUnitFormat = "{0}"; + + string bodyFormat = "{0}"; + + string rowFormat = "{0}"; + string UnitFormat = "{1}";//properties, content + + string footFormat = "{0}"; + + string emptyFormat = "{0}"; + + StringBuilder table = new StringBuilder();//Entire Table + + //thead + StringBuilder thead = new StringBuilder();//thead + StringBuilder theadUints = new StringBuilder();//cells within thead + + //body + StringBuilder tbody = new StringBuilder();//tbody + StringBuilder tbodyRows = new StringBuilder();//OK + + //tfoot + StringBuilder tfoot = new StringBuilder();//tfoot + StringBuilder tfootUints = new StringBuilder();//cells within tfoot + + StringBuilder empty = new StringBuilder(); + + + /*******************/ + /** Start constructing Table **/ + /*******************/ + + //thead + foreach (var itemTemplete in itemTempletes) + { + theadUints.AppendFormat(headUnitFormat, itemTemplete.Header == null ? "" : itemTemplete.Header); + } + thead.AppendFormat(headFormat, string.Format(rowFormat, theadUints.ToString()));//head line + + + /* tbody start */ + string[] colAttributes = itemTempletes.Select(z => z.HtmlAttributes.ToAttributeList()).ToArray(); + + dataSource = dataSource ?? new List();//If the data is empty, create an empty record for getting Count + int dataSourceCount = dataSource.Count(); + if (dataSourceCount > 0) + { + int rowIndex = 0; + foreach (var data in dataSource) + { + StringBuilder units = new StringBuilder();//cells within a row of tbody + foreach (var itemTemplete in itemTempletes) + { + string itemResult = itemTemplete.ItemTemplate(data, rowIndex) == null + ? "" + : itemTemplete.ItemTemplate(data, rowIndex).ToString();//Template data of the Bank + + //Determine whether the body cell is automatically bound + if (itemResult.StartsWith("$")) + { + //Automatic binding + string bindDataName = itemResult.Replace("$", ""); + var pro = data.GetType().GetProperty(bindDataName); + itemResult = data.GetType().GetProperty(bindDataName).GetValue(data, null).ToString(); + } + + //Cell properties + + units.AppendFormat(UnitFormat, itemTemplete.HtmlAttributes != null ? itemTemplete.HtmlAttributes.ToString() : "", itemResult);//cell + } + tbodyRows.AppendFormat(rowFormat, units.ToString());//OK + rowIndex++; + } + tbody.AppendFormat(bodyFormat, tbodyRows.ToString()); //the whole tbody + /* tbody end */ + + + //tfoot + foreach (var itemTemplete in itemTempletes) + { + string result = ""; + + if (itemTemplete.Footer != null) + result = itemTemplete.Footer(dataSource); + + tfootUints.AppendFormat(UnitFormat, "", result);//There is no in footer? + } + tfoot.AppendFormat(footFormat, string.Format(rowFormat, tfootUints.ToString()));//foot row + } + else + { + empty.AppendFormat(emptyFormat, emptyTemplete);//empty data + } + + + //Integrate head, body, foot + StringBuilder tableData = new StringBuilder(); + tableData.Append(thead.ToString()).Append(tbody.ToString()).Append(tfoot.ToString()); + + + //property + string setHash = htmlAttributes.ToAttributeList(); + string attributeList = string.Empty; + if (setHash != null) + attributeList = setHash; + + table.AppendFormat(tableFormat, attributeList, tableData.ToString());//Integrate the entire Table, including attributes + table.Append(empty.ToString());//empty data + + + /*******************/ + /** End of constructing Table **/ + /*******************/ + + string tableHtml = string.Empty; + + if (htmlCodeFormat) + return new HtmlString(table.Replace("><", ">\r\n<").ToString()); + else + return new HtmlString(table.ToString()); + + } + + + /// + /// Automatically bind GridView + /// + /// + /// + /// + /// + /// + public static string GridView(this HtmlHelper helper, IEnumerable dataSource, object htmlAttributes) + { + List>> itemTempletes = new List>>(); + PropertyInfo[] propertys = typeof(T).GetProperties().Where(z => z.PropertyType.IsValueType).ToArray();//Filter to value type + string[] header = propertys.Select(z => z.Name).ToArray(); + + + //property + var setHash = htmlAttributes.ToAttributeList(); + + string attributeList = string.Empty; + if (setHash != null) + attributeList = setHash; + + StringBuilder table = new StringBuilder(); + table.AppendFormat("", attributeList); + foreach (var item in header) + { + table.AppendFormat("", item); + } + table.Append(""); + + foreach (var item in dataSource) + { + var members = item.GetType().GetMembers(); + table.Append(""); + foreach (var pro in propertys) + { + table.AppendFormat("", pro.GetValue(item, null)); + } + table.Append(""); + } + table.Append("
{0}
{0}
"); + + + return table.ToString(); + //return GridView(helper, dataSource, header, null, htmlAttributes, itemTempletes.ToArray()); + } + + //public static string GridView(this HtmlHelper helper, IEnumerable dataSource, + // string[] header, string[] footer, + // object htmlAttributes, params Expression>[] itemTempletes) + //{ + // List>> items = new List>>(); + + // //foreach (var item in itemTempletes) + // //{ + // // items.Add(); + // //} + + // //return GridView(helper, dataSource, header, footer, htmlAttributes, itemTempletes.Select( + // // z => z.Compile().); + + // return GridView(helper, dataSource, header, footer, htmlAttributes, items.ToArray()); + + //} + } +} diff --git a/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/OnClickSpanExtension.cs b/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/OnClickSpanExtension.cs index 6bd5ee1af..0f376c244 100644 --- a/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/OnClickSpanExtension.cs +++ b/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/OnClickSpanExtension.cs @@ -1,57 +1,57 @@ -using System.Text; -using Microsoft.AspNetCore.Mvc.ViewFeatures; - -namespace System.Web.Mvc -{ - - public static class OnClickSpanExtension - { - /// - /// 供Onclick操作的span(样式默认为“onclick”) - /// - /// - /// 事件名称(onclick中的所有内容) - /// 文字 - /// - public static string OnClickSpan(this HtmlHelper helper, string text, string onclickMethod) - { - return OnClickSpan(helper, text, onclickMethod, null, null); - } - - /// - /// 供Onclick操作的span(样式默认为“onclick”) - /// - /// - /// 事件名称(onclick中的所有内容) - /// 文字 - /// - public static string OnClickSpan(this HtmlHelper helper, string text, string onclickMethod, object htmlAttributes) - { - return OnClickSpan(helper, text, onclickMethod, null, htmlAttributes); - } - - /// - /// 供Onclick操作的span - /// - /// - /// class样式,如果为空,则使用“onclick” - /// 事件名称(onclick中的所有内容) - /// 文字 - /// - public static string OnClickSpan(this HtmlHelper helper, string text, string onclickMethod, string cssClass, object htmlAttributes) - { - //样式 - cssClass = cssClass ?? "onclick"; - //属性 - string setHash = htmlAttributes.ToAttributeList(); - string attributeList = string.Empty; - if (setHash != null) - attributeList = setHash; - - StringBuilder html = new StringBuilder(); - html.AppendFormat("{2}", onclickMethod.Replace("\"","\\\'"), attributeList, text); - - return html.ToString(); - } - } -} +using System.Text; +using Microsoft.AspNetCore.Mvc.ViewFeatures; + +namespace System.Web.Mvc +{ + + public static class OnClickSpanExtension + { + /// + /// span for Onclick operation (the style defaults to "onclick") + /// + /// + /// Event name (everything in onclick) + /// Text + /// + public static string OnClickSpan(this HtmlHelper helper, string text, string onclickMethod) + { + return OnClickSpan(helper, text, onclickMethod, null, null); + } + + /// + /// span for Onclick operation (the style defaults to "onclick") + /// + /// + /// Event name (everything in onclick) + /// Text + /// + public static string OnClickSpan(this HtmlHelper helper, string text, string onclickMethod, object htmlAttributes) + { + return OnClickSpan(helper, text, onclickMethod, null, htmlAttributes); + } + + /// + /// span for Onclick operation + /// + /// + /// class style, if empty, use "onclick" + /// Event name (everything in onclick) + /// Text + /// + public static string OnClickSpan(this HtmlHelper helper, string text, string onclickMethod, string cssClass, object htmlAttributes) + { + //style + cssClass = cssClass ?? "onclick"; + //property + string setHash = htmlAttributes.ToAttributeList(); + string attributeList = string.Empty; + if (setHash != null) + attributeList = setHash; + + StringBuilder html = new StringBuilder(); + html.AppendFormat("{2}", onclickMethod.Replace("\"","\\\'"), attributeList, text); + + return html.ToString(); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/PagerBarExtension.cs b/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/PagerBarExtension.cs index 42722e507..bd3e96999 100644 --- a/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/PagerBarExtension.cs +++ b/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/PagerBarExtension.cs @@ -1,341 +1,341 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.Rendering; -using Senparc.CO2NET; -using Senparc.CO2NET.Extensions; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Utility; -using System.Text; -using Microsoft.Extensions.DependencyInjection; - -namespace System.Web.Mvc -{ - public enum BarStyle - { - defaultStyle = 0,//默认 - digg, - yahoo, - meneame, - flickr, - sabrosus, - scott, - quotes, - black, - black2, - black_red, - grayr, - yellow, - jogger, - starcraft2, - tres, - megas512, - technorati, - youtube, - msdn, - badoo, - manu, - green_black, - viciao, - yahoo2, - weixinShopPager, - taskPager, - bootstrap - } - - public class PagerSocureData - { - public int PageIndex { get; set; } - public int PageCount { get; set; } - public int TotalCount { get; set; } - } - - public class PagerBarSettings - { - public BarStyle barStyle { get; set; } - - public string Text { get; set; } - - public int PageCount { get; set; } - - public int PageIndex { get; set; } - - public int TotalCount { get; set; } - - public int ShowPageNumber { get; set; } - - public string NoRecordTip { get; set; } - - public string PreWord { get; set; } - - public string NextWord { get; set; } - - public string Onclick { get; set; } - - public string Url { get; set; } - - /// - /// 项目中处在iframe中的页码条 - /// - public bool InIframe { get; set; } - - public string BarMark { get; set; } - - public string CurrentPageWordFormat { get; set; } - - public bool ShowTotalCount { get; set; } - - public PagerBarSettings() : this(null) { } - - public PagerBarSettings(string url) : this(null, 0, 0, url, false) { } - - public PagerBarSettings(string url, bool inIframe) : this(null, 0, 0, url, inIframe) { } - - - public PagerBarSettings(int? pageIndex, int pageCount, int totalCount, string url, bool inIframe) - { - this.PageIndex = pageIndex ?? 1; - this.PageCount = pageCount > 0 ? pageCount : 20; - this.TotalCount = totalCount; - this.Url = url; - this.InIframe = inIframe; - - this.ShowTotalCount = true; - } - } - - - public static class PagerBarExtension - { - public static string PagerBar(this IHtmlHelper html, PagedList dataSource) where T : class - { - return PagerBar(html, dataSource, null); - } - - public static string PagerBar(this IHtmlHelper html, PagedList dataSource, PagerBarSettings settings) where T : class - { - settings = settings ?? new PagerBarSettings(); - settings.PageIndex = dataSource.PageIndex; - settings.PageCount = dataSource.PageCount; - settings.TotalCount = dataSource.TotalCount; - return PagerBar(html, settings); - } - - - public static string PagerBar(this IHtmlHelper html, PagerSocureData dataSource, PagerBarSettings settings) where T : class - { - settings = settings ?? new PagerBarSettings(); - settings.PageIndex = dataSource.PageIndex; - settings.PageCount = dataSource.PageCount; - settings.TotalCount = dataSource.TotalCount; - return PagerBar(html, settings); - } - - - - public static string PagerBar(this IHtmlHelper html, PagerBarSettings settings) - { - SetDefaultValue(settings);//设置默认值 - - StringBuilder sb = new StringBuilder(); - sb.AppendFormat("
", settings.barStyle.ToString()); - - //判断空记录 - if (settings.TotalCount == 0) - { - sb.Append(settings.NoRecordTip); - sb.Append("
"); - - return sb.ToString(); - } - - int totalPage = Convert.ToInt32((settings.TotalCount - 1) / settings.PageCount) + 1;//总页数 - - string backPageStyle = (settings.PageIndex <= 1) ? "disabled" : ""; - string nextPageStyle = (settings.PageIndex >= totalPage) ? "disabled" : ""; - - var firstDisplayPageEnd = 0;//从第1页显示到xx页 - var bodyDisplayPageStart = 0;//当前页临近最左页码 - var bodyDisplayPageEnd = 0;//当前页临近最右页码 - var endDisplayPageStart = 0;//从第xx页显示到最后一页 - - - //设定 bodyDisplayPageStart - bodyDisplayPageStart = (settings.PageIndex - settings.ShowPageNumber <= 1) ? 1 : settings.PageIndex - settings.ShowPageNumber; // (ViewData.pageIndex - ViewData.showPageNumber <= ViewData.showPageNumber) ? ViewData.showPageNumber + 1 : ViewData.pageIndex - ViewData.showPageNumber; - - - //设定 bodyDisplayPageEnd - bodyDisplayPageEnd = (settings.PageIndex + settings.ShowPageNumber >= totalPage) ? totalPage : settings.PageIndex + settings.ShowPageNumber; - - - //设定 firstDisplayPageEnd - if (bodyDisplayPageStart > 1) - { - if (bodyDisplayPageStart - settings.ShowPageNumber <= 1) - { - firstDisplayPageEnd = bodyDisplayPageStart - 1; - } - else - { - firstDisplayPageEnd = settings.ShowPageNumber; - } - } - else - { - firstDisplayPageEnd = 0; - } - - //设定 endDisplayPageStart - if (bodyDisplayPageEnd < totalPage) - { - if (bodyDisplayPageEnd + settings.ShowPageNumber >= totalPage) - { - endDisplayPageStart = bodyDisplayPageEnd + 1; - } - else - { - endDisplayPageStart = totalPage - settings.ShowPageNumber + 1; - } - } - else - { - endDisplayPageStart = totalPage + 1; - } - //页面参数设定结束 - - - //开始输出 - - // 上一条 - if (settings.PageIndex <= 1) - { - sb.Append("").Append(settings.PreWord).Append(""); - } - else - { - sb.Append(GetPageLink(settings.PageIndex - 1, settings.PageIndex, settings.PreWord, settings.CurrentPageWordFormat, settings.Onclick, settings.Url, settings.BarMark, settings.InIframe)); - } - - - //first - for (var i = 1; i <= firstDisplayPageEnd; i++) - { - sb.Append(GetPageLink(i, settings.PageIndex, i.ToString(), settings.CurrentPageWordFormat, settings.Onclick, settings.Url, settings.BarMark, settings.InIframe)); - } - - //省略号 - if (firstDisplayPageEnd + 1 < bodyDisplayPageStart) - { - sb.Append("... "); - } - - //body - for (var i = bodyDisplayPageStart; i <= bodyDisplayPageEnd; i++) - { - sb.Append(GetPageLink(i, settings.PageIndex, i.ToString(), settings.CurrentPageWordFormat, settings.Onclick, settings.Url, settings.BarMark, settings.InIframe)); - } - - //省略号 - if (bodyDisplayPageEnd + 1 < endDisplayPageStart) - { - sb.Append("... "); - } - - //end - for (var i = endDisplayPageStart; i <= totalPage; i++) - { - sb.Append(GetPageLink(i, settings.PageIndex, i.ToString(), settings.CurrentPageWordFormat, settings.Onclick, settings.Url, settings.BarMark, settings.InIframe)); - } - - // 下一条 - if (settings.PageIndex >= totalPage) - { - sb.Append("").Append(settings.NextWord).Append(""); - } - else - { - sb.Append(GetPageLink(settings.PageIndex + 1, settings.PageIndex, settings.NextWord, settings.CurrentPageWordFormat, settings.Onclick, settings.Url, settings.BarMark, settings.InIframe)); - } - - //总数 - if (settings.ShowTotalCount) - { - int recordStart = (settings.PageIndex - 1) * settings.PageCount + 1; - int recordEnd = recordStart + settings.PageCount - 1; - if (recordEnd > settings.TotalCount) - { - recordEnd = settings.TotalCount;//如果超出最大值,则修正为最大值 - } - sb.Append("总共 " + settings.TotalCount.ToString() + ""); - } - - sb.Append(""); - - return sb.ToString(); - } - - private static void SetDefaultValue(PagerBarSettings settings) - { - if (settings.barStyle == BarStyle.defaultStyle) - { - settings.barStyle = BarStyle.quotes; - } - - if (settings.PageIndex == 0) - { - settings.PageIndex = 1; - } - - if (settings.ShowPageNumber == 0) - { - settings.ShowPageNumber = 3; - } - - if (string.IsNullOrEmpty(settings.PreWord)) - { - settings.PreWord = "上一页"; - } - - if (string.IsNullOrEmpty(settings.NextWord)) - { - settings.NextWord = "下一页"; - } - - if (settings.PageCount == 0) - { - settings.PageCount = 20; - } - - if (string.IsNullOrEmpty(settings.CurrentPageWordFormat)) - { - settings.CurrentPageWordFormat = "{0}"; - } - - } - - private static string GetPageLink(int linkPageIndex, int currentPageIndex, string text, string currentPageWordFormat, string onclick, string url, string barMark, bool inInframe) - { - //var pageData = "?page=";//string.Format("{0}page=", (Request.QueryString.Count == 0) ? "?" : "&") + "{0}";//页码参数 - - onclick = (onclick != null) ? "onclick=\"" + onclick + "\"" : ""; - onclick = onclick.Replace("{pageindex}", linkPageIndex.ToString()); - - var httpContext = SenparcDI.GetServiceProvider().GetService().HttpContext; - - url = (!string.IsNullOrEmpty(url)) ? url.UrlDecode().Replace(currentPageWordFormat, linkPageIndex.ToString()) : ""; - var href = (onclick != null && onclick.IndexOf("return false") != -1) ? "href=\"#" + barMark + "\" " : "href=\"" + url + "\" "; - - var linkHTML = ""; - - string formatedText = currentPageWordFormat.IndexOf("{0}") >= 0 ? string.Format(currentPageWordFormat, text) : text;//判断是{0}形式还是自定义替换字符串。 - - if (linkPageIndex == currentPageIndex) - { - linkHTML = $"{formatedText}"; - } - else - { - linkHTML = "{text}"; - } - return linkHTML; - } - } +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Rendering; +using Senparc.CO2NET; +using Senparc.CO2NET.Extensions; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Utility; +using System.Text; +using Microsoft.Extensions.DependencyInjection; + +namespace System.Web.Mvc +{ + public enum BarStyle + { + defaultStyle = 0,//default + digg, + yahoo, + meneame, + flickr, + sabrosus, + scott, + quotes, + black, + black2, + black_red, + grayr, + yellow, + jogger, + starcraft2, + tres, + megas512, + technorati, + youtube, + msdn, + badoo, + manu, + green_black, + viciao, + yahoo2, + weixinShopPager, + taskPager, + bootstrap + } + + public class PagerSocureData + { + public int PageIndex { get; set; } + public int PageCount { get; set; } + public int TotalCount { get; set; } + } + + public class PagerBarSettings + { + public BarStyle barStyle { get; set; } + + public string Text { get; set; } + + public int PageCount { get; set; } + + public int PageIndex { get; set; } + + public int TotalCount { get; set; } + + public int ShowPageNumber { get; set; } + + public string NoRecordTip { get; set; } + + public string PreWord { get; set; } + + public string NextWord { get; set; } + + public string Onclick { get; set; } + + public string Url { get; set; } + + /// + /// The page number bar in the iframe in the project + /// + public bool InIframe { get; set; } + + public string BarMark { get; set; } + + public string CurrentPageWordFormat { get; set; } + + public bool ShowTotalCount { get; set; } + + public PagerBarSettings() : this(null) { } + + public PagerBarSettings(string url) : this(null, 0, 0, url, false) { } + + public PagerBarSettings(string url, bool inIframe) : this(null, 0, 0, url, inIframe) { } + + + public PagerBarSettings(int? pageIndex, int pageCount, int totalCount, string url, bool inIframe) + { + this.PageIndex = pageIndex ?? 1; + this.PageCount = pageCount > 0 ? pageCount : 20; + this.TotalCount = totalCount; + this.Url = url; + this.InIframe = inIframe; + + this.ShowTotalCount = true; + } + } + + + public static class PagerBarExtension + { + public static string PagerBar(this IHtmlHelper html, PagedList dataSource) where T : class + { + return PagerBar(html, dataSource, null); + } + + public static string PagerBar(this IHtmlHelper html, PagedList dataSource, PagerBarSettings settings) where T : class + { + settings = settings ?? new PagerBarSettings(); + settings.PageIndex = dataSource.PageIndex; + settings.PageCount = dataSource.PageCount; + settings.TotalCount = dataSource.TotalCount; + return PagerBar(html, settings); + } + + + public static string PagerBar(this IHtmlHelper html, PagerSocureData dataSource, PagerBarSettings settings) where T : class + { + settings = settings ?? new PagerBarSettings(); + settings.PageIndex = dataSource.PageIndex; + settings.PageCount = dataSource.PageCount; + settings.TotalCount = dataSource.TotalCount; + return PagerBar(html, settings); + } + + + + public static string PagerBar(this IHtmlHelper html, PagerBarSettings settings) + { + SetDefaultValue(settings);//Set default value + + StringBuilder sb = new StringBuilder(); + sb.AppendFormat("
", settings.barStyle.ToString()); + + //Determine empty records + if (settings.TotalCount == 0) + { + sb.Append(settings.NoRecordTip); + sb.Append("
"); + + return sb.ToString(); + } + + int totalPage = Convert.ToInt32((settings.TotalCount - 1) / settings.PageCount) + 1;//Total pages + + string backPageStyle = (settings.PageIndex <= 1) ? "disabled" : ""; + string nextPageStyle = (settings.PageIndex >= totalPage) ? "disabled" : ""; + + var firstDisplayPageEnd = 0;//Display from page 1 to page xx + var bodyDisplayPageStart = 0;//The current page is adjacent to the leftmost page number + var bodyDisplayPageEnd = 0;//The current page is adjacent to the rightmost page number + var endDisplayPageStart = 0;//Display from page xx to the last page + + + //Set bodyDisplayPageStart + bodyDisplayPageStart = (settings.PageIndex - settings.ShowPageNumber <= 1) ? 1 : settings.PageIndex - settings.ShowPageNumber; // (ViewData.pageIndex - ViewData.showPageNumber <= ViewData.showPageNumber) ? ViewData.showPageNumber + 1 : ViewData.pageIndex - ViewData.showPageNumber; + + + //Set bodyDisplayPageEnd + bodyDisplayPageEnd = (settings.PageIndex + settings.ShowPageNumber >= totalPage) ? totalPage : settings.PageIndex + settings.ShowPageNumber; + + + //Set firstDisplayPageEnd + if (bodyDisplayPageStart > 1) + { + if (bodyDisplayPageStart - settings.ShowPageNumber <= 1) + { + firstDisplayPageEnd = bodyDisplayPageStart - 1; + } + else + { + firstDisplayPageEnd = settings.ShowPageNumber; + } + } + else + { + firstDisplayPageEnd = 0; + } + + //Set endDisplayPageStart + if (bodyDisplayPageEnd < totalPage) + { + if (bodyDisplayPageEnd + settings.ShowPageNumber >= totalPage) + { + endDisplayPageStart = bodyDisplayPageEnd + 1; + } + else + { + endDisplayPageStart = totalPage - settings.ShowPageNumber + 1; + } + } + else + { + endDisplayPageStart = totalPage + 1; + } + //Page parameter setting ends + + + //Start output + + // Previous article + if (settings.PageIndex <= 1) + { + sb.Append("").Append(settings.PreWord).Append(""); + } + else + { + sb.Append(GetPageLink(settings.PageIndex - 1, settings.PageIndex, settings.PreWord, settings.CurrentPageWordFormat, settings.Onclick, settings.Url, settings.BarMark, settings.InIframe)); + } + + + //first + for (var i = 1; i <= firstDisplayPageEnd; i++) + { + sb.Append(GetPageLink(i, settings.PageIndex, i.ToString(), settings.CurrentPageWordFormat, settings.Onclick, settings.Url, settings.BarMark, settings.InIframe)); + } + + //Ellipsis + if (firstDisplayPageEnd + 1 < bodyDisplayPageStart) + { + sb.Append("... "); + } + + //body + for (var i = bodyDisplayPageStart; i <= bodyDisplayPageEnd; i++) + { + sb.Append(GetPageLink(i, settings.PageIndex, i.ToString(), settings.CurrentPageWordFormat, settings.Onclick, settings.Url, settings.BarMark, settings.InIframe)); + } + + //Ellipsis + if (bodyDisplayPageEnd + 1 < endDisplayPageStart) + { + sb.Append("... "); + } + + //end + for (var i = endDisplayPageStart; i <= totalPage; i++) + { + sb.Append(GetPageLink(i, settings.PageIndex, i.ToString(), settings.CurrentPageWordFormat, settings.Onclick, settings.Url, settings.BarMark, settings.InIframe)); + } + + // Next article + if (settings.PageIndex >= totalPage) + { + sb.Append("").Append(settings.NextWord).Append(""); + } + else + { + sb.Append(GetPageLink(settings.PageIndex + 1, settings.PageIndex, settings.NextWord, settings.CurrentPageWordFormat, settings.Onclick, settings.Url, settings.BarMark, settings.InIframe)); + } + + //total + if (settings.ShowTotalCount) + { + int recordStart = (settings.PageIndex - 1) * settings.PageCount + 1; + int recordEnd = recordStart + settings.PageCount - 1; + if (recordEnd > settings.TotalCount) + { + recordEnd = settings.TotalCount;//If it exceeds the maximum value, correct it to the maximum value + } + sb.Append("总共 " + settings.TotalCount.ToString() + ""); + } + + sb.Append(""); + + return sb.ToString(); + } + + private static void SetDefaultValue(PagerBarSettings settings) + { + if (settings.barStyle == BarStyle.defaultStyle) + { + settings.barStyle = BarStyle.quotes; + } + + if (settings.PageIndex == 0) + { + settings.PageIndex = 1; + } + + if (settings.ShowPageNumber == 0) + { + settings.ShowPageNumber = 3; + } + + if (string.IsNullOrEmpty(settings.PreWord)) + { + settings.PreWord = "上一页"; + } + + if (string.IsNullOrEmpty(settings.NextWord)) + { + settings.NextWord = "下一页"; + } + + if (settings.PageCount == 0) + { + settings.PageCount = 20; + } + + if (string.IsNullOrEmpty(settings.CurrentPageWordFormat)) + { + settings.CurrentPageWordFormat = "{0}"; + } + + } + + private static string GetPageLink(int linkPageIndex, int currentPageIndex, string text, string currentPageWordFormat, string onclick, string url, string barMark, bool inInframe) + { + //var pageData = "?page=";//string.Format("{0}page=", (Request.QueryString.Count == 0) ? "?" : "&") + "{0}";//Page number parameter + + onclick = (onclick != null) ? "onclick=\"" + onclick + "\"" : ""; + onclick = onclick.Replace("{pageindex}", linkPageIndex.ToString()); + + var httpContext = SenparcDI.GetServiceProvider().GetService().HttpContext; + + url = (!string.IsNullOrEmpty(url)) ? url.UrlDecode().Replace(currentPageWordFormat, linkPageIndex.ToString()) : ""; + var href = (onclick != null && onclick.IndexOf("return false") != -1) ? "href=\"#" + barMark + "\" " : "href=\"" + url + "\" "; + + var linkHTML = ""; + + string formatedText = currentPageWordFormat.IndexOf("{0}") >= 0 ? string.Format(currentPageWordFormat, text) : text;//Determine whether it is in {0} form or a custom replacement string. + + if (linkPageIndex == currentPageIndex) + { + linkHTML = $"{formatedText}"; + } + else + { + linkHTML = "{text}"; + } + return linkHTML; + } + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/RepeaterExtension.cs b/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/RepeaterExtension.cs index 1b7cea001..0a0033d8b 100644 --- a/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/RepeaterExtension.cs +++ b/src/Basic/Senparc.Ncf.Mvc.UI/UIHelpers/RepeaterExtension.cs @@ -1,223 +1,223 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Text; - -namespace System.Web.Mvc -{ - public static class RepeaterExtension - { - public class SingleRepeater : IDisposable - { - protected HttpContext _context; - - //bool wroteStartTag = false, wroteEndTag = false; - - string startTag, endTag;//开始、结束标签 - public string itemStartTag, itemEndTag;//单项开始、结束标签 - public StringBuilder body = new StringBuilder(); - - public SingleRepeater(HttpContext context, RepeaterMode repeaterMode, object htmlAttributes) - { - this._context = context;//目前没有用到,如果直接在这里用context.Response.Write输出则需要。 - switch (repeaterMode) - { - case RepeaterMode.None: - startTag = string.Empty; - itemStartTag = string.Empty; - itemEndTag = string.Empty; - endTag = string.Empty; - break; - case RepeaterMode.Table: - startTag = ""; - itemStartTag = ""; - endTag = "
"; - itemEndTag = "
"; - break; - case RepeaterMode.Div: - startTag = "
"; - itemStartTag = "
"; - itemEndTag = "
"; - endTag = "
"; - break; - case RepeaterMode.Ul: - startTag = "
    "; - itemStartTag = "
  • "; - itemEndTag = "
  • "; - endTag = "
"; - break; - } - - //属性 - var setHash = htmlAttributes.ToAttributeList(); - - string attributeList = string.Empty; - if (setHash != null) - attributeList = setHash; - - startTag = string.Format(startTag, attributeList); - - WriteStartTag(); - } - - public void WriteStartTag() - { - body.Append(startTag); - } - - public void WriteEndTag() - { - body.Append(endTag); - } - - public override string ToString() - { - return body.ToString(); - } - - #region IDisposable 成员 - - public void Dispose() - { - WriteEndTag(); - } - - #endregion - } - - /// - /// Repeater - /// - /// - /// - /// 数据源 - /// 第一个单项循环开始之前的内容 - /// 单项内容 - /// 每次循环分隔内容(Table慎用) - /// 最后一个单项循环结束之后的内容 - /// repeater模式 - /// 每行烈数(只在repeaterMode为Table时有效) - /// 标签属性 - /// - public static string Repeater(this HtmlHelper helper, IEnumerable dataSource, - string header, Expression> itemTempletes, Expression> separatorTemplate, string footer, - RepeaterMode repeaterMode, int colCount, object htmlAttributes) - { - if (dataSource == null) - return ""; - - HttpContext context = helper.ViewContext.HttpContext; - - //Dictionary data = MvcControlDataBinder.SourceToDictionary(dataSource, "", ""); - SingleRepeater repeater = new SingleRepeater(context, repeaterMode, htmlAttributes); - using (repeater) - { - StringBuilder body = repeater.body; - Func itemResult = itemTempletes.Compile(); - Func separatorResult = separatorTemplate.Compile(); - - - //header - body.Append(header); - - int tableRow = 0; - - /** 单项循环开始 **/ - - int dataCount = dataSource.Count(); - int i = 0; - foreach (var item in dataSource) - { - - /* 仅适用Table */ - if (repeaterMode == RepeaterMode.Table) - { - if (tableRow % colCount == 0) - body.Append("");//添加行开始标记 - } - - body.Append(repeater.itemStartTag);//单项开头 - - switch (repeaterMode) - { - case RepeaterMode.None: - case RepeaterMode.Div: - case RepeaterMode.Ul: - case RepeaterMode.Table: - body.Append(itemResult(item, i).ToString());//主体内容 - break; - } - - body.Append(repeater.itemEndTag);//单项结尾 - - /* 仅适用Table */ - if (repeaterMode == RepeaterMode.Table) - { - tableRow++; - if (tableRow % colCount == 0) - body.Append("");//添加行结束标记 - } - - //添加分隔符 - if (i < dataCount - 1) - { - body.Append(separatorResult(item).ToString()); - } - - i++; - } - /** 单项循环结束 **/ - - - //为Table添加结束标记 - if (repeaterMode == RepeaterMode.Table && tableRow % colCount != 0) - { - do - { - body.Append(repeater.itemStartTag).Append(repeater.itemEndTag);//空白单元格 - tableRow++; - } while (tableRow % colCount != 0); - body.Append(""); - } - //context.Response.Write(body.ToString()); - - //footer - body.Append(footer); - - } - return repeater.ToString(); - } - - //public static string Repeater(this HtmlHelper helper, IEnumerable dataSource, - // string header, Expression> itemTempletes, Expression> separatorTemplate, string footer, - // RepeaterMode repeaterMode, int colCount, object htmlAttributes) - //{ - - //} - - public enum RepeaterMode - { - /// - /// 不自动加任何多于标记,等同于foreach。但header和footer仍然有效 - /// - None = 0, - Table, - Div, - Ul - } - - //public class MVCRepeater : Repeater - //{ - - // public Repeater Repeater(this HtmlHelper html,object datasource, string header,string footer,string bodyType) - // { - // //StringBuilder sb = new StringBuilder(); - - - // //return sb.ToString(); - // } - //} - } +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; + +namespace System.Web.Mvc +{ + public static class RepeaterExtension + { + public class SingleRepeater : IDisposable + { + protected HttpContext _context; + + //bool wroteStartTag = false, wroteEndTag = false; + + string startTag, endTag;//start and end tags + public string itemStartTag, itemEndTag;//Individual start and end tags + public StringBuilder body = new StringBuilder(); + + public SingleRepeater(HttpContext context, RepeaterMode repeaterMode, object htmlAttributes) + { + this._context = context;//It is not used currently. It is required if you use context.Response.Write to output directly here. + switch (repeaterMode) + { + case RepeaterMode.None: + startTag = string.Empty; + itemStartTag = string.Empty; + itemEndTag = string.Empty; + endTag = string.Empty; + break; + case RepeaterMode.Table: + startTag = ""; + itemStartTag = ""; + endTag = "
"; + itemEndTag = "
"; + break; + case RepeaterMode.Div: + startTag = "
"; + itemStartTag = "
"; + itemEndTag = "
"; + endTag = "
"; + break; + case RepeaterMode.Ul: + startTag = "
    "; + itemStartTag = "
  • "; + itemEndTag = "
  • "; + endTag = "
"; + break; + } + + //property + var setHash = htmlAttributes.ToAttributeList(); + + string attributeList = string.Empty; + if (setHash != null) + attributeList = setHash; + + startTag = string.Format(startTag, attributeList); + + WriteStartTag(); + } + + public void WriteStartTag() + { + body.Append(startTag); + } + + public void WriteEndTag() + { + body.Append(endTag); + } + + public override string ToString() + { + return body.ToString(); + } + + #region IDisposable 成员 + + public void Dispose() + { + WriteEndTag(); + } + + #endregion + } + + /// + /// Repeater + /// + /// + /// + /// Data source + /// Content before the start of the first single item loop + /// Single item content + /// Separate content in each loop (use Table with caution) + /// Content after the end of the last single item loop + /// repeater mode + /// Count of each row (only valid when repeaterMode is Table) + /// Tag attributes + /// + public static string Repeater(this HtmlHelper helper, IEnumerable dataSource, + string header, Expression> itemTempletes, Expression> separatorTemplate, string footer, + RepeaterMode repeaterMode, int colCount, object htmlAttributes) + { + if (dataSource == null) + return ""; + + HttpContext context = helper.ViewContext.HttpContext; + + //Dictionary data = MvcControlDataBinder.SourceToDictionary(dataSource, "", ""); + SingleRepeater repeater = new SingleRepeater(context, repeaterMode, htmlAttributes); + using (repeater) + { + StringBuilder body = repeater.body; + Func itemResult = itemTempletes.Compile(); + Func separatorResult = separatorTemplate.Compile(); + + + //header + body.Append(header); + + int tableRow = 0; + + /** Single cycle starts **/ + + int dataCount = dataSource.Count(); + int i = 0; + foreach (var item in dataSource) + { + + /* Applies only to Table */ + if (repeaterMode == RepeaterMode.Table) + { + if (tableRow % colCount == 0) + body.Append("");//Add line start tag + } + + body.Append(repeater.itemStartTag);//Beginning with a single item + + switch (repeaterMode) + { + case RepeaterMode.None: + case RepeaterMode.Div: + case RepeaterMode.Ul: + case RepeaterMode.Table: + body.Append(itemResult(item, i).ToString());//Main content + break; + } + + body.Append(repeater.itemEndTag);//single ending + + /* Applies only to Table */ + if (repeaterMode == RepeaterMode.Table) + { + tableRow++; + if (tableRow % colCount == 0) + body.Append("");//Add end of line tag + } + + //Add separator + if (i < dataCount - 1) + { + body.Append(separatorResult(item).ToString()); + } + + i++; + } + /** End of single cycle **/ + + + //Add closing tag to Table + if (repeaterMode == RepeaterMode.Table && tableRow % colCount != 0) + { + do + { + body.Append(repeater.itemStartTag).Append(repeater.itemEndTag);//blank cell + tableRow++; + } while (tableRow % colCount != 0); + body.Append(""); + } + //context.Response.Write(body.ToString()); + + //footer + body.Append(footer); + + } + return repeater.ToString(); + } + + //public static string Repeater(this HtmlHelper helper, IEnumerable dataSource, + // string header, Expression> itemTempletes, Expression> separatorTemplate, string footer, + // RepeaterMode repeaterMode, int colCount, object htmlAttributes) + //{ + + //} + + public enum RepeaterMode + { + /// + /// Does not automatically add any more than tags, equivalent to foreach. But header and footer are still valid + /// + None = 0, + Table, + Div, + Ul + } + + //public class MVCRepeater : Repeater + //{ + + // public Repeater Repeater(this HtmlHelper html,object datasource, string header,string footer,string bodyType) + // { + // //StringBuilder sb = new StringBuilder(); + + + // //return sb.ToString(); + // } + //} + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Repository/BaseRepoisitory/IRepositoryBase.cs b/src/Basic/Senparc.Ncf.Repository/BaseRepoisitory/IRepositoryBase.cs index dcb8250e7..b3ab2b587 100644 --- a/src/Basic/Senparc.Ncf.Repository/BaseRepoisitory/IRepositoryBase.cs +++ b/src/Basic/Senparc.Ncf.Repository/BaseRepoisitory/IRepositoryBase.cs @@ -1,180 +1,180 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Query; -using Microsoft.EntityFrameworkCore.Storage; -using Senparc.Ncf.Core.DI; -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.Core.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Repository -{ - public interface IRepositoryBase : IDataBase, IAutoDI where T : class, IEntityBase// global::System.Data.Objects.DataClasses.EntityObject, new() - { - bool IsInsert(T obj); - - IQueryable GeAll(Expression> orderBy, OrderingType orderingType, params string[] includes); - IQueryable GeAll(Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - - /// - /// 获取分页列表 - /// - /// 搜索条件 - /// - /// 当pageCount小于等于0时不分页 - /// - PagedList GetObjectList(Expression> where, Expression> orderBy, OrderingType orderingType, int pageIndex, int pageCount, params string[] includes); - - PagedList GetObjectList(Expression> where, Expression> orderBy, OrderingType orderingType, int pageIndex, int pageCount, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - - - Task> GetObjectListAsync(Expression> where, - Expression> orderBy, OrderingType orderingType, int pageIndex, int pageCount, - params string[] includes); - - Task> GetObjectListAsync(Expression> where, - Expression> orderBy, OrderingType orderingType, int pageIndex, int pageCount, - Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - - - //Task GetFirstOrDefaultObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, Expression> includesNavigationPropertyPathFunc); - - T GetFirstOrDefaultObject(Expression> where, params string[] includes); - T GetFirstOrDefaultObject(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - - T GetFirstOrDefaultObject(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes); - T GetFirstOrDefaultObject(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - - - int ObjectCount(Expression> where, params string[] includes); - int ObjectCount(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - - decimal GetSum(Expression> where, Expression> sum, params string[] includes); - decimal GetSum(Expression> where, Expression> sum, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - - void Add(T obj); - - void Update(T obj); - - /// - /// 此方法会自动判断应当执行更新(Update)还是添加(Add) - /// - /// - void Save(T obj); - - void Delete(T obj, bool softDelete = false); - - void SaveChanges(); - - /// - /// - /// - /// - /// - Task SaveAsync(T obj); - - /// - /// - /// - /// - /// - /// - Task DeleteAsync(T obj, bool softDelete = false); - - Task SaveChangesAsync(); - - /// - /// - /// - /// - /// - /// - Task GetFirstOrDefaultObjectAsync(Expression> where, params string[] includes); - Task GetFirstOrDefaultObjectAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - - Task GetFirstOrDefaultObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes); - Task GetFirstOrDefaultObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - - Task ObjectCountAsync(Expression> where, params string[] includes); - Task ObjectCountAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - - Task GetSumAsync(Expression> where, Expression> sum, params string[] includes); - Task GetSumAsync(Expression> where, Expression> sum, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - - /// - /// 批量删除 - /// - /// - /// 删除每一个对象的操作 - /// - /// - Task DeleteAllAsync(IEnumerable objs, Action deleteItemAction = null, bool softDelete = false); - - /// - /// 批量添加 - /// - /// - /// - /// - Task AddAllAsync(IEnumerable objs); - - /// - /// 动态字段排序 - /// - /// - /// xxx desc, bbb asc - /// - /// - /// - /// - Task> GetObjectListAsync(Expression> where, string OrderbyField, int pageIndex, int pageCount, params string[] includes); - Task> GetObjectListAsync(Expression> where, string OrderbyField, int pageIndex, int pageCount, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - - /// - /// - /// - /// - /// - Task SaveObjectListAsync(IEnumerable objs); - - /// - /// 开启事务 - /// - /// - Task BeginTransactionAsync(); - - /// - /// 开启事务 - /// - /// - /// - Task BeginTransactionAsync(System.Data.IsolationLevel isolationLevel); - - /// - /// 开启事务 - /// - /// - void BeginTransaction(); - - /// - /// - /// - /// - /// - void BeginTransaction(System.Data.IsolationLevel isolationLevel); - - /// - /// 回滚事务 - /// - /// - void RollbackTransaction(); - - /// - /// 提交事务 - /// - void CommitTransaction(); - } +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.EntityFrameworkCore.Storage; +using Senparc.Ncf.Core.DI; +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.Core.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Repository +{ + public interface IRepositoryBase : IDataBase, IAutoDI where T : class, IEntityBase// global::System.Data.Objects.DataClasses.EntityObject, new() + { + bool IsInsert(T obj); + + IQueryable GeAll(Expression> orderBy, OrderingType orderingType, params string[] includes); + IQueryable GeAll(Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + + /// + /// Get paginated list + /// + /// Search conditions + /// + /// No paging when pageCount is less than or equal to 0 + /// + PagedList GetObjectList(Expression> where, Expression> orderBy, OrderingType orderingType, int pageIndex, int pageCount, params string[] includes); + + PagedList GetObjectList(Expression> where, Expression> orderBy, OrderingType orderingType, int pageIndex, int pageCount, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + + + Task> GetObjectListAsync(Expression> where, + Expression> orderBy, OrderingType orderingType, int pageIndex, int pageCount, + params string[] includes); + + Task> GetObjectListAsync(Expression> where, + Expression> orderBy, OrderingType orderingType, int pageIndex, int pageCount, + Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + + + //Task GetFirstOrDefaultObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, Expression> includesNavigationPropertyPathFunc); + + T GetFirstOrDefaultObject(Expression> where, params string[] includes); + T GetFirstOrDefaultObject(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + + T GetFirstOrDefaultObject(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes); + T GetFirstOrDefaultObject(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + + + int ObjectCount(Expression> where, params string[] includes); + int ObjectCount(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + + decimal GetSum(Expression> where, Expression> sum, params string[] includes); + decimal GetSum(Expression> where, Expression> sum, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + + void Add(T obj); + + void Update(T obj); + + /// + /// This method will automatically determine whether to perform Update (Update) or Add (Add) + /// + /// + void Save(T obj); + + void Delete(T obj, bool softDelete = false); + + void SaveChanges(); + + /// + /// + /// + /// + /// + Task SaveAsync(T obj); + + /// + /// + /// + /// + /// + /// + Task DeleteAsync(T obj, bool softDelete = false); + + Task SaveChangesAsync(); + + /// + /// + /// + /// + /// + /// + Task GetFirstOrDefaultObjectAsync(Expression> where, params string[] includes); + Task GetFirstOrDefaultObjectAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + + Task GetFirstOrDefaultObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes); + Task GetFirstOrDefaultObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + + Task ObjectCountAsync(Expression> where, params string[] includes); + Task ObjectCountAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + + Task GetSumAsync(Expression> where, Expression> sum, params string[] includes); + Task GetSumAsync(Expression> where, Expression> sum, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + + /// + /// Batch delete + /// + /// + /// The operation of deleting each object + /// + /// + Task DeleteAllAsync(IEnumerable objs, Action deleteItemAction = null, bool softDelete = false); + + /// + /// Batch add + /// + /// + /// + /// + Task AddAllAsync(IEnumerable objs); + + /// + ///Dynamic field sorting + /// + /// + /// xxx desc, bbb asc + /// + /// + /// + /// + Task> GetObjectListAsync(Expression> where, string OrderbyField, int pageIndex, int pageCount, params string[] includes); + Task> GetObjectListAsync(Expression> where, string OrderbyField, int pageIndex, int pageCount, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + + /// + /// + /// + /// + /// + Task SaveObjectListAsync(IEnumerable objs); + + /// + /// start transaction + /// + /// + Task BeginTransactionAsync(); + + /// + /// start transaction + /// + /// + /// + Task BeginTransactionAsync(System.Data.IsolationLevel isolationLevel); + + /// + /// start transaction + /// + /// + void BeginTransaction(); + + /// + /// + /// + /// + /// + void BeginTransaction(System.Data.IsolationLevel isolationLevel); + + /// + /// rollback transaction + /// + /// + void RollbackTransaction(); + + /// + /// Commit transaction + /// + void CommitTransaction(); + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Repository/BaseRepoisitory/RepositoryBase.cs b/src/Basic/Senparc.Ncf.Repository/BaseRepoisitory/RepositoryBase.cs index 52e4a1853..bb50a9eb9 100644 --- a/src/Basic/Senparc.Ncf.Repository/BaseRepoisitory/RepositoryBase.cs +++ b/src/Basic/Senparc.Ncf.Repository/BaseRepoisitory/RepositoryBase.cs @@ -1,763 +1,763 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Query; -using Microsoft.EntityFrameworkCore.Storage; -using Senparc.CO2NET; -using Senparc.CO2NET.Extensions; -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.Core.Extensions; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Utility.ExpressionExtension; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Repository -{ - public class RepositoryBase : DataBase, IRepositoryBase where T : EntityBase //global::System.Data.Objects.DataClasses.EntityObject, new() - { - protected string _entitySetName; - - //public RepositoryBase() : - // this(null) - //{ - //} - - public RepositoryBase(INcfDbData db) : base(db) - { - //System.Web.HttpContext.Current.Response.Write("-"+this.GetType().Name + "
"); - //DB = db ?? ObjectFactory.GetInstance();//如果没有定义,取默认数据库 - - base.BaseDB = db; - // ObjectFactory.GetInstance(); - - EntitySetKeysDictionary keys = EntitySetKeys.GetAllEntitySetInfo(); - _entitySetName = keys[typeof(T)].SetName; - } - - //public BaseRepository() { } - - - #region IBaseRepository 成员 - - - public virtual bool IsInsert(T obj) - { - Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry entry = BaseDB.BaseDataContext.Entry(obj); - return entry.State == EntityState.Added || entry.State == EntityState.Detached; //TODO:EF5、Core 验证正确性 - //return obj.EntityKey == null || obj.EntityKey.EntityKeyValues == null; - - //entry.IsKeySet - } - - public virtual IQueryable GeAll(Expression> orderBy, OrderingType orderingType, params string[] includes) - { - //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - return BaseDB.BaseDataContext.Set() - //.SqlQuery(sql) - //.CreateQuery(sql) - .Includes(includes) - .OrderBy(orderBy, orderingType).AsQueryable(); - } - - public virtual IQueryable GeAll(Expression> orderBy, OrderingType orderingType, - /*[JetBrains.Annotations.NotNull]*/Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - //Expression> navigationPropertyPath - - //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - return includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) - .OrderBy(orderBy, orderingType).AsQueryable(); - } - - - public virtual PagedList GetObjectList(Expression> where, Expression> orderBy, OrderingType orderingType, int pageIndex, int pageCount, params string[] includes) - { - //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - int skipCount = Senparc.Ncf.Core.Utility.Extensions.GetSkipRecord(pageIndex, pageCount); - int totalCount = -1; - List result = null; - //var query = BaseDB.BaseDataContext.CreateQuery(sql).Includes(includes).OrderBy(orderBy, orderingType);//.Includes(includes); - IQueryable resultList = BaseDB.BaseDataContext - .Set() - //.CreateQuery(sql) - .Includes(includes) - .Where(where) - .OrderBy(orderBy, orderingType);//.Includes(includes); - - if (pageCount > 0 && pageIndex > 0) - { - resultList = resultList.Skip(skipCount).Take(pageCount); - totalCount = this.ObjectCount(where, null); //whereList.Count(); - } - //else - //{ - // resultList = query.; - //} - - //try - { - result = resultList.ToList(); - } - //catch (ArgumentException ex)//DbArithmeticExpression 参数必须具有数值通用类型。 - //{ - // //通常是ordery by的问题 TODO:重新整理是否需要Skip等操作 - // //result = query.Includes(includes) - // // .OrderByIEnumerable(orderBy.Compile(), orderingType)//改用非延时地方法,效率最低 - // // .Skip(skipCount).Take(pageCount) - // // .Where(where.Compile())//保险起见用where.Compile(),但是会影响效率 - // // .ToList(); - // AdminLogUtility.WebLogger.Warn("EF ArgumentException", ex); - // throw; - //} - //catch (NotSupportedException ex)//System.Reflection.TargetException - //{ - // result = resultList.Where(where.Compile()).ToList(); - // AdminLogUtility.WebLogger.Warn("EF NotSupportedException", ex); - // throw; - //} - //catch (Exception ex) - //{ - // result = resultList.Where(where.Compile()).ToList(); - // AdminLogUtility.WebLogger.Warn("EF Exception", ex); - // throw; - //} - - PagedList list = new PagedList(result, pageIndex, pageCount, totalCount, skipCount); - return list; - } - public virtual PagedList GetObjectList(Expression> where, Expression> orderBy, OrderingType orderingType, int pageIndex, int pageCount, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - int skipCount = Senparc.Ncf.Core.Utility.Extensions.GetSkipRecord(pageIndex, pageCount); - int totalCount = -1; - List result = null; - //var query = BaseDB.BaseDataContext.CreateQuery(sql).Includes(includes).OrderBy(orderBy, orderingType);//.Includes(includes); - IQueryable resultList = includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) - .OrderBy(orderBy, orderingType) - .Where(where) - .OrderBy(orderBy, orderingType);//.Includes(includes); - - if (pageCount > 0 && pageIndex > 0) - { - resultList = resultList.Skip(skipCount).Take(pageCount); - totalCount = this.ObjectCount(where, null); //whereList.Count(); - } - //else - //{ - // resultList = query.; - //} - - //try - { - result = resultList.ToList(); - } - //catch (ArgumentException ex)//DbArithmeticExpression 参数必须具有数值通用类型。 - //{ - // //通常是ordery by的问题 TODO:重新整理是否需要Skip等操作 - // //result = query.Includes(includes) - // // .OrderByIEnumerable(orderBy.Compile(), orderingType)//改用非延时地方法,效率最低 - // // .Skip(skipCount).Take(pageCount) - // // .Where(where.Compile())//保险起见用where.Compile(),但是会影响效率 - // // .ToList(); - // AdminLogUtility.WebLogger.Warn("EF ArgumentException", ex); - // throw; - //} - //catch (NotSupportedException ex)//System.Reflection.TargetException - //{ - // result = resultList.Where(where.Compile()).ToList(); - // AdminLogUtility.WebLogger.Warn("EF NotSupportedException", ex); - // throw; - //} - //catch (Exception ex) - //{ - // result = resultList.Where(where.Compile()).ToList(); - // AdminLogUtility.WebLogger.Warn("EF Exception", ex); - // throw; - //} - - PagedList list = new PagedList(result, pageIndex, pageCount, totalCount, skipCount); - return list; - } - - public virtual async Task> GetObjectListAsync(Expression> where, Expression> orderBy, OrderingType orderingType, int pageIndex, int pageCount, params string[] includes) - { - int skipCount = Senparc.Ncf.Core.Utility.Extensions.GetSkipRecord(pageIndex, pageCount); - int totalCount = -1; - List result = null; - IQueryable resultList = BaseDB.BaseDataContext - .Set() - .Includes(includes) - .Where(where) - .OrderBy(orderBy, orderingType);//.Includes(includes); - if (pageCount > 0 && pageIndex > 0) - { - resultList = resultList.Skip(skipCount).Take(pageCount); - totalCount = this.ObjectCount(where, null); - } - - result = await resultList.ToListAsync(); - - PagedList list = new PagedList(result, pageIndex, pageCount, totalCount, skipCount); - return list; - } - - public virtual async Task> GetObjectListAsync(Expression> where, Expression> orderBy, OrderingType orderingType, int pageIndex, int pageCount, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - int skipCount = Senparc.Ncf.Core.Utility.Extensions.GetSkipRecord(pageIndex, pageCount); - int totalCount = -1; - List result = null; - IQueryable resultList = includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) - .OrderBy(orderBy, orderingType) - .Where(where) - .OrderBy(orderBy, orderingType);//.Includes(includes); - if (pageCount > 0 && pageIndex > 0) - { - resultList = resultList.Skip(skipCount).Take(pageCount); - totalCount = this.ObjectCount(where, null); - } - - result = await resultList.ToListAsync(); - - PagedList list = new PagedList(result, pageIndex, pageCount, totalCount, skipCount); - return list; - } - - - public virtual T GetFirstOrDefaultObject(Expression> where, params string[] includes) - { - //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - return BaseDB.BaseDataContext - .Set() - //.CreateQuery(sql) - .Includes(includes).FirstOrDefault(where); - } - - public virtual T GetFirstOrDefaultObject(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - return includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) - .FirstOrDefault(where); - } - - public virtual T GetFirstOrDefaultObject(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes) - { - //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - return BaseDB.BaseDataContext - .Set() - //.CreateQuery(sql) - .Includes(includes).Where(where).OrderBy(orderBy, orderingType).FirstOrDefault(); - } - - public virtual T GetFirstOrDefaultObject(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - Console.WriteLine("RepositoryBase GetFirstOrDefaultObject"); - Console.WriteLine("Data:" + BaseDB.BaseDataContext.Set().ToList().ToJson(true, new Newtonsoft.Json.JsonSerializerSettings() { ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore })); - - //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - return includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) - .OrderBy(orderBy, orderingType) - .Where(where).OrderBy(orderBy, orderingType).FirstOrDefault(); - } - - public virtual T GetObjectById(int id) - { - T obj = BaseDB.BaseDataContext - .Set() - .Find(id); - - return obj; - } - - public virtual T GetObjectById(long id) - { - //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - //T obj = BaseDB.BaseDataContext - // .Set() - // //.CreateQuery(sql) - // .Includes(includes).Where("it.Id = @id", new ObjectParameter("id", id)).FirstOrDefault(); - T obj = BaseDB.BaseDataContext.Set().Find(id); - return obj; - } - - //public virtual T GetObjectByGuid(Guid guid,params string[] includes) - //{ - // string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - // T obj = BaseDB.BaseDataContext - // .Set() - // //.CreateQuery(sql) - // .Includes(includes).Where("it.Guid = @guid", new ObjectParameter("guid", guid)).FirstOrDefault(); - // return obj; - //} - - public virtual int ObjectCount(Expression> where, params string[] includes) - { - string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - int count = 0; - IQueryable query = BaseDB.BaseDataContext - .Set() - //.CreateQuery(sql) - .Includes(includes); - //try - { - count = query.Count(where); - } - //catch (NotSupportedException ex) - //{ - // count = query.Count(where.Compile()); - //} - //catch (Exception ex) - //{ - // count = query.Count(where.Compile()); - // throw; - //} - return count; - } - - public virtual int ObjectCount(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - int count = 0; - IQueryable query = includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()); - //try - { - count = query.Count(where); - } - //catch (NotSupportedException ex) - //{ - // count = query.Count(where.Compile()); - //} - //catch (Exception ex) - //{ - // count = query.Count(where.Compile()); - // throw; - //} - return count; - } - - public virtual decimal GetSum(Expression> where, Expression> sum, params string[] includes) - { - //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - decimal result = BaseDB.BaseDataContext - .Set() - //.CreateQuery(sql) - .Includes(includes).Where(where).Sum(sum); - return result; - } - - public virtual decimal GetSum(Expression> where, Expression> sum, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - decimal result = includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) - .Where(where).Sum(sum); - return result; - } - //public virtual object ObjectSum(Expression> where, Func sumBy, string[] includes) - //{ - // string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - // object result= _db.DataContext.CreateQuery(sql).Includes(includes).Where(where).Sum(sumBy); - // return result; - //} - - - public virtual void Add(T obj) - { - BaseDB.BaseDataContext.Set().Add(obj); - this.SaveChanges(); - } - - public virtual void Update(T obj) - { - //_db.DataContext.ApplyPropertyChanges(_entitySetName, obj); - this.SaveChanges(); - } - - public virtual void Save(T obj) - { - if (this.IsInsert(obj)) - { - obj.AddTime = obj.LastUpdateTime = SystemTime.Now.LocalDateTime; - this.Add(obj); - } - else - { - obj.LastUpdateTime = SystemTime.Now.LocalDateTime; - this.Update(obj); - } - } - - public virtual void SaveChanges() - { - BaseDB.BaseDataContext.SaveChanges();//TODO: SaveOptions. - } - - /// - /// 删除对象 - /// - /// - /// 是否使用软删除 - public virtual void Delete(T obj, bool softDelete = false) - { - if (softDelete) - { - obj.Flag = true;//软删除 - } - else - { - BaseDB.BaseDataContext.Set().Remove(obj);//硬删除 - } - this.SaveChanges(); - } - - //public virtual void DeleteAll(IEnumerable objs) - //{ - // //foreach (var obj in objs) - // //{ - // // _db.DataContext.DeleteObject(obj); - // //} - // //this.SaveChanges(); - // var list = objs.ToList(); - // for (int i = 0; i < list.Count; i++) - // { - // this.Delete(list[i]); - // } - //} - - #endregion - - public virtual async Task SaveChangesAsync() - { - await BaseDB.BaseDataContext.SaveChangesAsync().ConfigureAwait(false);//TODO: SaveOptions. - } - - public virtual async Task SaveAsync(T obj) - { - if (this.IsInsert(obj)) - { - obj.AddTime = obj.LastUpdateTime = SystemTime.Now.LocalDateTime; - await this.AddAsync(obj).ConfigureAwait(false); - } - else - { - obj.LastUpdateTime = SystemTime.Now.LocalDateTime; - await this.UpdateAsync(obj).ConfigureAwait(false); - } - } - - /// - /// - /// - /// - /// - /// - public async Task GetFirstOrDefaultObjectAsync(Expression> where, params string[] includes) - { - return await BaseDB.BaseDataContext - .Set() - //.CreateQuery(sql) - .Includes(includes).FirstOrDefaultAsync(where); - } - - public async Task GetFirstOrDefaultObjectAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - return await includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) - .FirstOrDefaultAsync(where); - } - - public virtual async Task GetFirstOrDefaultObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes) - { - //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - return await BaseDB.BaseDataContext - .Set() - //.CreateQuery(sql) - .Includes(includes).Where(where).OrderBy(orderBy, orderingType).FirstOrDefaultAsync(); - } - - public virtual async Task GetFirstOrDefaultObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - return await includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) - .OrderBy(orderBy, orderingType) - .Where(where).OrderBy(orderBy, orderingType).FirstOrDefaultAsync(); - } - - //public virtual async Task GetFirstOrDefaultObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, Expression> includesNavigationPropertyPathFunc) - //{ - // return await BaseDB.BaseDataContext - // .Set() - // .Include(includesNavigationPropertyPathFunc).Where(where).OrderBy(orderBy, orderingType).FirstOrDefaultAsync(); - //} - - - - public virtual async Task ObjectCountAsync(Expression> where, params string[] includes) - { - string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - int count = 0; - IQueryable query = BaseDB.BaseDataContext - .Set() - //.CreateQuery(sql) - .Includes(includes); - //try - { - count = await query.CountAsync(where).ConfigureAwait(false); - } - //catch (NotSupportedException ex) - //{ - // count = query.Count(where.Compile()); - //} - //catch (Exception ex) - //{ - // count = query.Count(where.Compile()); - // throw; - //} - return count; - } - - public virtual async Task ObjectCountAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - int count = 0; - IQueryable query = includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()); - //try - { - count = await query.CountAsync(where).ConfigureAwait(false); - } - //catch (NotSupportedException ex) - //{ - // count = query.Count(where.Compile()); - //} - //catch (Exception ex) - //{ - // count = query.Count(where.Compile()); - // throw; - //} - return count; - } - - public virtual async Task GetSumAsync(Expression> where, Expression> sum, params string[] includes) - { - //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - var query = BaseDB.BaseDataContext - .Set() - //.CreateQuery(sql) - .Includes(includes); - - decimal result = await query.Where(where).SumAsync(sum); - return result; - } - - public virtual async Task GetSumAsync(Expression> where, Expression> sum, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); - var query = includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()); - - decimal result = await query.Where(where).SumAsync(sum); - return result; - } - - - public virtual async Task AddAsync(T obj) - { - BaseDB.BaseDataContext.Set().Add(obj); - await this.SaveChangesAsync(); - } - - public virtual async Task UpdateAsync(T obj) - { - //_db.DataContext.ApplyPropertyChanges(_entitySetName, obj); - await this.SaveChangesAsync(); - } - - - /// - /// 删除对象 - /// - /// - /// 是否使用软删除 - public virtual async Task DeleteAsync(T obj, bool softDelete = false) - { - if (softDelete) - { - obj.Flag = true;//软删除 - } - else - { - BaseDB.BaseDataContext.Set().Remove(obj);//硬删除 - } - await this.SaveChangesAsync(); - } - - - /// - /// 批量删除 - /// - /// - /// 删除每一个对象的操作 - /// - /// - public virtual async Task DeleteAllAsync(IEnumerable objs, Action deleteItemAction = null, bool softDelete = false) - { - foreach (var obj in objs) - { - if (softDelete) - { - obj.Flag = true;//软删除 - } - else - { - BaseDB.BaseDataContext.Set().Remove(obj);//硬删除 - } - - deleteItemAction?.Invoke(obj); - } - await this.SaveChangesAsync(); - } - - - /// - /// 批量添加, 待优化 - /// - /// - /// - /// - public virtual async Task AddAllAsync(IEnumerable objs) - { - //foreach (var obj in objs) - //{ - // //BaseDB.BaseDataContext.Set().(obj);//硬删除 - //} - BaseDB.BaseDataContext.Set().AddRange(objs); - await this.SaveChangesAsync(); - } - - - /// - /// 动态排序 - /// - /// - /// xxx DESC, bbb ASC - /// - /// - /// - /// - public virtual async Task> GetObjectListAsync(Expression> where, string OrderbyField, int pageIndex, int pageCount, params string[] includes) - { - int skipCount = Senparc.Ncf.Core.Utility.Extensions.GetSkipRecord(pageIndex, pageCount); - int totalCount = -1; - List result = null; - IQueryable resultList = BaseDB.BaseDataContext - .Set() - .Includes(includes) - .Where(where); - resultList = resultList.OrderByExtension(OrderbyField); - if (pageCount > 0 && pageIndex > 0) - { - resultList = resultList.Skip(skipCount).Take(pageCount); - totalCount = this.ObjectCount(where, null); - } - - result = await resultList.ToListAsync(); - - PagedList list = new PagedList(result, pageIndex, pageCount, totalCount, skipCount); - return list; - } - - public virtual async Task> GetObjectListAsync(Expression> where, string OrderbyField, int pageIndex, int pageCount, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - int skipCount = Senparc.Ncf.Core.Utility.Extensions.GetSkipRecord(pageIndex, pageCount); - int totalCount = -1; - List result = null; - IQueryable resultList = includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) - .Where(where); - resultList = resultList.OrderByExtension(OrderbyField); - if (pageCount > 0 && pageIndex > 0) - { - resultList = resultList.Skip(skipCount).Take(pageCount); - totalCount = this.ObjectCount(where, null); - } - - result = await resultList.ToListAsync(); - - PagedList list = new PagedList(result, pageIndex, pageCount, totalCount, skipCount); - return list; - } - - public async Task SaveObjectListAsync(IEnumerable objs) - { - if (!objs.Any()) - { - return; - } - ICollection addList = new List(); - foreach (var item in objs) - { - if (this.IsInsert(item)) - { - addList.Add(item); - } - else - { - BaseDB.BaseDataContext.Entry(item).State = EntityState.Modified; - } - } - - BaseDB.BaseDataContext.Set().AddRange(addList); - await this.SaveChangesAsync(); - } - - /// - /// 开启事物 - /// - /// - public void BeginTransaction() - { - BaseDB.BaseDataContext.Database.BeginTransaction(); - } - - /// - /// 开启事物 - /// - /// - public void BeginTransaction(System.Data.IsolationLevel isolationLevel) - { - BaseDB.BaseDataContext.Database.BeginTransaction(); - } - - /// - /// 开启事物 - /// - /// - public async Task BeginTransactionAsync() - { - await BaseDB.BaseDataContext.Database.BeginTransactionAsync(); - } - - /// - /// 开启事物 - /// - /// - public async Task BeginTransactionAsync(System.Data.IsolationLevel isolationLevel) - { - await BaseDB.BaseDataContext.Database.BeginTransactionAsync(isolationLevel); - } - - /// - /// 回滚事物 - /// - /// - public void RollbackTransaction() - { - BaseDB.BaseDataContext.Database.RollbackTransaction(); - } - - /// - /// 提交事务 - /// - public void CommitTransaction() - { - BaseDB.BaseDataContext.Database.CommitTransaction(); - } - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.EntityFrameworkCore.Storage; +using Senparc.CO2NET; +using Senparc.CO2NET.Extensions; +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.Core.Extensions; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Utility.ExpressionExtension; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Repository +{ + public class RepositoryBase : DataBase, IRepositoryBase where T : EntityBase //global::System.Data.Objects.DataClasses.EntityObject, new() + { + protected string _entitySetName; + + //public RepositoryBase() : + // this(null) + //{ + //} + + public RepositoryBase(INcfDbData db) : base(db) + { + //System.Web.HttpContext.Current.Response.Write("-"+this.GetType().Name + "
"); + //DB = db ?? ObjectFactory.GetInstance();//If not defined, take the default database + + base.BaseDB = db; + // ObjectFactory.GetInstance(); + + EntitySetKeysDictionary keys = EntitySetKeys.GetAllEntitySetInfo(); + _entitySetName = keys[typeof(T)].SetName; + } + + //public BaseRepository() { } + + + #region IBaseRepository 成员 + + + public virtual bool IsInsert(T obj) + { + Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry entry = BaseDB.BaseDataContext.Entry(obj); + return entry.State == EntityState.Added || entry.State == EntityState.Detached; //TODO: EF5, Core verification correctness + //return obj.EntityKey == null || obj.EntityKey.EntityKeyValues == null; + + //entry.IsKeySet + } + + public virtual IQueryable GeAll(Expression> orderBy, OrderingType orderingType, params string[] includes) + { + //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + return BaseDB.BaseDataContext.Set() + //.SqlQuery(sql) + //.CreateQuery(sql) + .Includes(includes) + .OrderBy(orderBy, orderingType).AsQueryable(); + } + + public virtual IQueryable GeAll(Expression> orderBy, OrderingType orderingType, + /*[JetBrains.Annotations.NotNull]*/Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + //Expression> navigationPropertyPath + + //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + return includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) + .OrderBy(orderBy, orderingType).AsQueryable(); + } + + + public virtual PagedList GetObjectList(Expression> where, Expression> orderBy, OrderingType orderingType, int pageIndex, int pageCount, params string[] includes) + { + //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + int skipCount = Senparc.Ncf.Core.Utility.Extensions.GetSkipRecord(pageIndex, pageCount); + int totalCount = -1; + List result = null; + //var query = BaseDB.BaseDataContext.CreateQuery(sql).Includes(includes).OrderBy(orderBy, orderingType);//.Includes(includes); + IQueryable resultList = BaseDB.BaseDataContext + .Set() + //.CreateQuery(sql) + .Includes(includes) + .Where(where) + .OrderBy(orderBy, orderingType);//.Includes(includes); + + if (pageCount > 0 && pageIndex > 0) + { + resultList = resultList.Skip(skipCount).Take(pageCount); + totalCount = this.ObjectCount(where, null); //whereList.Count(); + } + //else + //{ + // resultList = query.; + //} + + //try + { + result = resultList.ToList(); + } + //catch (ArgumentException ex)//DbArithmeticExpression parameter must have a numeric generic type. + //{ + // //Usually a problem with order by TODO: Does reorganizing require operations such as Skip? + // //result = query.Includes(includes) + // // .OrderByIEnumerable(orderBy.Compile(), orderingType)//Switch to non-delayed method, the lowest efficiency + // // .Skip(skipCount).Take(pageCount) + // // .Where(where.Compile())//Use where.Compile() to be on the safe side, but it will affect efficiency + // // .ToList(); + // AdminLogUtility.WebLogger.Warn("EF ArgumentException", ex); + // throw; + //} + //catch (NotSupportedException ex)//System.Reflection.TargetException + //{ + // result = resultList.Where(where.Compile()).ToList(); + // AdminLogUtility.WebLogger.Warn("EF NotSupportedException", ex); + // throw; + //} + //catch (Exception ex) + //{ + // result = resultList.Where(where.Compile()).ToList(); + // AdminLogUtility.WebLogger.Warn("EF Exception", ex); + // throw; + //} + + PagedList list = new PagedList(result, pageIndex, pageCount, totalCount, skipCount); + return list; + } + public virtual PagedList GetObjectList(Expression> where, Expression> orderBy, OrderingType orderingType, int pageIndex, int pageCount, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + int skipCount = Senparc.Ncf.Core.Utility.Extensions.GetSkipRecord(pageIndex, pageCount); + int totalCount = -1; + List result = null; + //var query = BaseDB.BaseDataContext.CreateQuery(sql).Includes(includes).OrderBy(orderBy, orderingType);//.Includes(includes); + IQueryable resultList = includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) + .OrderBy(orderBy, orderingType) + .Where(where) + .OrderBy(orderBy, orderingType);//.Includes(includes); + + if (pageCount > 0 && pageIndex > 0) + { + resultList = resultList.Skip(skipCount).Take(pageCount); + totalCount = this.ObjectCount(where, null); //whereList.Count(); + } + //else + //{ + // resultList = query.; + //} + + //try + { + result = resultList.ToList(); + } + //catch (ArgumentException ex)//DbArithmeticExpression parameter must have a numeric generic type. + //{ + // //Usually a problem with order by TODO: Does reorganizing require operations such as Skip? + // //result = query.Includes(includes) + // // .OrderByIEnumerable(orderBy.Compile(), orderingType)//Switch to non-delayed method, the lowest efficiency + // // .Skip(skipCount).Take(pageCount) + // // .Where(where.Compile())//Use where.Compile() to be on the safe side, but it will affect efficiency + // // .ToList(); + // AdminLogUtility.WebLogger.Warn("EF ArgumentException", ex); + // throw; + //} + //catch (NotSupportedException ex)//System.Reflection.TargetException + //{ + // result = resultList.Where(where.Compile()).ToList(); + // AdminLogUtility.WebLogger.Warn("EF NotSupportedException", ex); + // throw; + //} + //catch (Exception ex) + //{ + // result = resultList.Where(where.Compile()).ToList(); + // AdminLogUtility.WebLogger.Warn("EF Exception", ex); + // throw; + //} + + PagedList list = new PagedList(result, pageIndex, pageCount, totalCount, skipCount); + return list; + } + + public virtual async Task> GetObjectListAsync(Expression> where, Expression> orderBy, OrderingType orderingType, int pageIndex, int pageCount, params string[] includes) + { + int skipCount = Senparc.Ncf.Core.Utility.Extensions.GetSkipRecord(pageIndex, pageCount); + int totalCount = -1; + List result = null; + IQueryable resultList = BaseDB.BaseDataContext + .Set() + .Includes(includes) + .Where(where) + .OrderBy(orderBy, orderingType);//.Includes(includes); + if (pageCount > 0 && pageIndex > 0) + { + resultList = resultList.Skip(skipCount).Take(pageCount); + totalCount = this.ObjectCount(where, null); + } + + result = await resultList.ToListAsync(); + + PagedList list = new PagedList(result, pageIndex, pageCount, totalCount, skipCount); + return list; + } + + public virtual async Task> GetObjectListAsync(Expression> where, Expression> orderBy, OrderingType orderingType, int pageIndex, int pageCount, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + int skipCount = Senparc.Ncf.Core.Utility.Extensions.GetSkipRecord(pageIndex, pageCount); + int totalCount = -1; + List result = null; + IQueryable resultList = includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) + .OrderBy(orderBy, orderingType) + .Where(where) + .OrderBy(orderBy, orderingType);//.Includes(includes); + if (pageCount > 0 && pageIndex > 0) + { + resultList = resultList.Skip(skipCount).Take(pageCount); + totalCount = this.ObjectCount(where, null); + } + + result = await resultList.ToListAsync(); + + PagedList list = new PagedList(result, pageIndex, pageCount, totalCount, skipCount); + return list; + } + + + public virtual T GetFirstOrDefaultObject(Expression> where, params string[] includes) + { + //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + return BaseDB.BaseDataContext + .Set() + //.CreateQuery(sql) + .Includes(includes).FirstOrDefault(where); + } + + public virtual T GetFirstOrDefaultObject(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + return includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) + .FirstOrDefault(where); + } + + public virtual T GetFirstOrDefaultObject(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes) + { + //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + return BaseDB.BaseDataContext + .Set() + //.CreateQuery(sql) + .Includes(includes).Where(where).OrderBy(orderBy, orderingType).FirstOrDefault(); + } + + public virtual T GetFirstOrDefaultObject(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + Console.WriteLine("RepositoryBase GetFirstOrDefaultObject"); + Console.WriteLine("Data:" + BaseDB.BaseDataContext.Set().ToList().ToJson(true, new Newtonsoft.Json.JsonSerializerSettings() { ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore })); + + //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + return includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) + .OrderBy(orderBy, orderingType) + .Where(where).OrderBy(orderBy, orderingType).FirstOrDefault(); + } + + public virtual T GetObjectById(int id) + { + T obj = BaseDB.BaseDataContext + .Set() + .Find(id); + + return obj; + } + + public virtual T GetObjectById(long id) + { + //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + //T obj = BaseDB.BaseDataContext + // .Set() + // //.CreateQuery(sql) + // .Includes(includes).Where("it.Id = @id", new ObjectParameter("id", id)).FirstOrDefault(); + T obj = BaseDB.BaseDataContext.Set().Find(id); + return obj; + } + + //public virtual T GetObjectByGuid(Guid guid,params string[] includes) + //{ + // string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + // T obj = BaseDB.BaseDataContext + // .Set() + // //.CreateQuery(sql) + // .Includes(includes).Where("it.Guid = @guid", new ObjectParameter("guid", guid)).FirstOrDefault(); + // return obj; + //} + + public virtual int ObjectCount(Expression> where, params string[] includes) + { + string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + int count = 0; + IQueryable query = BaseDB.BaseDataContext + .Set() + //.CreateQuery(sql) + .Includes(includes); + //try + { + count = query.Count(where); + } + //catch (NotSupportedException ex) + //{ + // count = query.Count(where.Compile()); + //} + //catch (Exception ex) + //{ + // count = query.Count(where.Compile()); + // throw; + //} + return count; + } + + public virtual int ObjectCount(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + int count = 0; + IQueryable query = includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()); + //try + { + count = query.Count(where); + } + //catch (NotSupportedException ex) + //{ + // count = query.Count(where.Compile()); + //} + //catch (Exception ex) + //{ + // count = query.Count(where.Compile()); + // throw; + //} + return count; + } + + public virtual decimal GetSum(Expression> where, Expression> sum, params string[] includes) + { + //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + decimal result = BaseDB.BaseDataContext + .Set() + //.CreateQuery(sql) + .Includes(includes).Where(where).Sum(sum); + return result; + } + + public virtual decimal GetSum(Expression> where, Expression> sum, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + decimal result = includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) + .Where(where).Sum(sum); + return result; + } + //public virtual object ObjectSum(Expression> where, Func sumBy, string[] includes) + //{ + // string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + // object result= _db.DataContext.CreateQuery(sql).Includes(includes).Where(where).Sum(sumBy); + // return result; + //} + + + public virtual void Add(T obj) + { + BaseDB.BaseDataContext.Set().Add(obj); + this.SaveChanges(); + } + + public virtual void Update(T obj) + { + //_db.DataContext.ApplyPropertyChanges(_entitySetName, obj); + this.SaveChanges(); + } + + public virtual void Save(T obj) + { + if (this.IsInsert(obj)) + { + obj.AddTime = obj.LastUpdateTime = SystemTime.Now.LocalDateTime; + this.Add(obj); + } + else + { + obj.LastUpdateTime = SystemTime.Now.LocalDateTime; + this.Update(obj); + } + } + + public virtual void SaveChanges() + { + BaseDB.BaseDataContext.SaveChanges();//TODO: SaveOptions. + } + + /// + /// delete object + /// + /// + /// Whether to use soft delete + public virtual void Delete(T obj, bool softDelete = false) + { + if (softDelete) + { + obj.Flag = true;//soft delete + } + else + { + BaseDB.BaseDataContext.Set().Remove(obj);//hard delete + } + this.SaveChanges(); + } + + //public virtual void DeleteAll(IEnumerable objs) + //{ + // //foreach (var obj in objs) + // //{ + // // _db.DataContext.DeleteObject(obj); + // //} + // //this.SaveChanges(); + // var list = objs.ToList(); + // for (int i = 0; i < list.Count; i++) + // { + // this.Delete(list[i]); + // } + //} + + #endregion + + public virtual async Task SaveChangesAsync() + { + await BaseDB.BaseDataContext.SaveChangesAsync().ConfigureAwait(false);//TODO: SaveOptions. + } + + public virtual async Task SaveAsync(T obj) + { + if (this.IsInsert(obj)) + { + obj.AddTime = obj.LastUpdateTime = SystemTime.Now.LocalDateTime; + await this.AddAsync(obj).ConfigureAwait(false); + } + else + { + obj.LastUpdateTime = SystemTime.Now.LocalDateTime; + await this.UpdateAsync(obj).ConfigureAwait(false); + } + } + + /// + /// + /// + /// + /// + /// + public async Task GetFirstOrDefaultObjectAsync(Expression> where, params string[] includes) + { + return await BaseDB.BaseDataContext + .Set() + //.CreateQuery(sql) + .Includes(includes).FirstOrDefaultAsync(where); + } + + public async Task GetFirstOrDefaultObjectAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + return await includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) + .FirstOrDefaultAsync(where); + } + + public virtual async Task GetFirstOrDefaultObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes) + { + //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + return await BaseDB.BaseDataContext + .Set() + //.CreateQuery(sql) + .Includes(includes).Where(where).OrderBy(orderBy, orderingType).FirstOrDefaultAsync(); + } + + public virtual async Task GetFirstOrDefaultObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + return await includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) + .OrderBy(orderBy, orderingType) + .Where(where).OrderBy(orderBy, orderingType).FirstOrDefaultAsync(); + } + + //public virtual async Task GetFirstOrDefaultObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, Expression> includesNavigationPropertyPathFunc) + //{ + // return await BaseDB.BaseDataContext + // .Set() + // .Include(includesNavigationPropertyPathFunc).Where(where).OrderBy(orderBy, orderingType).FirstOrDefaultAsync(); + //} + + + + public virtual async Task ObjectCountAsync(Expression> where, params string[] includes) + { + string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + int count = 0; + IQueryable query = BaseDB.BaseDataContext + .Set() + //.CreateQuery(sql) + .Includes(includes); + //try + { + count = await query.CountAsync(where).ConfigureAwait(false); + } + //catch (NotSupportedException ex) + //{ + // count = query.Count(where.Compile()); + //} + //catch (Exception ex) + //{ + // count = query.Count(where.Compile()); + // throw; + //} + return count; + } + + public virtual async Task ObjectCountAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + int count = 0; + IQueryable query = includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()); + //try + { + count = await query.CountAsync(where).ConfigureAwait(false); + } + //catch (NotSupportedException ex) + //{ + // count = query.Count(where.Compile()); + //} + //catch (Exception ex) + //{ + // count = query.Count(where.Compile()); + // throw; + //} + return count; + } + + public virtual async Task GetSumAsync(Expression> where, Expression> sum, params string[] includes) + { + //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + var query = BaseDB.BaseDataContext + .Set() + //.CreateQuery(sql) + .Includes(includes); + + decimal result = await query.Where(where).SumAsync(sum); + return result; + } + + public virtual async Task GetSumAsync(Expression> where, Expression> sum, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + //string sql = string.Format("SELECT VALUE c FROM {0} AS c ", _entitySetName); + var query = includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()); + + decimal result = await query.Where(where).SumAsync(sum); + return result; + } + + + public virtual async Task AddAsync(T obj) + { + BaseDB.BaseDataContext.Set().Add(obj); + await this.SaveChangesAsync(); + } + + public virtual async Task UpdateAsync(T obj) + { + //_db.DataContext.ApplyPropertyChanges(_entitySetName, obj); + await this.SaveChangesAsync(); + } + + + /// + /// delete object + /// + /// + /// Whether to use soft delete + public virtual async Task DeleteAsync(T obj, bool softDelete = false) + { + if (softDelete) + { + obj.Flag = true;//soft delete + } + else + { + BaseDB.BaseDataContext.Set().Remove(obj);//hard delete + } + await this.SaveChangesAsync(); + } + + + /// + /// Batch delete + /// + /// + /// The operation of deleting each object + /// + /// + public virtual async Task DeleteAllAsync(IEnumerable objs, Action deleteItemAction = null, bool softDelete = false) + { + foreach (var obj in objs) + { + if (softDelete) + { + obj.Flag = true;//soft delete + } + else + { + BaseDB.BaseDataContext.Set().Remove(obj);//hard delete + } + + deleteItemAction?.Invoke(obj); + } + await this.SaveChangesAsync(); + } + + + /// + /// Batch addition, to be optimized + /// + /// + /// + /// + public virtual async Task AddAllAsync(IEnumerable objs) + { + //foreach (var obj in objs) + //{ + // //BaseDB.BaseDataContext.Set().(obj);//Hard deletion + //} + BaseDB.BaseDataContext.Set().AddRange(objs); + await this.SaveChangesAsync(); + } + + + /// + /// dynamic sorting + /// + /// + /// xxx DESC, bbb ASC + /// + /// + /// + /// + public virtual async Task> GetObjectListAsync(Expression> where, string OrderbyField, int pageIndex, int pageCount, params string[] includes) + { + int skipCount = Senparc.Ncf.Core.Utility.Extensions.GetSkipRecord(pageIndex, pageCount); + int totalCount = -1; + List result = null; + IQueryable resultList = BaseDB.BaseDataContext + .Set() + .Includes(includes) + .Where(where); + resultList = resultList.OrderByExtension(OrderbyField); + if (pageCount > 0 && pageIndex > 0) + { + resultList = resultList.Skip(skipCount).Take(pageCount); + totalCount = this.ObjectCount(where, null); + } + + result = await resultList.ToListAsync(); + + PagedList list = new PagedList(result, pageIndex, pageCount, totalCount, skipCount); + return list; + } + + public virtual async Task> GetObjectListAsync(Expression> where, string OrderbyField, int pageIndex, int pageCount, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + int skipCount = Senparc.Ncf.Core.Utility.Extensions.GetSkipRecord(pageIndex, pageCount); + int totalCount = -1; + List result = null; + IQueryable resultList = includesNavigationPropertyPathFunc.Compile()(BaseDB.BaseDataContext.Set()) + .Where(where); + resultList = resultList.OrderByExtension(OrderbyField); + if (pageCount > 0 && pageIndex > 0) + { + resultList = resultList.Skip(skipCount).Take(pageCount); + totalCount = this.ObjectCount(where, null); + } + + result = await resultList.ToListAsync(); + + PagedList list = new PagedList(result, pageIndex, pageCount, totalCount, skipCount); + return list; + } + + public async Task SaveObjectListAsync(IEnumerable objs) + { + if (!objs.Any()) + { + return; + } + ICollection addList = new List(); + foreach (var item in objs) + { + if (this.IsInsert(item)) + { + addList.Add(item); + } + else + { + BaseDB.BaseDataContext.Entry(item).State = EntityState.Modified; + } + } + + BaseDB.BaseDataContext.Set().AddRange(addList); + await this.SaveChangesAsync(); + } + + /// + ///turn on things + /// + /// + public void BeginTransaction() + { + BaseDB.BaseDataContext.Database.BeginTransaction(); + } + + /// + ///turn on things + /// + /// + public void BeginTransaction(System.Data.IsolationLevel isolationLevel) + { + BaseDB.BaseDataContext.Database.BeginTransaction(); + } + + /// + ///turn on things + /// + /// + public async Task BeginTransactionAsync() + { + await BaseDB.BaseDataContext.Database.BeginTransactionAsync(); + } + + /// + ///turn on things + /// + /// + public async Task BeginTransactionAsync(System.Data.IsolationLevel isolationLevel) + { + await BaseDB.BaseDataContext.Database.BeginTransactionAsync(isolationLevel); + } + + /// + /// rollback transaction + /// + /// + public void RollbackTransaction() + { + BaseDB.BaseDataContext.Database.RollbackTransaction(); + } + + /// + /// Commit transaction + /// + public void CommitTransaction() + { + BaseDB.BaseDataContext.Database.CommitTransaction(); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Repository/System/SysButtonRespository.cs b/src/Basic/Senparc.Ncf.Repository/System/SysButtonRespository.cs index fed8d5f89..db752e88f 100644 --- a/src/Basic/Senparc.Ncf.Repository/System/SysButtonRespository.cs +++ b/src/Basic/Senparc.Ncf.Repository/System/SysButtonRespository.cs @@ -1,47 +1,47 @@ -using Microsoft.EntityFrameworkCore; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Core.Models.DataBaseModel; -using Senparc.Ncf.Repository; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Respository -{ - public interface ISysButtonRespository : IClientRepositoryBase - { - /// - /// 删除某个页面下的所有按钮 - /// - /// - /// - Task DeleteButtonsByMenuId(string menuId); - } - - public class SysButtonRespository : ClientRepositoryBase, ISysButtonRespository - { - private readonly SenparcEntitiesBase _senparcEntities; - - public SysButtonRespository(INcfDbData db) : base(db) - { - _senparcEntities = db.BaseDataContext as SenparcEntitiesBase; - } - - /// - /// 删除某个页面下的所有按钮 - /// - /// - /// - public async Task DeleteButtonsByMenuId(string menuId) - { - //升级至 EF Core 5.0 后方法无效 - //return await _senparcEntities.Database.ExecuteSqlCommandAsync($"DELETE {nameof(_senparcEntities.SysButtons)} WHERE {nameof(SysButton.MenuId)} = {{0}}", menuId); - - //return await _senparcEntities.Database.ExecuteSqlRawAsync($"DELETE {nameof(_senparcEntitie.SysButtons)} WHERE {nameof(SysButton.MenuId)} = {{0}}", menuId); - return await _senparcEntities.Database.ExecuteSqlRawAsync($"DELETE {"SysButtons"} WHERE {nameof(SysButton.MenuId)} = {{0}}", menuId); - } - } -} +using Microsoft.EntityFrameworkCore; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Core.Models.DataBaseModel; +using Senparc.Ncf.Repository; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Respository +{ + public interface ISysButtonRespository : IClientRepositoryBase + { + /// + /// Delete all buttons under a page + /// + /// + /// + Task DeleteButtonsByMenuId(string menuId); + } + + public class SysButtonRespository : ClientRepositoryBase, ISysButtonRespository + { + private readonly SenparcEntitiesBase _senparcEntities; + + public SysButtonRespository(INcfDbData db) : base(db) + { + _senparcEntities = db.BaseDataContext as SenparcEntitiesBase; + } + + /// + /// Delete all buttons under a page + /// + /// + /// + public async Task DeleteButtonsByMenuId(string menuId) + { + //Method not working after upgrading to EF Core 5.0 + //return await _senparcEntities.Database.ExecuteSqlCommandAsync($"DELETE {nameof(_senparcEntities.SysButtons)} WHERE {nameof(SysButton.MenuId)} = {{0}}", menuId); + + //return await _senparcEntities.Database.ExecuteSqlRawAsync($"DELETE {nameof(_senparcEntitie.SysButtons)} WHERE {nameof(SysButton.MenuId)} = {{0}}", menuId); + return await _senparcEntities.Database.ExecuteSqlRawAsync($"DELETE {"SysButtons"} WHERE {nameof(SysButton.MenuId)} = {{0}}", menuId); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Repository/System/SysRolePermissionRepository.cs b/src/Basic/Senparc.Ncf.Repository/System/SysRolePermissionRepository.cs index 8b61259e6..34632522c 100644 --- a/src/Basic/Senparc.Ncf.Repository/System/SysRolePermissionRepository.cs +++ b/src/Basic/Senparc.Ncf.Repository/System/SysRolePermissionRepository.cs @@ -1,44 +1,44 @@ -using Microsoft.EntityFrameworkCore; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Core.Models.DataBaseModel; -using Senparc.Ncf.Repository; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Respository -{ - public interface ISysRolePermissionRepository: IClientRepositoryBase - { - /// - /// 获取某个用户下面的所有资源 - /// - /// - /// - Task> GetAllResouceCodesByAccountIdAsync(int adminUserInfoId); - } - - public class SysRolePermissionRepository : ClientRepositoryBase, ISysRolePermissionRepository - { - public SysRolePermissionRepository(INcfDbData db) : base(db) - { - } - - /// - /// 获取某个用户下面的所有资源 - /// - /// - /// - public async Task> GetAllResouceCodesByAccountIdAsync(int adminUserInfoId) - { - IQueryable roleIdQuery = BaseDB.BaseDataContext.Set().Where(sra => sra.AccountId == adminUserInfoId).Select(_ => _.RoleId); // 查询角色 - var menuIdQuery = BaseDB.BaseDataContext.Set().Where(srp => roleIdQuery.Contains(srp.RoleId)).Select(srp => srp.PermissionId); // 查询所拥有的菜单 - var resourceCodes = await BaseDB.BaseDataContext.Set().Where(menu => menuIdQuery.Contains(menu.Id)) - .Select(menu => menu.ResourceCode) - .ToListAsync(); - return resourceCodes.Where(code => !string.IsNullOrEmpty(code)).ToList(); - } - } -} +using Microsoft.EntityFrameworkCore; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Core.Models.DataBaseModel; +using Senparc.Ncf.Repository; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Respository +{ + public interface ISysRolePermissionRepository: IClientRepositoryBase + { + /// + /// Get all resources under a user + /// + /// + /// + Task> GetAllResouceCodesByAccountIdAsync(int adminUserInfoId); + } + + public class SysRolePermissionRepository : ClientRepositoryBase, ISysRolePermissionRepository + { + public SysRolePermissionRepository(INcfDbData db) : base(db) + { + } + + /// + /// Get all resources under a user + /// + /// + /// + public async Task> GetAllResouceCodesByAccountIdAsync(int adminUserInfoId) + { + IQueryable roleIdQuery = BaseDB.BaseDataContext.Set().Where(sra => sra.AccountId == adminUserInfoId).Select(_ => _.RoleId); // Query roles + var menuIdQuery = BaseDB.BaseDataContext.Set().Where(srp => roleIdQuery.Contains(srp.RoleId)).Select(srp => srp.PermissionId); // Query the menu you own + var resourceCodes = await BaseDB.BaseDataContext.Set().Where(menu => menuIdQuery.Contains(menu.Id)) + .Select(menu => menu.ResourceCode) + .ToListAsync(); + return resourceCodes.Where(code => !string.IsNullOrEmpty(code)).ToList(); + } + } +} diff --git a/src/Basic/Senparc.Ncf.SMS/Enums.cs b/src/Basic/Senparc.Ncf.SMS/Enums.cs index 5c9ee8f5e..9e797ec9c 100644 --- a/src/Basic/Senparc.Ncf.SMS/Enums.cs +++ b/src/Basic/Senparc.Ncf.SMS/Enums.cs @@ -1,43 +1,43 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.SMS -{ - - /// - /// 短信平台类型 - /// - public enum SmsPlatformType - { - Unknow = -1, - JunMei = 0, - Fissoft = 1, - SMS1 = 10, - SMS2 = 20, - SMS3 = 30, - SMS4 = 40 - } - - - /// - /// 短信发送状态 - /// - public enum SmsResult - { - 未知错误 = 0, - 成功 = 1, - 访问数据库写入数据错误 = -1, - 一次发送的手机号码过多 = -3, - 内容包含不合法文字 = -4, - 登录账户错误 = -5, - 手机号码不合法黑名单 = -9, - 号码太长不能超过100条一次提交 = -10, - 内容太长 = -11, - 余额不足 = -13, - 子号号不正确 = -14, - 参数不全 = -999, - 超过当天请求限制 = -998, - 请在60秒后重试 = -997 - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.SMS +{ + + /// + /// SMS platform type + /// + public enum SmsPlatformType + { + Unknow = -1, + JunMei = 0, + Fissoft = 1, + SMS1 = 10, + SMS2 = 20, + SMS3 = 30, + SMS4 = 40 + } + + + /// + /// SMS sending status + /// + public enum SmsResult + { + 未知错误 = 0, + 成功 = 1, + 访问数据库写入数据错误 = -1, + 一次发送的手机号码过多 = -3, + 内容包含不合法文字 = -4, + 登录账户错误 = -5, + 手机号码不合法黑名单 = -9, + 号码太长不能超过100条一次提交 = -10, + 内容太长 = -11, + 余额不足 = -13, + 子号号不正确 = -14, + 参数不全 = -999, + 超过当天请求限制 = -998, + 请在60秒后重试 = -997 + } +} diff --git a/src/Basic/Senparc.Ncf.SMS/SmsPlatform/SmsPlatform.cs b/src/Basic/Senparc.Ncf.SMS/SmsPlatform/SmsPlatform.cs index 56567b3da..96fcd0869 100644 --- a/src/Basic/Senparc.Ncf.SMS/SmsPlatform/SmsPlatform.cs +++ b/src/Basic/Senparc.Ncf.SMS/SmsPlatform/SmsPlatform.cs @@ -1,57 +1,57 @@ -using System; -using System.Text; - -namespace Senparc.Ncf.SMS -{ - public interface ISmsPlatform - { - string SmsServiceAddress { get; } - SenparcSmsSetting Setting { get; set; } - - /// - /// 发送 - /// - /// - /// - /// - SmsResult Send(string content, string number); - /// - /// 获取剩余短信数量 - /// - /// - string GetLastCount(); - } - - public abstract class SmsPlatform : ISmsPlatform - { - public virtual string SmsServiceAddress { get; set; } - public SenparcSmsSetting Setting { get; set; } - public abstract SmsResult Send(string content, string number); - public abstract string GetLastCount(); - - public SmsPlatform(SmsPlatformType smsPlatformType, string smsServiceAddress, string smsAccountCORPID, string smsAccountName, string smsAccountPassword, string smsAccountSubNumber) - { - Setting = new SenparcSmsSetting() - { - SmsPlatformType = smsPlatformType, - SmsAccountCORPID = smsAccountCORPID, - SmsAccountName = smsAccountName, - SmsAccountPassword = smsAccountPassword, - SmsAccountSubNumber = smsAccountSubNumber, - }; - SmsServiceAddress = smsServiceAddress; - } - - - protected string UrlEncode(string url, Encoding encoding) - { - StringBuilder sb = new StringBuilder(); - byte[] bytes = encoding.GetBytes(url); - for (int i = 0; i < bytes.Length; i++) - { - sb.Append(@"%" + Convert.ToString(bytes[i], 16)); - } - return (sb.ToString()); - } - } -} +using System; +using System.Text; + +namespace Senparc.Ncf.SMS +{ + public interface ISmsPlatform + { + string SmsServiceAddress { get; } + SenparcSmsSetting Setting { get; set; } + + /// + /// send + /// + /// + /// + /// + SmsResult Send(string content, string number); + /// + /// Get the remaining number of text messages + /// + /// + string GetLastCount(); + } + + public abstract class SmsPlatform : ISmsPlatform + { + public virtual string SmsServiceAddress { get; set; } + public SenparcSmsSetting Setting { get; set; } + public abstract SmsResult Send(string content, string number); + public abstract string GetLastCount(); + + public SmsPlatform(SmsPlatformType smsPlatformType, string smsServiceAddress, string smsAccountCORPID, string smsAccountName, string smsAccountPassword, string smsAccountSubNumber) + { + Setting = new SenparcSmsSetting() + { + SmsPlatformType = smsPlatformType, + SmsAccountCORPID = smsAccountCORPID, + SmsAccountName = smsAccountName, + SmsAccountPassword = smsAccountPassword, + SmsAccountSubNumber = smsAccountSubNumber, + }; + SmsServiceAddress = smsServiceAddress; + } + + + protected string UrlEncode(string url, Encoding encoding) + { + StringBuilder sb = new StringBuilder(); + byte[] bytes = encoding.GetBytes(url); + for (int i = 0; i < bytes.Length; i++) + { + sb.Append(@"%" + Convert.ToString(bytes[i], 16)); + } + return (sb.ToString()); + } + } +} diff --git a/src/Basic/Senparc.Ncf.SMS/SmsPlatform/SmsPlatform_Fissoft.cs b/src/Basic/Senparc.Ncf.SMS/SmsPlatform/SmsPlatform_Fissoft.cs index da61df4b2..48673ed2c 100644 --- a/src/Basic/Senparc.Ncf.SMS/SmsPlatform/SmsPlatform_Fissoft.cs +++ b/src/Basic/Senparc.Ncf.SMS/SmsPlatform/SmsPlatform_Fissoft.cs @@ -1,134 +1,134 @@ -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.Core.Extensions; -using Senparc.Ncf.SMS; -using System; -using System.Collections.Generic; -using System.Net; -using System.Text; -using System.Threading; - -namespace Senparc.Ncf.SMS -{ - public class SmsPlatform_Fissoft : SmsPlatform, ISmsPlatform - { - public SmsPlatform_Fissoft(string smsServiceAddress, string smsAccountCORPID, string smsAccountName, string smsAccountPassword, string smsAccountSubNumber) - : base(SmsPlatformType.Fissoft, smsServiceAddress, smsAccountCORPID, smsAccountName, smsAccountPassword, smsAccountSubNumber) - { - - } - - public override string SmsServiceAddress => "http://api.fissoft.com/pubservice"; - - public override SmsResult Send(string content, string number) - { - SmsResult smsResult = SmsResult.未知错误; - try - { - - Encoding encoding = Encoding.UTF8; - var wc = new WebClient(); - wc.Encoding = encoding; - int contentIndex = 0; - int limitedLetterCount = 65 * 4; - - //组装消息,判断内容长度,太长则分开发 - List messageList = new List(); - while (contentIndex < content.Length) - { - string limitedContent = content.Substring(contentIndex, Math.Min(limitedLetterCount, content.Length - contentIndex)); - messageList.Add(limitedContent); - contentIndex += limitedLetterCount; - } - - //分批发送 - for (int i = 0; i < messageList.Count; i++) - { - var msg = messageList[i]; - if (messageList.Count > 1) - { - msg = $" [{i + 1}/{messageList.Count}]" + msg; - } - //msg += "【盛派网络】"; - string encodedLimitedConent = UrlEncode(msg, encoding); - - - - var url = string.Format("{0}/SMSSend?OperId={1}&OperPass={2}&Mobiles={3}&Message={4}", - SmsServiceAddress, Setting.SmsAccountCORPID, Setting.SmsAccountPassword, number, encodedLimitedConent); - var smsState = wc.DownloadString(url); - - var fissosoft_SendResult = Newtonsoft.Json.JsonConvert.DeserializeObject(smsState); - if (fissosoft_SendResult.Code == 1) - { - smsResult = SmsResult.成功; - } - else - { - if (!Enum.TryParse(fissosoft_SendResult.Code.ToString(), out smsResult)) - { - smsResult = SmsResult.未知错误; - } - } - Log.LogUtility.SmsLogger.Info($"发送短信成功:{content},号码:{number}。状态:{smsResult.ToString()}。发送通道:Fissoft"); - Thread.Sleep(200); - } - } - catch - { - Log.LogUtility.SmsLogger.Error($"发送短信失败:{content},号码:{number}。发送通道:Fissoft"); - } - finally - { - } - return smsResult; - } - - public override string GetLastCount() - { - var url = string.Format("{0}/SMSAccountInfo?OperId={1}&OperPass={2}", - SmsServiceAddress, Setting.SmsAccountName, Setting.SmsAccountPassword); - - Encoding encoding = Encoding.UTF8; - var wc = new WebClient(); - wc.Encoding = encoding; - var result = wc.DownloadString(url); - var lastCountResult = Newtonsoft.Json.JsonConvert.DeserializeObject(result); - var value = Newtonsoft.Json.JsonConvert.DeserializeObject(lastCountResult.Value); - return value.SMSNum.ToString(); - } - } - - /// - /// 发送结果 - /// - public class Fissosoft_SendResult - { - public int Code { get; set; } - public string Msg { get; set; } - } - - - /// - /// 获取剩余短信条数结果 - /// 格式:{"Code":1,"Msg":"成功!","Value":"{\"UserName\":\"test\",\"OperId\":\"sms0013\",\"SMSNum\":5846,\"EmergentSMSNum\":10,\"IsEnable\":1}"} - /// - public class Fissoft_LastCountResult - { - public int Code { get; set; } - public string Msg { get; set; } - public string Value { get; set; } - } - - /// - /// Fissoft_LastCountResult的Value类型 - /// - public class Fissoft_LastCountResult_Value - { - public string UserName { get; set; } - public string OperId { get; set; } - public int SMSNum { get; set; } - public int EmergentSMSNum { get; set; } - public int IsEnable { get; set; } - } - -} +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.Core.Extensions; +using Senparc.Ncf.SMS; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; +using System.Threading; + +namespace Senparc.Ncf.SMS +{ + public class SmsPlatform_Fissoft : SmsPlatform, ISmsPlatform + { + public SmsPlatform_Fissoft(string smsServiceAddress, string smsAccountCORPID, string smsAccountName, string smsAccountPassword, string smsAccountSubNumber) + : base(SmsPlatformType.Fissoft, smsServiceAddress, smsAccountCORPID, smsAccountName, smsAccountPassword, smsAccountSubNumber) + { + + } + + public override string SmsServiceAddress => "http://api.fissoft.com/pubservice"; + + public override SmsResult Send(string content, string number) + { + SmsResult smsResult = SmsResult.未知错误; + try + { + + Encoding encoding = Encoding.UTF8; + var wc = new WebClient(); + wc.Encoding = encoding; + int contentIndex = 0; + int limitedLetterCount = 65 * 4; + + //Assemble the message and determine the length of the content. If it is too long, develop it separately. + List messageList = new List(); + while (contentIndex < content.Length) + { + string limitedContent = content.Substring(contentIndex, Math.Min(limitedLetterCount, content.Length - contentIndex)); + messageList.Add(limitedContent); + contentIndex += limitedLetterCount; + } + + //Send in batches + for (int i = 0; i < messageList.Count; i++) + { + var msg = messageList[i]; + if (messageList.Count > 1) + { + msg = $" [{i + 1}/{messageList.Count}]" + msg; + } + //msg += "[Shengpai Network]"; + string encodedLimitedConent = UrlEncode(msg, encoding); + + + + var url = string.Format("{0}/SMSSend?OperId={1}&OperPass={2}&Mobiles={3}&Message={4}", + SmsServiceAddress, Setting.SmsAccountCORPID, Setting.SmsAccountPassword, number, encodedLimitedConent); + var smsState = wc.DownloadString(url); + + var fissosoft_SendResult = Newtonsoft.Json.JsonConvert.DeserializeObject(smsState); + if (fissosoft_SendResult.Code == 1) + { + smsResult = SmsResult.成功; + } + else + { + if (!Enum.TryParse(fissosoft_SendResult.Code.ToString(), out smsResult)) + { + smsResult = SmsResult.未知错误; + } + } + Log.LogUtility.SmsLogger.Info($"发送短信成功:{content},号码:{number}。状态:{smsResult.ToString()}。发送通道:Fissoft"); + Thread.Sleep(200); + } + } + catch + { + Log.LogUtility.SmsLogger.Error($"发送短信失败:{content},号码:{number}。发送通道:Fissoft"); + } + finally + { + } + return smsResult; + } + + public override string GetLastCount() + { + var url = string.Format("{0}/SMSAccountInfo?OperId={1}&OperPass={2}", + SmsServiceAddress, Setting.SmsAccountName, Setting.SmsAccountPassword); + + Encoding encoding = Encoding.UTF8; + var wc = new WebClient(); + wc.Encoding = encoding; + var result = wc.DownloadString(url); + var lastCountResult = Newtonsoft.Json.JsonConvert.DeserializeObject(result); + var value = Newtonsoft.Json.JsonConvert.DeserializeObject(lastCountResult.Value); + return value.SMSNum.ToString(); + } + } + + /// + ///Send results + /// + public class Fissosoft_SendResult + { + public int Code { get; set; } + public string Msg { get; set; } + } + + + /// + /// Get the result of the number of remaining text messages + /// Format: {"Code":1,"Msg":"Success!","Value":"{\"UserName\":\"test\",\"OperId\":\"sms0013\",\"SMSNum\":5846,\"EmergentSMSNum\":10,\"IsEnable\":1}"} + /// + public class Fissoft_LastCountResult + { + public int Code { get; set; } + public string Msg { get; set; } + public string Value { get; set; } + } + + /// + ///Value type of Fissoft_LastCountResult + /// + public class Fissoft_LastCountResult_Value + { + public string UserName { get; set; } + public string OperId { get; set; } + public int SMSNum { get; set; } + public int EmergentSMSNum { get; set; } + public int IsEnable { get; set; } + } + +} diff --git a/src/Basic/Senparc.Ncf.SMS/SmsPlatform/SmsPlatform_JunMei.cs b/src/Basic/Senparc.Ncf.SMS/SmsPlatform/SmsPlatform_JunMei.cs index cfa0fb0cc..11b4bb6f6 100644 --- a/src/Basic/Senparc.Ncf.SMS/SmsPlatform/SmsPlatform_JunMei.cs +++ b/src/Basic/Senparc.Ncf.SMS/SmsPlatform/SmsPlatform_JunMei.cs @@ -1,119 +1,119 @@ -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.Core.Extensions; -using Senparc.Ncf.Log; -using Senparc.Ncf.SMS; -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; -using System.Xml.Linq; - -namespace Senparc.Ncf.SMS -{ - public class SmsPlatform_JunMei : SmsPlatform, ISmsPlatform - { - public SmsPlatform_JunMei(string smsServiceAddress, string smsAccountCORPID, string smsAccountName, string smsAccountPassword, string smsAccountSubNumber) - : base(SmsPlatformType.JunMei, smsServiceAddress, smsAccountCORPID, smsAccountName, smsAccountPassword, smsAccountSubNumber) - { - - } - - public override string SmsServiceAddress => "http://120.77.14.55:8888/sms.aspx"; - /// - /// - /// - /// - /// - /// - public override SmsResult Send(string content, string number) - { - SmsResult smsResult = SmsResult.未知错误; - try - { - Encoding encoding = Encoding.UTF8; - int contentIndex = 0; - int limitedLetterCount = 65 * 4; - - //组装消息,判断内容长度,太长则分开发 - List messageList = new List(); - while (contentIndex < content.Length) - { - string limitedContent = content.Substring(contentIndex, Math.Min(limitedLetterCount, content.Length - contentIndex)); - messageList.Add(limitedContent); - contentIndex += limitedLetterCount; - } - - //分批发送 - for (int i = 0; i < messageList.Count; i++) - { - var msg = messageList[i]; - if (messageList.Count > 1) - { - msg = $" [{i + 1}/{messageList.Count}]" + msg; - } - string encodedLimitedConent = UrlEncode(msg, encoding); - - var url = $"{SmsServiceAddress}?action=send&userid={Setting.SmsAccountCORPID}&account={Setting.SmsAccountName}&password={Setting.SmsAccountPassword}&mobile={number}&content={encodedLimitedConent}"; - var resultDoc = XDocument.Load(url); - var status = resultDoc.Root.Element("returnstatus").Value; - var message = resultDoc.Root.Element("message").Value; - if (message.ToUpper() == "OK") - { - smsResult = SmsResult.成功; - LogUtility.SmsLogger.Info($"发送短信成功:{content},号码:{number}。状态:{status}({message})。发送通道:JunMei"); - } - Thread.Sleep(200); - } - } - catch (Exception) - { - LogUtility.SmsLogger.Error($"发送短信失败:{content},号码:{number}。发送通道:JunMei"); - } - return smsResult; - } - - public override string GetLastCount() - { - var url = $"{SmsServiceAddress}?action=overage&userid={Setting.SmsAccountCORPID}&account={Setting.SmsAccountName}&password={Setting.SmsAccountPassword}"; - var resultDoc = XDocument.Load(url); - return $"overage: {resultDoc.Root.Element("overage").Value}, sendTotal: {resultDoc.Root.Element("sendTotal").Value}"; - - } - - /// - /// 接收回复信息 - /// - /// - public ReplyMessageCollection GetReplyMessages() - { - throw new Exception("此方法已修改,请查看文档!"); - - //var url = - // $"{SmsServiceAddress}/Mo.asp?CORPID={Setting.SmsAccountCORPID}&USERNAME={Setting.SmsAccountName}&PASSWORD={Setting.SmsAccountPassword}"; - - //var replyMessageCollection = new ReplyMessageCollection(); - //var wc = new WebClient(); - //var result = wc.DownloadString(url); - //if (result == "0") - //{ - // return replyMessageCollection; - //} - - //var msgs = result.Split(new[] { "|;|" }, StringSplitOptions.RemoveEmptyEntries); - //msgs.AsParallel().ForAll(z => - //{ - // var datas = z.Split(new[] { "|^|" }, StringSplitOptions.RemoveEmptyEntries); - // var replyMessage = new ReplyMessage() - // { - // State = int.Parse(datas[0]), - // Id = int.Parse(datas[1]), - // PhoneNumber = datas[2], - // DateTime = DateTime.Parse(datas[3]) - // }; - // replyMessageCollection.MsgCollection.Add(replyMessage); - //}); - //return replyMessageCollection; - } - } - -} +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.Core.Extensions; +using Senparc.Ncf.Log; +using Senparc.Ncf.SMS; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Xml.Linq; + +namespace Senparc.Ncf.SMS +{ + public class SmsPlatform_JunMei : SmsPlatform, ISmsPlatform + { + public SmsPlatform_JunMei(string smsServiceAddress, string smsAccountCORPID, string smsAccountName, string smsAccountPassword, string smsAccountSubNumber) + : base(SmsPlatformType.JunMei, smsServiceAddress, smsAccountCORPID, smsAccountName, smsAccountPassword, smsAccountSubNumber) + { + + } + + public override string SmsServiceAddress => "http://120.77.14.55:8888/sms.aspx"; + /// + /// + /// + /// + /// + /// + public override SmsResult Send(string content, string number) + { + SmsResult smsResult = SmsResult.未知错误; + try + { + Encoding encoding = Encoding.UTF8; + int contentIndex = 0; + int limitedLetterCount = 65 * 4; + + //Assemble the message and determine the length of the content. If it is too long, develop it separately. + List messageList = new List(); + while (contentIndex < content.Length) + { + string limitedContent = content.Substring(contentIndex, Math.Min(limitedLetterCount, content.Length - contentIndex)); + messageList.Add(limitedContent); + contentIndex += limitedLetterCount; + } + + //Send in batches + for (int i = 0; i < messageList.Count; i++) + { + var msg = messageList[i]; + if (messageList.Count > 1) + { + msg = $" [{i + 1}/{messageList.Count}]" + msg; + } + string encodedLimitedConent = UrlEncode(msg, encoding); + + var url = $"{SmsServiceAddress}?action=send&userid={Setting.SmsAccountCORPID}&account={Setting.SmsAccountName}&password={Setting.SmsAccountPassword}&mobile={number}&content={encodedLimitedConent}"; + var resultDoc = XDocument.Load(url); + var status = resultDoc.Root.Element("returnstatus").Value; + var message = resultDoc.Root.Element("message").Value; + if (message.ToUpper() == "OK") + { + smsResult = SmsResult.成功; + LogUtility.SmsLogger.Info($"发送短信成功:{content},号码:{number}。状态:{status}({message})。发送通道:JunMei"); + } + Thread.Sleep(200); + } + } + catch (Exception) + { + LogUtility.SmsLogger.Error($"发送短信失败:{content},号码:{number}。发送通道:JunMei"); + } + return smsResult; + } + + public override string GetLastCount() + { + var url = $"{SmsServiceAddress}?action=overage&userid={Setting.SmsAccountCORPID}&account={Setting.SmsAccountName}&password={Setting.SmsAccountPassword}"; + var resultDoc = XDocument.Load(url); + return $"overage: {resultDoc.Root.Element("overage").Value}, sendTotal: {resultDoc.Root.Element("sendTotal").Value}"; + + } + + /// + ///Receive reply message + /// + /// + public ReplyMessageCollection GetReplyMessages() + { + throw new Exception("此方法已修改,请查看文档!"); + + //var url = + // $"{SmsServiceAddress}/Mo.asp?CORPID={Setting.SmsAccountCORPID}&USERNAME={Setting.SmsAccountName}&PASSWORD={Setting.SmsAccountPassword}"; + + //var replyMessageCollection = new ReplyMessageCollection(); + //var wc = new WebClient(); + //var result = wc.DownloadString(url); + //if (result == "0") + //{ + // return replyMessageCollection; + //} + + //var msgs = result.Split(new[] { "|;|" }, StringSplitOptions.RemoveEmptyEntries); + //msgs.AsParallel().ForAll(z => + //{ + // var datas = z.Split(new[] { "|^|" }, StringSplitOptions.RemoveEmptyEntries); + // var replyMessage = new ReplyMessage() + // { + // State = int.Parse(datas[0]), + // Id = int.Parse(datas[1]), + // PhoneNumber = datas[2], + // DateTime = DateTime.Parse(datas[3]) + // }; + // replyMessageCollection.MsgCollection.Add(replyMessage); + //}); + //return replyMessageCollection; + } + } + +} diff --git a/src/Basic/Senparc.Ncf.Service/Common/EncryptionService.cs b/src/Basic/Senparc.Ncf.Service/Common/EncryptionService.cs index e72e12240..77bb2d3be 100644 --- a/src/Basic/Senparc.Ncf.Service/Common/EncryptionService.cs +++ b/src/Basic/Senparc.Ncf.Service/Common/EncryptionService.cs @@ -1,90 +1,90 @@ -using Senparc.CO2NET; -using Senparc.Ncf.Core.Utility; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Senparc.Ncf.Service -{ - //public partial interface IEncryptionService : IEncryptionServiceBase - //{ - // string CommonEncrypt(string str); - // string CommonDecrypt(string str); - - // string GetEncodedUserAvatar(int accountId, string encodeKey); - // int GetDecodedUserAvatar(string code, string encodeKey); - - // string GetEncodedWeixinTokenPass(int weixinApp, string token, string encodeKey); - // Dictionary CheckDecodedWeixinTokenPass(string code, string encodeKey); - - // string GetEncodedQyAppKey(int qyAppId, DateTime qyAppAddTime, string encodeKey); - // //QyApp CheckDecodedQyAppKey(string code, string encodeKey); - - // string EncodeAppCode(int appId, int neuralAppId, string encodeKey); - // int[] DecodeAppCode(string appCode, string encodeKey); - //} - - public partial class EncryptionService : EncryptionServiceBase//, IEncryptionService - { - public const string BASE_ENCRYPT_ENCODING_KEY = "3Eddo*8jp"; - - - public EncryptionService()//(IBaseData baseData): base(baseData) - { } - - /// - /// 通用加密 - /// - /// - /// - public string CommonEncrypt(string str) - { - var content = $"{str}|{BASE_ENCRYPT_ENCODING_KEY}"; - return DesUtility.EncryptDES(content, BASE_ENCRYPT_ENCODING_KEY); - } - - /// - /// 通用解密 - /// - /// - /// - public string CommonDecrypt(string str) - { - var content = DesUtility.EncryptDES(str, BASE_ENCRYPT_ENCODING_KEY); - return str.Split('|')[0]; - } - - #region UserAvatar - - public string GetEncodedUserAvatar(int accountId, string encodeKey) - { - var content = $"{DateTime.Now.Ticks}|{accountId}"; - return DesUtility.EncryptDES(content, encodeKey); - } - - public int GetDecodedUserAvatar(string code, string encodeKey) - { - try - { - var content = DesUtility.DecryptDES(code, encodeKey); - var datas = content.Split('|'); - var dateTime = new DateTime(long.Parse(datas[0])); - - if ((DateTime.Now - dateTime).TotalSeconds > 15) - { - return -1;//指定秒后失效 - } - - var accountId = int.Parse(datas[1]); - return accountId; - } - catch - { - return -1; - } - } - - #endregion - } -} +using Senparc.CO2NET; +using Senparc.Ncf.Core.Utility; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Senparc.Ncf.Service +{ + //public partial interface IEncryptionService : IEncryptionServiceBase + //{ + // string CommonEncrypt(string str); + // string CommonDecrypt(string str); + + // string GetEncodedUserAvatar(int accountId, string encodeKey); + // int GetDecodedUserAvatar(string code, string encodeKey); + + // string GetEncodedWeixinTokenPass(int weixinApp, string token, string encodeKey); + // Dictionary CheckDecodedWeixinTokenPass(string code, string encodeKey); + + // string GetEncodedQyAppKey(int qyAppId, DateTime qyAppAddTime, string encodeKey); + // //QyApp CheckDecodedQyAppKey(string code, string encodeKey); + + // string EncodeAppCode(int appId, int neuralAppId, string encodeKey); + // int[] DecodeAppCode(string appCode, string encodeKey); + //} + + public partial class EncryptionService : EncryptionServiceBase//, IEncryptionService + { + public const string BASE_ENCRYPT_ENCODING_KEY = "3Eddo*8jp"; + + + public EncryptionService()//(IBaseData baseData): base(baseData) + { } + + /// + ///Universal Encryption + /// + /// + /// + public string CommonEncrypt(string str) + { + var content = $"{str}|{BASE_ENCRYPT_ENCODING_KEY}"; + return DesUtility.EncryptDES(content, BASE_ENCRYPT_ENCODING_KEY); + } + + /// + /// universal decryption + /// + /// + /// + public string CommonDecrypt(string str) + { + var content = DesUtility.EncryptDES(str, BASE_ENCRYPT_ENCODING_KEY); + return str.Split('|')[0]; + } + + #region UserAvatar + + public string GetEncodedUserAvatar(int accountId, string encodeKey) + { + var content = $"{DateTime.Now.Ticks}|{accountId}"; + return DesUtility.EncryptDES(content, encodeKey); + } + + public int GetDecodedUserAvatar(string code, string encodeKey) + { + try + { + var content = DesUtility.DecryptDES(code, encodeKey); + var datas = content.Split('|'); + var dateTime = new DateTime(long.Parse(datas[0])); + + if ((DateTime.Now - dateTime).TotalSeconds > 15) + { + return -1;//Expires after specified seconds + } + + var accountId = int.Parse(datas[1]); + return accountId; + } + catch + { + return -1; + } + } + + #endregion + } +} diff --git a/src/Basic/Senparc.Ncf.Service/ServiceBase/AutoDetectChangeContext/AutoDetectChangeContext.cs b/src/Basic/Senparc.Ncf.Service/ServiceBase/AutoDetectChangeContext/AutoDetectChangeContext.cs index 85ee5d80a..b2ae615f9 100644 --- a/src/Basic/Senparc.Ncf.Service/ServiceBase/AutoDetectChangeContext/AutoDetectChangeContext.cs +++ b/src/Basic/Senparc.Ncf.Service/ServiceBase/AutoDetectChangeContext/AutoDetectChangeContext.cs @@ -1,46 +1,46 @@ -using System; - -namespace Senparc.Ncf.Service -{ - /// - /// 临时开放BaseDataContext.Configuration.AutoDetectChangesEnabled属性,用于大批量更新数据的环境,结束后还原到false状态。 - /// - public class AutoDetectChangeContextWrap : IDisposable - { - public IServiceDataBase ServiceData { get; set; } - //private INcfDbData _ncfDbData; - - - public AutoDetectChangeContextWrap(IServiceDataBase serviceData) - { - //_service = service; - //_service.BaseRepository.BaseDB.BaseDataContext.Configuration.AutoDetectChangesEnabled = true; - //_ncfDbData = ncfDbData; - ServiceData = serviceData; - ServiceData.BaseData.BaseDB.BaseDataContext.ChangeTracker.AutoDetectChangesEnabled= false; - ServiceData.BaseData.BaseDB.ManualDetectChangeObject = true; - } - - public void Dispose() - { - //_service.BaseRepository.BaseDB.BaseDataContext.Configuration.AutoDetectChangesEnabled = false; - ServiceData.BaseData.BaseDB.BaseDataContext.ChangeTracker.AutoDetectChangesEnabled = true; - ServiceData.BaseData.BaseDB.ManualDetectChangeObject = false; - } - } - - public class CloseAutoDetectChangeContext : IDisposable - { - AutoDetectChangeContextWrap _wrap; - public CloseAutoDetectChangeContext(AutoDetectChangeContextWrap wrap) - { - _wrap = wrap; - _wrap.ServiceData.BaseData.BaseDB.BaseDataContext.ChangeTracker.AutoDetectChangesEnabled = true; - } - - public void Dispose() - { - _wrap.ServiceData.BaseData.BaseDB.BaseDataContext.ChangeTracker.AutoDetectChangesEnabled = false; - } - } -} +using System; + +namespace Senparc.Ncf.Service +{ + /// + /// Temporarily open the BaseDataContext.Configuration.AutoDetectChangesEnabled property, which is used in environments where data is updated in large batches. It will be restored to the false state after completion. + /// + public class AutoDetectChangeContextWrap : IDisposable + { + public IServiceDataBase ServiceData { get; set; } + //private INcfDbData _ncfDbData; + + + public AutoDetectChangeContextWrap(IServiceDataBase serviceData) + { + //_service = service; + //_service.BaseRepository.BaseDB.BaseDataContext.Configuration.AutoDetectChangesEnabled = true; + //_ncfDbData = ncfDbData; + ServiceData = serviceData; + ServiceData.BaseData.BaseDB.BaseDataContext.ChangeTracker.AutoDetectChangesEnabled= false; + ServiceData.BaseData.BaseDB.ManualDetectChangeObject = true; + } + + public void Dispose() + { + //_service.BaseRepository.BaseDB.BaseDataContext.Configuration.AutoDetectChangesEnabled = false; + ServiceData.BaseData.BaseDB.BaseDataContext.ChangeTracker.AutoDetectChangesEnabled = true; + ServiceData.BaseData.BaseDB.ManualDetectChangeObject = false; + } + } + + public class CloseAutoDetectChangeContext : IDisposable + { + AutoDetectChangeContextWrap _wrap; + public CloseAutoDetectChangeContext(AutoDetectChangeContextWrap wrap) + { + _wrap = wrap; + _wrap.ServiceData.BaseData.BaseDB.BaseDataContext.ChangeTracker.AutoDetectChangesEnabled = true; + } + + public void Dispose() + { + _wrap.ServiceData.BaseData.BaseDB.BaseDataContext.ChangeTracker.AutoDetectChangesEnabled = false; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Service/ServiceBase/AutoDetectChangeContext/AutoDetectChangeContextExtension.cs b/src/Basic/Senparc.Ncf.Service/ServiceBase/AutoDetectChangeContext/AutoDetectChangeContextExtension.cs index 51eb8db7f..328b83d41 100644 --- a/src/Basic/Senparc.Ncf.Service/ServiceBase/AutoDetectChangeContext/AutoDetectChangeContextExtension.cs +++ b/src/Basic/Senparc.Ncf.Service/ServiceBase/AutoDetectChangeContext/AutoDetectChangeContextExtension.cs @@ -1,37 +1,37 @@ -using Microsoft.EntityFrameworkCore; - -namespace Senparc.Ncf.Service -{ - public static class AutoDetectChangeContextExtension - { - /// - /// 创建AutoDetectChangeContext的实例 - /// - /// - /// - public static AutoDetectChangeContextWrap InstanceAutoDetectChangeContextWrap(this IServiceDataBase serviceData) - { - return new AutoDetectChangeContextWrap(serviceData); - } - - /// - /// 创建CloseAutoDetectChangeContext的实例 - /// - /// AutoDetectChangeContextWrap实例 - /// - public static CloseAutoDetectChangeContext InstanceCloseAutoDetectChangeContext(this AutoDetectChangeContextWrap wrap) - { - return new CloseAutoDetectChangeContext(wrap); - } - - /// - /// 创建CloseAutoDetectChangeContext的实例 - /// - /// AutoDetectChangeContextWrap实例 - /// - public static void ForceDetectChange(this AutoDetectChangeContextWrap wrap, object entity) - { - wrap.ServiceData.BaseData.BaseDB.BaseDataContext.Entry(entity).State = EntityState.Modified; - } - } -} +using Microsoft.EntityFrameworkCore; + +namespace Senparc.Ncf.Service +{ + public static class AutoDetectChangeContextExtension + { + /// + /// Create an instance of AutoDetectChangeContext + /// + /// + /// + public static AutoDetectChangeContextWrap InstanceAutoDetectChangeContextWrap(this IServiceDataBase serviceData) + { + return new AutoDetectChangeContextWrap(serviceData); + } + + /// + /// Create an instance of CloseAutoDetectChangeContext + /// + /// AutoDetectChangeContextWrap instance + /// + public static CloseAutoDetectChangeContext InstanceCloseAutoDetectChangeContext(this AutoDetectChangeContextWrap wrap) + { + return new CloseAutoDetectChangeContext(wrap); + } + + /// + /// Create an instance of CloseAutoDetectChangeContext + /// + /// AutoDetectChangeContextWrap instance + /// + public static void ForceDetectChange(this AutoDetectChangeContextWrap wrap, object entity) + { + wrap.ServiceData.BaseData.BaseDB.BaseDataContext.Entry(entity).State = EntityState.Modified; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Service/ServiceBase/ClientServiceBase.cs b/src/Basic/Senparc.Ncf.Service/ServiceBase/ClientServiceBase.cs index f7cad55a1..732cc0d3e 100644 --- a/src/Basic/Senparc.Ncf.Service/ServiceBase/ClientServiceBase.cs +++ b/src/Basic/Senparc.Ncf.Service/ServiceBase/ClientServiceBase.cs @@ -1,38 +1,38 @@ -using AutoMapper; -using Microsoft.EntityFrameworkCore.Storage; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Repository; -using Senparc.Ncf.Service; -using System; - -namespace Senparc.Ncf.Service -{ - public interface IClientServiceBase : IServiceBase where T : EntityBase//global::System.Data.Objects.DataClasses.EntityObject, new() - { - IClientRepositoryBase BaseClientRepository { get; } - - ///// - ///// 开启事务 - ///// - ///// - //IDbContextTransaction BeginTransaction(); - } - - - public class ClientServiceBase : ServiceBase, IClientServiceBase where T : EntityBase//global::System.Data.Objects.DataClasses.EntityObject, new() - { - public IClientRepositoryBase BaseClientRepository - { - get - { - return RepositoryBase as IClientRepositoryBase; - } - } - - public ClientServiceBase(IClientRepositoryBase repo, IServiceProvider serviceProvider) - : base(repo, serviceProvider) - { - - } - } +using AutoMapper; +using Microsoft.EntityFrameworkCore.Storage; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Repository; +using Senparc.Ncf.Service; +using System; + +namespace Senparc.Ncf.Service +{ + public interface IClientServiceBase : IServiceBase where T : EntityBase//global::System.Data.Objects.DataClasses.EntityObject, new() + { + IClientRepositoryBase BaseClientRepository { get; } + + ///// + ///// Start transaction + ///// + ///// + //IDbContextTransaction BeginTransaction(); + } + + + public class ClientServiceBase : ServiceBase, IClientServiceBase where T : EntityBase//global::System.Data.Objects.DataClasses.EntityObject, new() + { + public IClientRepositoryBase BaseClientRepository + { + get + { + return RepositoryBase as IClientRepositoryBase; + } + } + + public ClientServiceBase(IClientRepositoryBase repo, IServiceProvider serviceProvider) + : base(repo, serviceProvider) + { + + } + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Service/ServiceBase/DtoServiceBase.cs b/src/Basic/Senparc.Ncf.Service/ServiceBase/DtoServiceBase.cs index b6d1a8982..753d528ae 100644 --- a/src/Basic/Senparc.Ncf.Service/ServiceBase/DtoServiceBase.cs +++ b/src/Basic/Senparc.Ncf.Service/ServiceBase/DtoServiceBase.cs @@ -1,131 +1,131 @@ -using Microsoft.EntityFrameworkCore; -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Repository; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Service.ServiceBase -{ - public class DtoServiceBase : ServiceBase - where TEntity : class, IEntityBase, new() - where TEntityDto : IDtoBase - { - public DtoServiceBase(IRepositoryBase repo, IServiceProvider serviceProvider) - : base(repo, serviceProvider) - { - - } - - - public override bool IsInsert(TEntity obj) - { - return RepositoryBase.IsInsert(obj); - } - - public override TEntity GetObject(Expression> where,params string[] includes) - { - return RepositoryBase.GetFirstOrDefaultObject(where, includes); - } - - public override TEntity GetObject(Expression> where, Expression> orderBy, OrderingType orderingType,params string[] includes) - { - return RepositoryBase.GetFirstOrDefaultObject(where, orderBy, orderingType, includes); - } - - public override PagedList GetFullList(Expression> where, Expression> orderBy, OrderingType orderingType,params string[] includes) - { - return this.GetObjectList(0, 0, where, orderBy, orderingType, includes); - } - - /// - /// 获取分页数据 - /// - /// - /// 页码 - /// 每页数量 - /// 条件 - /// 排序字段 - /// 正序|倒叙 - /// - /// - public override PagedList GetObjectList(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType,params string[] includes) - { - return RepositoryBase.GetObjectList(where, orderBy, orderingType, pageIndex, pageCount, includes); - } - - - /// - /// 获取分页数据 - /// - /// - /// 页码 - /// 每页数量 - /// 条件 - /// 排序字段 - /// 正序|倒叙 - /// - /// - public override async Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType,params string[] includes) - { - return await RepositoryBase.GetObjectListAsync(where, orderBy, orderingType, pageIndex, pageCount, includes); - } - - public override int GetCount(Expression> where,params string[] includes) - { - return RepositoryBase.ObjectCount(where, includes); - } - - public override decimal GetSum(Expression> where, Expression> sum,params string[] includes) - { - return RepositoryBase.GetSum(where, sum, includes); - } - - /// - /// 强制将实体设置为Modified状态 - /// - /// - public override void TryDetectChange(TEntity obj) - { - if (!IsInsert(obj)) - { - RepositoryBase.BaseDB.BaseDataContext.Entry(obj).State = EntityState.Modified; - } - } - - public override void SaveObject(TEntity obj) - { - if (RepositoryBase.BaseDB.ManualDetectChangeObject) - { - TryDetectChange(obj); - } - RepositoryBase.Save(obj); - } - - public override void DeleteObject(Expression> predicate) - { - TEntity obj = GetObject(predicate); - DeleteObject(obj); - } - - public override void DeleteObject(TEntity obj) - { - RepositoryBase.Delete(obj); - } - - public override void DeleteAll(IEnumerable objects) - { - var list = objects.ToList(); - for (int i = 0; i < list.Count; i++) - { - DeleteObject(list[i]); - } - } - - //TODO: 提供异步版本 - } -} +using Microsoft.EntityFrameworkCore; +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Repository; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Service.ServiceBase +{ + public class DtoServiceBase : ServiceBase + where TEntity : class, IEntityBase, new() + where TEntityDto : IDtoBase + { + public DtoServiceBase(IRepositoryBase repo, IServiceProvider serviceProvider) + : base(repo, serviceProvider) + { + + } + + + public override bool IsInsert(TEntity obj) + { + return RepositoryBase.IsInsert(obj); + } + + public override TEntity GetObject(Expression> where,params string[] includes) + { + return RepositoryBase.GetFirstOrDefaultObject(where, includes); + } + + public override TEntity GetObject(Expression> where, Expression> orderBy, OrderingType orderingType,params string[] includes) + { + return RepositoryBase.GetFirstOrDefaultObject(where, orderBy, orderingType, includes); + } + + public override PagedList GetFullList(Expression> where, Expression> orderBy, OrderingType orderingType,params string[] includes) + { + return this.GetObjectList(0, 0, where, orderBy, orderingType, includes); + } + + /// + /// Get paging data + /// + /// + /// Page number + /// Number per page + /// Condition + /// Sort field + /// Forward sequence | Flashback + /// + /// + public override PagedList GetObjectList(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType,params string[] includes) + { + return RepositoryBase.GetObjectList(where, orderBy, orderingType, pageIndex, pageCount, includes); + } + + + /// + /// Get paging data + /// + /// + /// Page number + /// Number per page + /// Condition + /// Sort field + /// Forward sequence | Flashback + /// + /// + public override async Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType,params string[] includes) + { + return await RepositoryBase.GetObjectListAsync(where, orderBy, orderingType, pageIndex, pageCount, includes); + } + + public override int GetCount(Expression> where,params string[] includes) + { + return RepositoryBase.ObjectCount(where, includes); + } + + public override decimal GetSum(Expression> where, Expression> sum,params string[] includes) + { + return RepositoryBase.GetSum(where, sum, includes); + } + + /// + /// Force the entity to Modified state + /// + /// + public override void TryDetectChange(TEntity obj) + { + if (!IsInsert(obj)) + { + RepositoryBase.BaseDB.BaseDataContext.Entry(obj).State = EntityState.Modified; + } + } + + public override void SaveObject(TEntity obj) + { + if (RepositoryBase.BaseDB.ManualDetectChangeObject) + { + TryDetectChange(obj); + } + RepositoryBase.Save(obj); + } + + public override void DeleteObject(Expression> predicate) + { + TEntity obj = GetObject(predicate); + DeleteObject(obj); + } + + public override void DeleteObject(TEntity obj) + { + RepositoryBase.Delete(obj); + } + + public override void DeleteAll(IEnumerable objects) + { + var list = objects.ToList(); + for (int i = 0; i < list.Count; i++) + { + DeleteObject(list[i]); + } + } + + //TODO: Provide an asynchronous version + } +} diff --git a/src/Basic/Senparc.Ncf.Service/ServiceBase/IServiceBase.cs b/src/Basic/Senparc.Ncf.Service/ServiceBase/IServiceBase.cs index efa849225..7058ae789 100644 --- a/src/Basic/Senparc.Ncf.Service/ServiceBase/IServiceBase.cs +++ b/src/Basic/Senparc.Ncf.Service/ServiceBase/IServiceBase.cs @@ -1,88 +1,88 @@ -using AutoMapper; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Query; -using Senparc.Ncf.Core.DI; -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Core.MultiTenant; -using Senparc.Ncf.Repository; -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Service -{ - public interface IServiceBase : IServiceDataBase, IAutoDI where T : class, IEntityBase// global::System.Data.Objects.DataClasses.EntityObject, new() - { - IMapper Mapper { get; set; } - IRepositoryBase RepositoryBase { get; set; } - - void BeginTransaction(); - void BeginTransaction(Action body, Action rollbackAction = null); - Task BeginTransactionAsync(); - Task BeginTransactionAsync(Action action); - Task BeginTransactionAsync(Func body, Action rollbackAction = null); - Task BeginTransactionAsync(Func body, Func rollbackAction); - Task BeginTransactionAsync(Func bodyAsync, Func> rollbackActionAsync); - void CommitTransaction(); - void DeleteAll(IEnumerable objects); - Task DeleteAllAsync(Expression> where, Action deleteItemAction = null, bool softDelete = false); - Task DeleteAllAsync(IEnumerable objects, Action deleteItemAction = null, bool softDelete = false); - void DeleteObject(Expression> predicate); - void DeleteObject(T obj); - Task DeleteObjectAsync(Expression> predicate); - Task DeleteObjectAsync(T obj); - int GetCount(Expression> where, params string[] includes); - int GetCount(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - Task GetCountAsync(Expression> where, params string[] includes); - Task GetCountAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - PagedList GetFullList(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - PagedList GetFullList(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes); - Task> GetFullListAsync(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes); - Task> GetFullListAsync(Expression> where, string orderField = null, params string[] includes); - Task> GetFullListAsync(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - Task> GetFullListAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc, string orderField = null); - T GetObject(Expression> where, params string[] includes); - T GetObject(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - T GetObject(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - T GetObject(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes); - Task GetObjectAsync(Expression> where, params string[] includes); - Task GetObjectAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - Task GetObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - Task GetObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes); - PagedList GetObjectList(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - PagedList GetObjectList(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes); - Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, string orderBy, params string[] includes); - Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, string orderBy, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes); - decimal GetSum(Expression> where, Expression> sum, params string[] includes); - Task GetSumAsync(Expression> where, Expression> sum, params string[] includes); - Task GetSumAsync(Expression> where, Expression> sum, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); - bool IsInsert(T obj); - void RollbackTransaction(); - void SaveObject(T obj); - void SaveChanges(); - - Task SaveObjectAsync(T obj); - Task SaveChangesAsync(); - Task SaveObjectListAsync(IEnumerable objs); - void TryDetectChange(T obj); - - /// - /// 使用 Mapper.Map<TDto>(entity) 快速返回 - /// - /// - /// - /// - TDto Mapping(T entity); - - /// - /// 强制设置租户信息 - /// - /// - /// - bool SetTenantInfo(RequestTenantInfo requestTenantInfo); - } +using AutoMapper; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using Senparc.Ncf.Core.DI; +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Core.MultiTenant; +using Senparc.Ncf.Repository; +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Service +{ + public interface IServiceBase : IServiceDataBase, IAutoDI where T : class, IEntityBase// global::System.Data.Objects.DataClasses.EntityObject, new() + { + IMapper Mapper { get; set; } + IRepositoryBase RepositoryBase { get; set; } + + void BeginTransaction(); + void BeginTransaction(Action body, Action rollbackAction = null); + Task BeginTransactionAsync(); + Task BeginTransactionAsync(Action action); + Task BeginTransactionAsync(Func body, Action rollbackAction = null); + Task BeginTransactionAsync(Func body, Func rollbackAction); + Task BeginTransactionAsync(Func bodyAsync, Func> rollbackActionAsync); + void CommitTransaction(); + void DeleteAll(IEnumerable objects); + Task DeleteAllAsync(Expression> where, Action deleteItemAction = null, bool softDelete = false); + Task DeleteAllAsync(IEnumerable objects, Action deleteItemAction = null, bool softDelete = false); + void DeleteObject(Expression> predicate); + void DeleteObject(T obj); + Task DeleteObjectAsync(Expression> predicate); + Task DeleteObjectAsync(T obj); + int GetCount(Expression> where, params string[] includes); + int GetCount(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + Task GetCountAsync(Expression> where, params string[] includes); + Task GetCountAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + PagedList GetFullList(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + PagedList GetFullList(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes); + Task> GetFullListAsync(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes); + Task> GetFullListAsync(Expression> where, string orderField = null, params string[] includes); + Task> GetFullListAsync(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + Task> GetFullListAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc, string orderField = null); + T GetObject(Expression> where, params string[] includes); + T GetObject(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + T GetObject(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + T GetObject(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes); + Task GetObjectAsync(Expression> where, params string[] includes); + Task GetObjectAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + Task GetObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + Task GetObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes); + PagedList GetObjectList(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + PagedList GetObjectList(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes); + Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, string orderBy, params string[] includes); + Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, string orderBy, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes); + decimal GetSum(Expression> where, Expression> sum, params string[] includes); + Task GetSumAsync(Expression> where, Expression> sum, params string[] includes); + Task GetSumAsync(Expression> where, Expression> sum, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc); + bool IsInsert(T obj); + void RollbackTransaction(); + void SaveObject(T obj); + void SaveChanges(); + + Task SaveObjectAsync(T obj); + Task SaveChangesAsync(); + Task SaveObjectListAsync(IEnumerable objs); + void TryDetectChange(T obj); + + /// + /// Use Mapper.Map<TDto>(entity) to return quickly + /// + /// + /// + /// + TDto Mapping(T entity); + + /// + /// Force tenant information to be set + /// + /// + /// + bool SetTenantInfo(RequestTenantInfo requestTenantInfo); + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Service/ServiceBase/ResilientTransaction.cs b/src/Basic/Senparc.Ncf.Service/ServiceBase/ResilientTransaction.cs index d901e8ed0..0b60cfeca 100644 --- a/src/Basic/Senparc.Ncf.Service/ServiceBase/ResilientTransaction.cs +++ b/src/Basic/Senparc.Ncf.Service/ServiceBase/ResilientTransaction.cs @@ -1,35 +1,35 @@ -using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Service.ServiceBase -{ - /// - /// 默认事务策略,微软文档用法 - /// - /// - public class ResilientTransaction - { - private DbContext _context; - private ResilientTransaction(DbContext context) => - _context = context ?? throw new ArgumentNullException(nameof(context)); - - public static ResilientTransaction New(DbContext context) => - new ResilientTransaction(context); - - public async Task ExecuteAsync(Func action) - { - var strategy = _context.Database.CreateExecutionStrategy(); - await strategy.ExecuteAsync(async () => - { - using (var transaction = _context.Database.BeginTransaction()) - { - await action(); - transaction.Commit(); - } - }); - } - } -} +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Service.ServiceBase +{ + /// + ///Default transaction policy, Microsoft document usage + /// + /// + public class ResilientTransaction + { + private DbContext _context; + private ResilientTransaction(DbContext context) => + _context = context ?? throw new ArgumentNullException(nameof(context)); + + public static ResilientTransaction New(DbContext context) => + new ResilientTransaction(context); + + public async Task ExecuteAsync(Func action) + { + var strategy = _context.Database.CreateExecutionStrategy(); + await strategy.ExecuteAsync(async () => + { + using (var transaction = _context.Database.BeginTransaction()) + { + await action(); + transaction.Commit(); + } + }); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Service/ServiceBase/ServiceBase.cs b/src/Basic/Senparc.Ncf.Service/ServiceBase/ServiceBase.cs index 30b116cf5..5627c8fd7 100644 --- a/src/Basic/Senparc.Ncf.Service/ServiceBase/ServiceBase.cs +++ b/src/Basic/Senparc.Ncf.Service/ServiceBase/ServiceBase.cs @@ -1,614 +1,614 @@ -using AutoMapper; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Storage; -using Senparc.CO2NET; -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Repository; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.EntityFrameworkCore.Query; -using Senparc.Ncf.Core.Models.DataBaseModel; -using Senparc.Ncf.Core.MultiTenant; - -namespace Senparc.Ncf.Service -{ - public class ServiceBase : ServiceDataBase, IServiceBase where T : class, IEntityBase// global::System.Data.Objects.DataClasses.EntityObject, new() - { - public IMapper Mapper { get; set; } //TODO: add in to Wapper - - public IRepositoryBase RepositoryBase { get; set; } - protected IServiceProvider _serviceProvider => base.ServiceProvider; - - public ServiceBase(IRepositoryBase repo, IServiceProvider serviceProvider) - : base(repo, serviceProvider) - { - //_serviceProvider = serviceProvider; - RepositoryBase = repo; - Mapper = _serviceProvider.GetService();//确保 Mapper 中有值 - } - - #region Insert & DetectChange - - /// - /// 强制将实体设置为Modified状态 - /// - /// - public virtual void TryDetectChange(T obj) - { - if (!IsInsert(obj)) - { - RepositoryBase.BaseDB.BaseDataContext.Entry(obj).State = EntityState.Modified; - } - } - - public virtual bool IsInsert(T obj) - { - return RepositoryBase.IsInsert(obj); - } - - #endregion - - - #region GetObject - - public virtual T GetObject(Expression> where, params string[] includes) - { - return RepositoryBase.GetFirstOrDefaultObject(where, includes); - } - - public virtual async Task GetObjectAsync(Expression> where, params string[] includes) - { - return await RepositoryBase.GetFirstOrDefaultObjectAsync(where, includes); - } - - public virtual T GetObject(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - return RepositoryBase.GetFirstOrDefaultObject(where, includesNavigationPropertyPathFunc); - } - - public virtual async Task GetObjectAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - return await RepositoryBase.GetFirstOrDefaultObjectAsync(where, includesNavigationPropertyPathFunc); - } - - public virtual T GetObject(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes) - { - return RepositoryBase.GetFirstOrDefaultObject(where, orderBy, orderingType, includes); - } - - public virtual async Task GetObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes) - { - return await RepositoryBase.GetFirstOrDefaultObjectAsync(where, orderBy, orderingType, includes); - } - - public virtual T GetObject(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - return RepositoryBase.GetFirstOrDefaultObject(where, orderBy, orderingType, includesNavigationPropertyPathFunc); - } - - public virtual async Task GetObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - return await RepositoryBase.GetFirstOrDefaultObjectAsync(where, orderBy, orderingType, includesNavigationPropertyPathFunc); - } - - #endregion - - #region GetFullList - - public virtual PagedList GetFullList(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes) - { - return this.GetObjectList(0, 0, where, orderBy, orderingType, includes); - } - - /// - /// 获取所有数据 - /// - /// - /// - /// - /// - public virtual async Task> GetFullListAsync(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes) - { - return await this.GetObjectListAsync(0, 0, where, orderBy, orderingType, includes); - } - - /// - /// 获取所有数据 - /// - /// - /// - /// xxx desc, yyy asc - /// - /// - public virtual async Task> GetFullListAsync(Expression> where, string orderField = null, params string[] includes) - { - return await RepositoryBase.GetObjectListAsync(where, orderField, 0, 0, includes); - } - - public virtual PagedList GetFullList(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - return this.GetObjectList(0, 0, where, orderBy, orderingType, includesNavigationPropertyPathFunc); - } - - /// - /// 获取所有数据 - /// - /// - /// - /// - /// - /// - /// - /// - public virtual async Task> GetFullListAsync(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - return await RepositoryBase.GetObjectListAsync(where, orderBy, orderingType, 0, 0, includesNavigationPropertyPathFunc); - } - - /// - /// 获取所有数据 - /// - /// - /// - /// - /// xxx desc, yyy asc - /// - /// - public virtual async Task> GetFullListAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc, string orderField = null) - { - return await RepositoryBase.GetObjectListAsync(where, orderField, 0, 0, includesNavigationPropertyPathFunc); - } - - #endregion - - #region GetObjectList(分页) - - /// - /// 获取分页数据 - /// - /// - /// - /// - /// - /// - /// - /// - /// - public virtual PagedList GetObjectList(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes) - { - return RepositoryBase.GetObjectList(where, orderBy, orderingType, pageIndex, pageCount, includes); - } - - /// - /// 获取分页数据 - /// - /// - /// 页码 - /// 每页数量 - /// 条件 - /// 排序字段 - /// 正序|倒叙 - /// - /// - public virtual async Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes) - { - return await RepositoryBase.GetObjectListAsync(where, orderBy, orderingType, pageIndex, pageCount, includes); - } - - /// - /// 获取分页数据 - /// - /// 页码 - /// 每页数量 - /// 条件 - /// 排序字段 eg.(xxx desc, bbb aec),默认升序 - /// - /// - public virtual async Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, string orderBy, params string[] includes) - { - return await RepositoryBase.GetObjectListAsync(where, orderBy, pageIndex, pageCount, includes); - } - - public virtual PagedList GetObjectList(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - return RepositoryBase.GetObjectList(where, orderBy, orderingType, pageIndex, pageCount, includesNavigationPropertyPathFunc); - } - - /// - /// 获取分页数据 - /// - /// 页码 - /// 每页数量 - /// 条件 - /// 排序字段 eg.(xxx desc, bbb aec),默认升序 - /// - /// - public virtual async Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, string orderBy, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - return await RepositoryBase.GetObjectListAsync(where, orderBy, pageIndex, pageCount, includesNavigationPropertyPathFunc); - } - - - /// - /// 获取分页数据 - /// - /// - /// 页码 - /// 每页数量 - /// 条件 - /// 排序字段 - /// 正序|倒叙 - /// - /// - public virtual async Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - return await RepositoryBase.GetObjectListAsync(where, orderBy, orderingType, pageIndex, pageCount, includesNavigationPropertyPathFunc); - } - - - #endregion - - #region GetCount - - public virtual int GetCount(Expression> where, params string[] includes) - { - return RepositoryBase.ObjectCount(where, includes); - } - - public virtual async Task GetCountAsync(Expression> where, params string[] includes) - { - return await RepositoryBase.ObjectCountAsync(where, includes); - } - - public virtual int GetCount(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - return RepositoryBase.ObjectCount(where, includesNavigationPropertyPathFunc); - } - - public virtual async Task GetCountAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - return await RepositoryBase.ObjectCountAsync(where, includesNavigationPropertyPathFunc); - } - - #endregion - - #region GetSum - - public virtual decimal GetSum(Expression> where, Expression> sum, params string[] includes) - { - return RepositoryBase.GetSum(where, sum, includes); - } - - - public virtual async Task GetSumAsync(Expression> where, Expression> sum, params string[] includes) - { - return await RepositoryBase.GetSumAsync(where, sum, includes); - } - - public virtual async Task GetSumAsync(Expression> where, Expression> sum, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) - { - return await RepositoryBase.GetSumAsync(where, sum, includesNavigationPropertyPathFunc); - } - - - #endregion - - #region SaveObject & SaveChanges - - public virtual void SaveObject(T obj) - { - if (RepositoryBase.BaseDB.ManualDetectChangeObject) - { - TryDetectChange(obj); - } - - RepositoryBase.Save(obj); - - AfterSaveObject?.Invoke(this.BaseData, obj); - } - - public virtual async Task SaveObjectAsync(T obj) - { - if (RepositoryBase.BaseDB.ManualDetectChangeObject) - { - TryDetectChange(obj); - } - - await RepositoryBase.SaveAsync(obj).ConfigureAwait(false); - - AfterSaveObject?.Invoke(this.BaseData, obj); - } - - public virtual async Task SaveObjectListAsync(IEnumerable objs) - { - await RepositoryBase.SaveObjectListAsync(objs); - - foreach (var item in objs) - { - AfterSaveObject?.Invoke(this.BaseData, item); - } - } - - public virtual void SaveChanges() - { - RepositoryBase.SaveChanges(); - - AfterSaveChanges?.Invoke(this.BaseData); - } - - public virtual async Task SaveChangesAsync() - { - await RepositoryBase.SaveChangesAsync().ConfigureAwait(false); - - AfterSaveChanges?.Invoke(this.BaseData); - } - - - #endregion - - #region Delete - - - public virtual void DeleteObject(Expression> predicate) - { - T obj = GetObject(predicate); - - DeleteObject(obj); - - AfterDeleteObject?.Invoke(this.BaseData, obj); - } - - public virtual async Task DeleteObjectAsync(Expression> predicate) - { - T obj = await GetObjectAsync(predicate); - - await DeleteObjectAsync(obj); - - AfterDeleteObject?.Invoke(this.BaseData, obj); - } - - - public virtual void DeleteObject(T obj) - { - RepositoryBase.Delete(obj); - - AfterDeleteObject?.Invoke(this.BaseData, obj); - } - - public virtual async Task DeleteObjectAsync(T obj) - { - await RepositoryBase.DeleteAsync(obj, true); - - AfterDeleteObject?.Invoke(this.BaseData, obj); - } - - public virtual void DeleteAll(IEnumerable objects) - { - var list = objects.ToList(); - for (int i = 0; i < list.Count; i++) - { - DeleteObject(list[i]); - } - } - - public virtual async Task DeleteAllAsync(Expression> where, Action deleteItemAction = null, bool softDelete = false) - { - var list = await GetFullListAsync(where); - await RepositoryBase.DeleteAllAsync(list, deleteItemAction, softDelete); - - foreach (var obj in list) - { - AfterDeleteObject?.Invoke(this.BaseData, obj); - } - } - - public virtual async Task DeleteAllAsync(IEnumerable objects, Action deleteItemAction = null, bool softDelete = false) - { - await RepositoryBase.DeleteAllAsync(objects, deleteItemAction, softDelete); - - foreach (var obj in objects) - { - AfterDeleteObject?.Invoke(this.BaseData, obj); - } - } - - #endregion - - #region Transaction - - /// - /// 开启事务 - /// - /// - public async Task BeginTransactionAsync() - { - await RepositoryBase.BeginTransactionAsync(); - } - - /// - /// 开启事务, 此方法回自动提交事务,失败则回滚 - /// - /// - public async Task BeginTransactionAsync(Action action) - { - await RepositoryBase.BeginTransactionAsync(); - try - { - action(); - CommitTransaction(); - } - catch (Exception) - { - RollbackTransaction(); - throw; - } - } - - /// - /// 开启事务 - /// - /// - public void BeginTransaction() - { - RepositoryBase.BeginTransaction(); - } - - /// - /// 开启事务 - /// - /// - public void BeginTransaction(Action body, Action rollbackAction = null) - { - BeginTransaction(); - try - { - body(); - CommitTransaction(); - } - catch (Exception ex) - { - RollbackTransaction(); - rollbackAction?.Invoke(ex); - throw; - } - } - - - /// - /// 开启事务, 此方法回自动提交事务,失败则回滚 - /// - /// - public async Task BeginTransactionAsync(Func body, Action rollbackAction = null) - { - await BeginTransactionAsync(); - try - { - await body(); - CommitTransaction(); - } - catch (Exception ex) - { - RollbackTransaction(); - rollbackAction?.Invoke(ex); - throw ex; - } - } - - - /// - /// 开启事务, 此方法会自动提交事务,失败则回滚 - /// - /// - /// 处理一个异常并抛出自定义的异常 - /// - public async Task BeginTransactionAsync(Func body, Func rollbackAction) - { - await BeginTransactionAsync(); - try - { - await body(); - CommitTransaction(); - } - catch (Exception ex) - { - RollbackTransaction(); - throw rollbackAction?.Invoke(ex) ?? ex; - } - } - - - /// - /// 开启事务, 此方法会自动提交事务,失败则回滚 - /// - /// - /// 处理一个异常并抛出自定义的异常 - /// - public async Task BeginTransactionAsync(Func bodyAsync, Func> rollbackActionAsync) - { - await BeginTransactionAsync(); - try - { - await bodyAsync(); - CommitTransaction(); - } - catch (Exception ex) - { - RollbackTransaction(); - throw await rollbackActionAsync?.Invoke(ex) ?? ex; - } - } - - - /// - /// 回滚事务 - /// - /// - public void RollbackTransaction() - { - RepositoryBase.RollbackTransaction(); - } - - /// - /// 提交事务 - /// - /// - public void CommitTransaction() - { - RepositoryBase.CommitTransaction(); - } - - #endregion - - #region Mapping - - /// - /// 使用 Mapper.Map<TDto>(entity) 快速返回 - /// - /// - /// - /// - public TDto Mapping(T entity) - { - return Mapper.Map(entity); - } - - /// - /// 将 PageList 转为 DTO 对象 - /// - /// - /// - /// - public PagedList Mapping(PagedList pagedList) - { - var dtoList = pagedList.Select(Mapper.Map).ToList(); - return new PagedList(dtoList, pagedList.PageIndex, pagedList.PageCount, pagedList.TotalCount, pagedList.SkipCount); - } - - #endregion - - #region Tenant - - /// - /// 强制设置租户信息 - /// - /// - /// - public bool SetTenantInfo(RequestTenantInfo requestTenantInfo) - { - if (this.BaseData.BaseDB.BaseDataContext is ISenparcEntitiesDbContext senparcDB) - { - senparcDB.TenantInfo = requestTenantInfo; - return true; - } - return false; - } - - - #endregion - } +using AutoMapper; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using Senparc.CO2NET; +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Repository; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.EntityFrameworkCore.Query; +using Senparc.Ncf.Core.Models.DataBaseModel; +using Senparc.Ncf.Core.MultiTenant; + +namespace Senparc.Ncf.Service +{ + public class ServiceBase : ServiceDataBase, IServiceBase where T : class, IEntityBase// global::System.Data.Objects.DataClasses.EntityObject, new() + { + public IMapper Mapper { get; set; } //TODO: add in to Wapper + + public IRepositoryBase RepositoryBase { get; set; } + protected IServiceProvider _serviceProvider => base.ServiceProvider; + + public ServiceBase(IRepositoryBase repo, IServiceProvider serviceProvider) + : base(repo, serviceProvider) + { + //_serviceProvider = serviceProvider; + RepositoryBase = repo; + Mapper = _serviceProvider.GetService();//Make sure there is a value in the Mapper + } + + #region Insert & DetectChange + + /// + /// Force the entity to Modified state + /// + /// + public virtual void TryDetectChange(T obj) + { + if (!IsInsert(obj)) + { + RepositoryBase.BaseDB.BaseDataContext.Entry(obj).State = EntityState.Modified; + } + } + + public virtual bool IsInsert(T obj) + { + return RepositoryBase.IsInsert(obj); + } + + #endregion + + + #region GetObject + + public virtual T GetObject(Expression> where, params string[] includes) + { + return RepositoryBase.GetFirstOrDefaultObject(where, includes); + } + + public virtual async Task GetObjectAsync(Expression> where, params string[] includes) + { + return await RepositoryBase.GetFirstOrDefaultObjectAsync(where, includes); + } + + public virtual T GetObject(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + return RepositoryBase.GetFirstOrDefaultObject(where, includesNavigationPropertyPathFunc); + } + + public virtual async Task GetObjectAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + return await RepositoryBase.GetFirstOrDefaultObjectAsync(where, includesNavigationPropertyPathFunc); + } + + public virtual T GetObject(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes) + { + return RepositoryBase.GetFirstOrDefaultObject(where, orderBy, orderingType, includes); + } + + public virtual async Task GetObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes) + { + return await RepositoryBase.GetFirstOrDefaultObjectAsync(where, orderBy, orderingType, includes); + } + + public virtual T GetObject(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + return RepositoryBase.GetFirstOrDefaultObject(where, orderBy, orderingType, includesNavigationPropertyPathFunc); + } + + public virtual async Task GetObjectAsync(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + return await RepositoryBase.GetFirstOrDefaultObjectAsync(where, orderBy, orderingType, includesNavigationPropertyPathFunc); + } + + #endregion + + #region GetFullList + + public virtual PagedList GetFullList(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes) + { + return this.GetObjectList(0, 0, where, orderBy, orderingType, includes); + } + + /// + /// Get all data + /// + /// + /// + /// + /// + public virtual async Task> GetFullListAsync(Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes) + { + return await this.GetObjectListAsync(0, 0, where, orderBy, orderingType, includes); + } + + /// + /// Get all data + /// + /// + /// + /// xxx desc, yyy asc + /// + /// + public virtual async Task> GetFullListAsync(Expression> where, string orderField = null, params string[] includes) + { + return await RepositoryBase.GetObjectListAsync(where, orderField, 0, 0, includes); + } + + public virtual PagedList GetFullList(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + return this.GetObjectList(0, 0, where, orderBy, orderingType, includesNavigationPropertyPathFunc); + } + + /// + /// Get all data + /// + /// + /// + /// + /// + /// + /// + /// + public virtual async Task> GetFullListAsync(Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + return await RepositoryBase.GetObjectListAsync(where, orderBy, orderingType, 0, 0, includesNavigationPropertyPathFunc); + } + + /// + /// Get all data + /// + /// + /// + /// + /// xxx desc, yyy asc + /// + /// + public virtual async Task> GetFullListAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc, string orderField = null) + { + return await RepositoryBase.GetObjectListAsync(where, orderField, 0, 0, includesNavigationPropertyPathFunc); + } + + #endregion + + #region GetObjectList(分页) + + /// + /// Get paging data + /// + /// + /// + /// + /// + /// + /// + /// + /// + public virtual PagedList GetObjectList(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes) + { + return RepositoryBase.GetObjectList(where, orderBy, orderingType, pageIndex, pageCount, includes); + } + + /// + /// Get paging data + /// + /// + /// Page number + /// Number per page + /// Condition + /// Sort field + /// Forward sequence | Flashback + /// + /// + public virtual async Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType, params string[] includes) + { + return await RepositoryBase.GetObjectListAsync(where, orderBy, orderingType, pageIndex, pageCount, includes); + } + + /// + /// Get paging data + /// + /// Page number + /// Number per page + /// Condition + /// Sort field eg.(xxx desc, bbb aec), default ascending order + /// + /// + public virtual async Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, string orderBy, params string[] includes) + { + return await RepositoryBase.GetObjectListAsync(where, orderBy, pageIndex, pageCount, includes); + } + + public virtual PagedList GetObjectList(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + return RepositoryBase.GetObjectList(where, orderBy, orderingType, pageIndex, pageCount, includesNavigationPropertyPathFunc); + } + + /// + /// Get paging data + /// + /// Page number + /// Number per page + /// Condition + /// Sort field eg.(xxx desc, bbb aec), default ascending order + /// + /// + public virtual async Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, string orderBy, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + return await RepositoryBase.GetObjectListAsync(where, orderBy, pageIndex, pageCount, includesNavigationPropertyPathFunc); + } + + + /// + /// Get paging data + /// + /// + /// Page number + /// Number per page + /// Condition + /// Sort field + /// Forward sequence | Flashback + /// + /// + public virtual async Task> GetObjectListAsync(int pageIndex, int pageCount, Expression> where, Expression> orderBy, OrderingType orderingType, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + return await RepositoryBase.GetObjectListAsync(where, orderBy, orderingType, pageIndex, pageCount, includesNavigationPropertyPathFunc); + } + + + #endregion + + #region GetCount + + public virtual int GetCount(Expression> where, params string[] includes) + { + return RepositoryBase.ObjectCount(where, includes); + } + + public virtual async Task GetCountAsync(Expression> where, params string[] includes) + { + return await RepositoryBase.ObjectCountAsync(where, includes); + } + + public virtual int GetCount(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + return RepositoryBase.ObjectCount(where, includesNavigationPropertyPathFunc); + } + + public virtual async Task GetCountAsync(Expression> where, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + return await RepositoryBase.ObjectCountAsync(where, includesNavigationPropertyPathFunc); + } + + #endregion + + #region GetSum + + public virtual decimal GetSum(Expression> where, Expression> sum, params string[] includes) + { + return RepositoryBase.GetSum(where, sum, includes); + } + + + public virtual async Task GetSumAsync(Expression> where, Expression> sum, params string[] includes) + { + return await RepositoryBase.GetSumAsync(where, sum, includes); + } + + public virtual async Task GetSumAsync(Expression> where, Expression> sum, Expression, IIncludableQueryable>> includesNavigationPropertyPathFunc) + { + return await RepositoryBase.GetSumAsync(where, sum, includesNavigationPropertyPathFunc); + } + + + #endregion + + #region SaveObject & SaveChanges + + public virtual void SaveObject(T obj) + { + if (RepositoryBase.BaseDB.ManualDetectChangeObject) + { + TryDetectChange(obj); + } + + RepositoryBase.Save(obj); + + AfterSaveObject?.Invoke(this.BaseData, obj); + } + + public virtual async Task SaveObjectAsync(T obj) + { + if (RepositoryBase.BaseDB.ManualDetectChangeObject) + { + TryDetectChange(obj); + } + + await RepositoryBase.SaveAsync(obj).ConfigureAwait(false); + + AfterSaveObject?.Invoke(this.BaseData, obj); + } + + public virtual async Task SaveObjectListAsync(IEnumerable objs) + { + await RepositoryBase.SaveObjectListAsync(objs); + + foreach (var item in objs) + { + AfterSaveObject?.Invoke(this.BaseData, item); + } + } + + public virtual void SaveChanges() + { + RepositoryBase.SaveChanges(); + + AfterSaveChanges?.Invoke(this.BaseData); + } + + public virtual async Task SaveChangesAsync() + { + await RepositoryBase.SaveChangesAsync().ConfigureAwait(false); + + AfterSaveChanges?.Invoke(this.BaseData); + } + + + #endregion + + #region Delete + + + public virtual void DeleteObject(Expression> predicate) + { + T obj = GetObject(predicate); + + DeleteObject(obj); + + AfterDeleteObject?.Invoke(this.BaseData, obj); + } + + public virtual async Task DeleteObjectAsync(Expression> predicate) + { + T obj = await GetObjectAsync(predicate); + + await DeleteObjectAsync(obj); + + AfterDeleteObject?.Invoke(this.BaseData, obj); + } + + + public virtual void DeleteObject(T obj) + { + RepositoryBase.Delete(obj); + + AfterDeleteObject?.Invoke(this.BaseData, obj); + } + + public virtual async Task DeleteObjectAsync(T obj) + { + await RepositoryBase.DeleteAsync(obj, true); + + AfterDeleteObject?.Invoke(this.BaseData, obj); + } + + public virtual void DeleteAll(IEnumerable objects) + { + var list = objects.ToList(); + for (int i = 0; i < list.Count; i++) + { + DeleteObject(list[i]); + } + } + + public virtual async Task DeleteAllAsync(Expression> where, Action deleteItemAction = null, bool softDelete = false) + { + var list = await GetFullListAsync(where); + await RepositoryBase.DeleteAllAsync(list, deleteItemAction, softDelete); + + foreach (var obj in list) + { + AfterDeleteObject?.Invoke(this.BaseData, obj); + } + } + + public virtual async Task DeleteAllAsync(IEnumerable objects, Action deleteItemAction = null, bool softDelete = false) + { + await RepositoryBase.DeleteAllAsync(objects, deleteItemAction, softDelete); + + foreach (var obj in objects) + { + AfterDeleteObject?.Invoke(this.BaseData, obj); + } + } + + #endregion + + #region Transaction + + /// + /// start transaction + /// + /// + public async Task BeginTransactionAsync() + { + await RepositoryBase.BeginTransactionAsync(); + } + + /// + /// Start the transaction. This method will automatically commit the transaction. If it fails, it will be rolled back. + /// + /// + public async Task BeginTransactionAsync(Action action) + { + await RepositoryBase.BeginTransactionAsync(); + try + { + action(); + CommitTransaction(); + } + catch (Exception) + { + RollbackTransaction(); + throw; + } + } + + /// + /// start transaction + /// + /// + public void BeginTransaction() + { + RepositoryBase.BeginTransaction(); + } + + /// + /// start transaction + /// + /// + public void BeginTransaction(Action body, Action rollbackAction = null) + { + BeginTransaction(); + try + { + body(); + CommitTransaction(); + } + catch (Exception ex) + { + RollbackTransaction(); + rollbackAction?.Invoke(ex); + throw; + } + } + + + /// + /// Start the transaction. This method will automatically commit the transaction. If it fails, it will be rolled back. + /// + /// + public async Task BeginTransactionAsync(Func body, Action rollbackAction = null) + { + await BeginTransactionAsync(); + try + { + await body(); + CommitTransaction(); + } + catch (Exception ex) + { + RollbackTransaction(); + rollbackAction?.Invoke(ex); + throw ex; + } + } + + + /// + /// Start the transaction. This method will automatically commit the transaction and roll it back if it fails. + /// + /// + /// Handle an exception and throw a custom exception + /// + public async Task BeginTransactionAsync(Func body, Func rollbackAction) + { + await BeginTransactionAsync(); + try + { + await body(); + CommitTransaction(); + } + catch (Exception ex) + { + RollbackTransaction(); + throw rollbackAction?.Invoke(ex) ?? ex; + } + } + + + /// + /// Start the transaction. This method will automatically commit the transaction and roll it back if it fails. + /// + /// + /// Handle an exception and throw a custom exception + /// + public async Task BeginTransactionAsync(Func bodyAsync, Func> rollbackActionAsync) + { + await BeginTransactionAsync(); + try + { + await bodyAsync(); + CommitTransaction(); + } + catch (Exception ex) + { + RollbackTransaction(); + throw await rollbackActionAsync?.Invoke(ex) ?? ex; + } + } + + + /// + /// rollback transaction + /// + /// + public void RollbackTransaction() + { + RepositoryBase.RollbackTransaction(); + } + + /// + /// Commit transaction + /// + /// + public void CommitTransaction() + { + RepositoryBase.CommitTransaction(); + } + + #endregion + + #region Mapping + + /// + /// Use Mapper.Map<TDto>(entity) to return quickly + /// + /// + /// + /// + public TDto Mapping(T entity) + { + return Mapper.Map(entity); + } + + /// + /// Convert PageList to DTO object + /// + /// + /// + /// + public PagedList Mapping(PagedList pagedList) + { + var dtoList = pagedList.Select(Mapper.Map).ToList(); + return new PagedList(dtoList, pagedList.PageIndex, pagedList.PageCount, pagedList.TotalCount, pagedList.SkipCount); + } + + #endregion + + #region Tenant + + /// + /// Force tenant information to be set + /// + /// + /// + public bool SetTenantInfo(RequestTenantInfo requestTenantInfo) + { + if (this.BaseData.BaseDB.BaseDataContext is ISenparcEntitiesDbContext senparcDB) + { + senparcDB.TenantInfo = requestTenantInfo; + return true; + } + return false; + } + + + #endregion + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Service/ServiceBase/ServiceDataBase.cs b/src/Basic/Senparc.Ncf.Service/ServiceBase/ServiceDataBase.cs index d779940d7..df6138fd7 100644 --- a/src/Basic/Senparc.Ncf.Service/ServiceBase/ServiceDataBase.cs +++ b/src/Basic/Senparc.Ncf.Service/ServiceBase/ServiceDataBase.cs @@ -1,65 +1,65 @@ -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Senparc.Ncf.Repository; - -namespace Senparc.Ncf.Service -{ - public interface IServiceDataBase - { - IServiceProvider ServiceProvider { get; set; } - IDataBase BaseData { get; set; } - void CloseConnection(); - - T GetService(); - } - - - public class ServiceDataBase : IServiceDataBase - { - #region 保存或删除后的操作,将影响全局 - - /// - /// 在指定对象保存后执行(无论是否成功),将影响全局所有保存过程 - /// - public static Action? AfterSaveObject { get; set; } - - /// - /// 在指定对象删除后执行(无论是否成功),将影响全局所有保存过程 - /// - public static Action? AfterDeleteObject { get; set; } - - /// - /// 在所有对象保存后执行(无论是否成功),将影响全局所有保存过程 - /// - public static Action? AfterSaveChanges { get; set; } - - #endregion - - - public IServiceProvider ServiceProvider { get; set; } - public IDataBase BaseData { get; set; } - - public ServiceDataBase(IDataBase baseData,IServiceProvider serviceProvider=null) - { - BaseData = baseData; - ServiceProvider = serviceProvider; - } - - public virtual void CloseConnection() - { - BaseData.CloseConnection(); - } - - public T GetService() - { - return ServiceProvider.GetService(); - } - - public T GetRequiredService() - { - return ServiceProvider.GetRequiredService(); - } - - } +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Senparc.Ncf.Repository; + +namespace Senparc.Ncf.Service +{ + public interface IServiceDataBase + { + IServiceProvider ServiceProvider { get; set; } + IDataBase BaseData { get; set; } + void CloseConnection(); + + T GetService(); + } + + + public class ServiceDataBase : IServiceDataBase + { + #region 保存或删除后的操作,将影响全局 + + /// + /// Executed after the specified object is saved (whether successful or not), it will affect all save processes globally. + /// + public static Action? AfterSaveObject { get; set; } + + /// + /// Executed after the specified object is deleted (whether successful or not), it will affect all global save processes + /// + public static Action? AfterDeleteObject { get; set; } + + /// + /// Executed after all objects are saved (whether successful or not), it will affect all save processes globally + /// + public static Action? AfterSaveChanges { get; set; } + + #endregion + + + public IServiceProvider ServiceProvider { get; set; } + public IDataBase BaseData { get; set; } + + public ServiceDataBase(IDataBase baseData,IServiceProvider serviceProvider=null) + { + BaseData = baseData; + ServiceProvider = serviceProvider; + } + + public virtual void CloseConnection() + { + BaseData.CloseConnection(); + } + + public T GetService() + { + return ServiceProvider.GetService(); + } + + public T GetRequiredService() + { + return ServiceProvider.GetRequiredService(); + } + + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Service/SignalrHubs/SignalrTicker.cs b/src/Basic/Senparc.Ncf.Service/SignalrHubs/SignalrTicker.cs index 7cca72175..5ae8cea5e 100644 --- a/src/Basic/Senparc.Ncf.Service/SignalrHubs/SignalrTicker.cs +++ b/src/Basic/Senparc.Ncf.Service/SignalrHubs/SignalrTicker.cs @@ -1,84 +1,84 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.SignalR; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Service.SignalrHubs -{ - /// - /// 记录 SignalR 全局上下文的类,可轮询执行操作 - /// - public class SignalrTicker - where THub : Hub - { - private static /*readonly*/ SignalrTicker _instance; - - private readonly IHubContext m_context; - - public IHubContext GlobalContext - { - get { return m_context; } - } - - /// - /// 静态实例对象 - /// - public static SignalrTicker Instance(HttpContext httpContext) - { - if (_instance == null) - { - if (httpContext!=null) - { - var hubContext = httpContext.RequestServices.GetRequiredService>(); - _instance = new SignalrTicker(hubContext); - } - } - return _instance; - } - - /// - /// 轮询间隔时间(毫秒),默认值:500 - /// - public int SleepMillionSeconds { get; set; } = 500; - - /// - /// 参数:SignalrTicker,当前轮询的次数 - /// - public Action, int> SenderAction { get; set; } - - /// - /// 构造函数 - /// - /// - private SignalrTicker(IHubContext context) - { - m_context = context; - //这里不能直接调用Sender,因为Sender是一个不退出的“死循环”,否则这个构造函数将不会退出。 - //其他的流程也将不会再执行下去了。所以要采用异步的方式。 - Task.Run(() => Sender()); - } - - /// - /// 注册 SignalrTicker,激活静态变量 - /// - public static void Register() - { - //可以写一些初始化 - } - - public void Sender() - { - int count = 0; - while (true) - { - SenderAction?.Invoke(this, count); - count++; - Thread.Sleep(SleepMillionSeconds); - } - } - } -} +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Service.SignalrHubs +{ + /// + /// Class that records the SignalR global context and can be polled to perform operations + /// + public class SignalrTicker + where THub : Hub + { + private static /*readonly*/ SignalrTicker _instance; + + private readonly IHubContext m_context; + + public IHubContext GlobalContext + { + get { return m_context; } + } + + /// + /// static instance object + /// + public static SignalrTicker Instance(HttpContext httpContext) + { + if (_instance == null) + { + if (httpContext!=null) + { + var hubContext = httpContext.RequestServices.GetRequiredService>(); + _instance = new SignalrTicker(hubContext); + } + } + return _instance; + } + + /// + /// Polling interval (milliseconds), default value: 500 + /// + public int SleepMillionSeconds { get; set; } = 500; + + /// + /// Parameter: SignalrTicker, the current number of polls + /// + public Action, int> SenderAction { get; set; } + + /// + ///Constructor + /// + /// + private SignalrTicker(IHubContext context) + { + m_context = context; + //Sender cannot be called directly here because Sender is an "infinite loop" that does not exit, otherwise the constructor will not exit. + //Other processes will no longer be executed. So use an asynchronous approach. + Task.Run(() => Sender()); + } + + /// + ///Register SignalrTicker, activate static variables + /// + public static void Register() + { + //You can write some initialization + } + + public void Sender() + { + int count = 0; + while (true) + { + SenderAction?.Invoke(this, count); + count++; + Thread.Sleep(SleepMillionSeconds); + } + } + } +} diff --git a/src/Basic/Senparc.Ncf.Service/System/SysMenuService.cs b/src/Basic/Senparc.Ncf.Service/System/SysMenuService.cs index 95683127d..1692d7230 100644 --- a/src/Basic/Senparc.Ncf.Service/System/SysMenuService.cs +++ b/src/Basic/Senparc.Ncf.Service/System/SysMenuService.cs @@ -1,377 +1,377 @@ -using AutoMapper; -using Microsoft.AspNetCore.Mvc.Rendering; -using Microsoft.Extensions.Caching.Distributed; -using Senparc.Ncf.Repository; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.EntityFrameworkCore; -using Senparc.Ncf.Core.Models.DataBaseModel; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Core.Exceptions; -using AutoMapper.QueryableExtensions; -using Senparc.Ncf.Core.MultiTenant; -using Senparc.Ncf.Core.Config; -using Senparc.Ncf.Service; -using Microsoft.AspNetCore.Authentication; - -namespace Senparc.Ncf.Service -{ - /// - /// - /// - public class SysMenuService : ClientServiceBase - { - private readonly SysButtonService _sysButtonService; - private readonly IDistributedCache _distributedCache; - private readonly SysRoleService sysRoleService; - private readonly ClientRepositoryBase sysRolePermissionService; - private readonly SysRoleAdminUserInfoService sysRoleAdminUserInfoService; - public const string MenuCacheKey = "AllMenus"; - public const string MenuTreeCacheKey = "AllMenusTree"; - - //private readonly SenparcEntitiesBase _senparcEntities; - - public SysMenuService(ClientRepositoryBase repo, IServiceProvider serviceProvider, - SysRoleService sysRoleService, ClientRepositoryBase sysRolePermissionService, - SysRoleAdminUserInfoService sysRoleAdminUserInfoService, SysButtonService sysButtonService - ) : base(repo, serviceProvider) - { - _sysButtonService = sysButtonService; - _distributedCache = _serviceProvider.GetService(); - this.sysRoleService = sysRoleService; - this.sysRolePermissionService = sysRolePermissionService; - this.sysRoleAdminUserInfoService = sysRoleAdminUserInfoService; - //_senparcEntities = _serviceProvider.GetService(); - } - - public void SetTenantInfoForAllServices(RequestTenantInfo requestTenantInfo) - { - if (SiteConfig.SenparcCoreSetting.EnableMultiTenant) - { - base.SetTenantInfo(requestTenantInfo); - _sysButtonService.SetTenantInfo(requestTenantInfo); - sysRoleService.SetTenantInfo(requestTenantInfo); - (sysRolePermissionService.BaseDB.BaseDataContext as ISenparcEntitiesDbContext).TenantInfo = requestTenantInfo; - sysRoleAdminUserInfoService.SetTenantInfo(requestTenantInfo); - } - } - - /// - /// TODO...重建菜单角色缓存 - /// - /// - /// - public virtual async Task CreateOrUpdateAsync(SysMenuDto sysMenuDto) - { - SysMenu menu; - ICollection sysButtons = new List(); - bool isRepeat; - if (!string.IsNullOrEmpty(sysMenuDto.Id)) - { - menu = await GetObjectAsync(_ => _.Id == sysMenuDto.Id); - if (menu.IsLocked) - { - return menu;//TODO:需要给出提示 - } - - isRepeat = await this.GetObjectAsync(_ => _.ResourceCode == sysMenuDto.ResourceCode && _.Id != sysMenuDto.Id) != null; - - //isRepeat = await _serviceProvider.GetService().Set().AnyAsync(_ => _.ResourceCode == sysMenuDto.ResourceCode && _.Id != sysMenuDto.Id); - - menu.Update(sysMenuDto); - } - else - { - menu = new SysMenu(sysMenuDto); - - isRepeat = await this.GetObjectAsync(_ => _.ResourceCode == sysMenuDto.ResourceCode) != null; - - //isRepeat = await _serviceProvider.GetService().Set().AnyAsync(_ => _.ResourceCode == sysMenuDto.ResourceCode); - } - if (isRepeat && sysMenuDto.MenuType == MenuType.按钮) - { - throw new NcfExceptionBase($"ResourceCode:{sysMenuDto.ResourceCode}已重复"); - } - menu.ResourceCode = sysMenuDto.MenuType == MenuType.按钮 ? menu.ResourceCode : string.Empty; - await SaveObjectAsync(menu); - await GetMenuDtoByCacheAsync(true); - return menu; - } - - /// - /// TODO...重建菜单角色缓存 - /// - /// - /// - /// - public virtual async Task CreateOrUpdateAsync(SysMenuDto sysMenuDto, IEnumerable buttons) - { - SysMenu menu; - List sysButtons = new List(); - if (!string.IsNullOrEmpty(sysMenuDto.Id)) - { - menu = await GetObjectAsync(_ => _.Id == sysMenuDto.Id); - menu.Update(sysMenuDto); - } - else - { - menu = new SysMenu(sysMenuDto); - } - - IEnumerable modifySysButtons = buttons.Where(_ => !string.IsNullOrEmpty(_.Id)).Select(_ => _.Id); - IEnumerable updateBUttons; - if (modifySysButtons.Any()) - { - updateBUttons = await _sysButtonService.GetFullListAsync(_ => modifySysButtons.Contains(_.Id)); - } - else - { - updateBUttons = new List(); - } - foreach (var item in buttons) - { - if (string.IsNullOrEmpty(item.ButtonName)) - { - continue; - } - SysButton sysButton; - if (string.IsNullOrEmpty(item.Id)) - { - sysButton = new SysButton(item); - sysButton.MenuId = menu.Id; - } - else - { - sysButton = updateBUttons.FirstOrDefault(_ => _.Id == item.Id); - sysButton.Update(item); - } - sysButtons.Add(sysButton); - } - await BeginTransactionAsync(async () => - { - if (sysButtons.Any()) - { - await _sysButtonService.SaveObjectListAsync(sysButtons); - } - await SaveObjectAsync(menu); - }); - await GetMenuDtoByCacheAsync(true); - } - - /// - /// 获取缓存中的数据 - /// - /// - /// - public virtual async Task RemoveMenuAsync() - { - await _distributedCache.RemoveAsync(MenuCacheKey); - } - - /// - /// 获取缓存中的数据 TODO... - /// - /// - /// - public virtual async Task> GetMenuDtoByCacheAsync(bool isRefresh = false) - { - List selectListItems = null; - byte[] selectLiteItemBytes = await _distributedCache.GetAsync(MenuCacheKey); - if (selectLiteItemBytes == null || isRefresh) - { - List sysMenus = (await GetFullListAsync(_ => _.Visible).ConfigureAwait(false)).OrderByDescending(z => z.Sort).ToList(); - List sysButtons = (await _sysButtonService.GetFullListAsync(_ => true).ConfigureAwait(false)).OrderBy(z => z.Id).ToList(); - selectListItems = Mapper.Map>(sysMenus); - List buttons = _sysButtonService.Mapper.Map>(sysButtons); - selectListItems.AddRange(buttons); - string jsonStr = Newtonsoft.Json.JsonConvert.SerializeObject(selectListItems); - await _distributedCache.RemoveAsync(MenuCacheKey); - await _distributedCache.RemoveAsync(MenuTreeCacheKey); - await _distributedCache.SetAsync(MenuCacheKey, System.Text.Encoding.UTF8.GetBytes(jsonStr)); - await _distributedCache.SetStringAsync(MenuTreeCacheKey, Newtonsoft.Json.JsonConvert.SerializeObject(GetSysMenuTreesMainRecursive(selectListItems))); - } - else - { - selectListItems = Newtonsoft.Json.JsonConvert.DeserializeObject>(System.Text.Encoding.UTF8.GetString(selectLiteItemBytes)); - } - return selectListItems; - } - - public virtual IEnumerable GetSysMenuTreesMainRecursive(IEnumerable sysMenuTreeItems) - { - List sysMenuTrees = new List(); - GetSysMenuTreesRecursive(sysMenuTreeItems, sysMenuTrees, null); - return sysMenuTrees; - } - - - private void GetSysMenuTreesRecursive(IEnumerable sysMenuTreeItems, IList sysMenuTrees, string parentId) - { - foreach (var item in sysMenuTreeItems.Where(_ => _.ParentId == parentId && _.IsMenu)) - { - SysMenuTreeItemDto sysMenu = new SysMenuTreeItemDto() { MenuName = item.MenuName, Id = item.Id, IsMenu = item.IsMenu, Icon = item.Icon, Url = item.Url, Children = new List() }; - sysMenuTrees.Add(sysMenu); - GetSysMenuTreesRecursive(sysMenuTreeItems, sysMenu.Children, item.Id); - } - } - - /// - /// 获取缓存中的数据 - /// - /// - public async Task> GetMenuTreeDtoByCacheAsync() - { - IEnumerable sysMenuTreeItems = null;// - string jsonStr = await _distributedCache.GetStringAsync(MenuTreeCacheKey); - if (string.IsNullOrEmpty(jsonStr)) - { - await GetMenuDtoByCacheAsync(true); - } - jsonStr = await _distributedCache.GetStringAsync(MenuTreeCacheKey); - sysMenuTreeItems = Newtonsoft.Json.JsonConvert.DeserializeObject>(jsonStr); - return sysMenuTreeItems; - } - - /// - /// 初始化菜单及其权限 - /// - public async void Init(RequestTenantInfo requestTenantInfo, int adminUserInfoId) - { - this.SetTenantInfoForAllServices(requestTenantInfo); - - //设置 TenantId 前缀,避免不同租户之间的 ID 冲突 - string tenantId = null; - if (SiteConfig.SenparcCoreSetting.EnableMultiTenant) - { - requestTenantInfo ??= _serviceProvider.GetRequiredService(); - tenantId = $"{requestTenantInfo.Id}-"; - } - - IEnumerable sysMenus = new List() - { - new SysMenu(){ Id = tenantId+"1", MenuName = "系统管理", Url = null, Icon = "fa fa-cog", Visible = true, IsLocked = true, Sort = 300, MenuType = MenuType.菜单}, - -#region 管理员管理 - new SysMenu(){ Id = tenantId+"1.1", MenuName = "管理员管理", Url = "/Admin/AdminUserInfo/Index", Icon = "fa fa-user-secret", Visible = true, IsLocked = true, Sort = 300, ParentId = tenantId+"1", MenuType = MenuType.菜单}, - - new SysMenu() { Id =tenantId+"1.1.1", MenuName = "新增", Visible = true,ResourceCode = "admin-add", ParentId =tenantId+"1.1", MenuType = MenuType.按钮 }, - new SysMenu() { Id =tenantId+"1.1.2", MenuName = "查询", Visible = true,ResourceCode = "admin-search", ParentId =tenantId+ "1.1", MenuType = MenuType.按钮 }, - new SysMenu() { Id =tenantId+"1.1.3", MenuName = "分配角色", Visible = true,ResourceCode = "admin-grant", ParentId = tenantId+"1.1", MenuType = MenuType.按钮 }, - new SysMenu() { Id =tenantId+"1.1.4", MenuName = "编辑", Visible = true,ResourceCode = "admin-edit", ParentId = tenantId+"1.1", MenuType = MenuType.按钮 }, - new SysMenu() { Id =tenantId+"1.1.5", MenuName = "删除", Visible = true,ResourceCode = "admin-delete", ParentId = tenantId+"1.1", MenuType = MenuType.按钮 }, - new SysMenu() { Id =tenantId+"1.1.6", MenuName = "查看", Visible = true,ResourceCode = "admin-detail", ParentId = tenantId+"1.1", MenuType = MenuType.按钮 }, -#endregion - -#region 角色管理 - new SysMenu(){ Id = tenantId+"1.2", MenuName = "角色管理", Url = "/Admin/Role/Index", Icon = "fa fa-user", Visible = true, IsLocked = true, Sort = 275, ParentId =tenantId+ "1", MenuType = MenuType.菜单}, - - new SysMenu() { Id =tenantId+"1.2.1", MenuName = "新增", Visible = true, ResourceCode = "role-add", ParentId = tenantId+"1.2", MenuType = MenuType.按钮 }, - new SysMenu() { Id =tenantId+"1.2.2", MenuName = "查询", Visible = true,ResourceCode = "role-search", ParentId = tenantId+"1.2", MenuType = MenuType.按钮 }, - new SysMenu() { Id =tenantId+"1.2.3", MenuName = "授权", Visible = true,ResourceCode = "role-grant", ParentId = tenantId+"1.2", MenuType = MenuType.按钮 }, - new SysMenu() { Id =tenantId+"1.2.4", MenuName = "删除", Visible = true,ResourceCode = "role-delete", ParentId = tenantId+"1.2", MenuType = MenuType.按钮 }, - new SysMenu() { Id =tenantId+"1.2.5", MenuName = "编辑", Visible = true,ResourceCode = "role-edit", ParentId =tenantId+ "1.2", MenuType = MenuType.按钮 }, - new SysMenu() { Id =tenantId+"1.2.6", MenuName = "查看", Visible = true,ResourceCode = "role-detail", ParentId =tenantId+ "1.2", MenuType = MenuType.按钮 }, -#endregion - - new SysMenu(){ Id = tenantId+"1.3", MenuName = "菜单管理", Url = "/Admin/Menu/Index", Icon = "fa fa-bars", Visible = true, IsLocked = true, Sort = 250, ParentId =tenantId+ "1", MenuType = MenuType.菜单}, - - new SysMenu(){ Id = tenantId+"1.4", MenuName = "系统信息", Url = "/Admin/SystemConfig/Index", Icon = "fa fa-flag", Visible = true, IsLocked = true, Sort = 225, ParentId =tenantId+ "1", MenuType = MenuType.菜单}, - new SysMenu(){ Id = tenantId+"1.5", MenuName = "多租户信息", Url = "/Admin/TenantInfo/Index", Icon = "fa fa-group", Visible = true, IsLocked = true, Sort = 210, ParentId = tenantId+"1", MenuType = MenuType.菜单}, - - new SysMenu(){ Id =tenantId+"2", MenuName = "扩展模块", Url = null, Icon = "fa fa-cog", Visible = true, IsLocked = true, Sort = 200, MenuType = MenuType.菜单}, - new SysMenu(){ Id =tenantId+"2.1", MenuName = "模块管理", Url = "/Admin/XncfModule/Index", Icon = "fa fa-user-secret", Visible = true, IsLocked = true, Sort = 175, ParentId =tenantId+ "2", MenuType = MenuType.菜单}, - }; - - IEnumerable sysRoles = new List() - { - new SysRole() { Id = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, RoleName = "超级管理员", Enabled = true } - }; - - - IEnumerable sysPermissions = new List() - { - new SysRolePermission() { PermissionId = tenantId+"1", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, - new SysRolePermission() { PermissionId = tenantId+"1.1", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, - new SysRolePermission() { PermissionId = tenantId+"1.1.1", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, - new SysRolePermission() { PermissionId = tenantId+"1.1.2", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, - new SysRolePermission() { PermissionId = tenantId+"1.1.3", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, - new SysRolePermission() { PermissionId = tenantId+"1.1.4", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, - new SysRolePermission() { PermissionId = tenantId+"1.1.5", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true } , - new SysRolePermission() { PermissionId = tenantId+"1.1.6", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true } , - new SysRolePermission() { PermissionId = tenantId+"1.2", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, - new SysRolePermission() { PermissionId = tenantId+"1.2.1", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, - new SysRolePermission() { PermissionId = tenantId+"1.2.2", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, - new SysRolePermission() { PermissionId = tenantId+"1.2.3", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, - new SysRolePermission() { PermissionId = tenantId+"1.2.4", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, - new SysRolePermission() { PermissionId = tenantId+"1.2.5", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, - new SysRolePermission() { PermissionId = tenantId+"1.2.6", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, - new SysRolePermission() { PermissionId = tenantId+"1.3", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, - new SysRolePermission() { PermissionId = tenantId+"1.4", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, - new SysRolePermission() { PermissionId = tenantId+"1.5", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, - new SysRolePermission() { PermissionId = tenantId+"2", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, - new SysRolePermission() { PermissionId = tenantId+"2.1", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true } - }; - - IEnumerable sysRoleAdminUserInfos = new List() - { - new SysRoleAdminUserInfo() { RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, RoleId =tenantId+ "1", AccountId = adminUserInfoId } - }; - - try - { - //确保 DbContext 实例一致 - //Console.WriteLine("sysRoleService DbConte HashCode: " + sysRoleService.BaseData.BaseDB.BaseDataContext.GetHashCode()); - //Console.WriteLine("sysRolePermissionService DbConte HashCode: " + sysRolePermissionService.BaseDB.BaseDataContext.GetHashCode()); - //Console.WriteLine("sysRoleAdminUserInfoService DbConte HashCode: " + sysRoleAdminUserInfoService.BaseData.BaseDB.BaseDataContext.GetHashCode()); - //Console.WriteLine("sysMenus DbConte HashCode: " + this.BaseData.BaseDB.BaseDataContext.GetHashCode()); - - - await base.SaveObjectListAsync(sysMenus) - .ContinueWith(async t => - { - await sysRoleService.SaveObjectListAsync(sysRoles); - await sysRolePermissionService.SaveObjectListAsync(sysPermissions); - await sysRoleAdminUserInfoService.SaveObjectListAsync(sysRoleAdminUserInfos); - }); - - - - //_senparcEntities.Set().AddRange(sysRoles); - //_senparcEntities.Set().AddRange(sysMenus); - //_senparcEntities.Set().AddRange(sysPermissions); - //_senparcEntities.Set().AddRange(sysRoleAdminUserInfos); - //_senparcEntities.SaveChanges(); - } - catch (Exception ex) - { - - throw new Exception("初始化数据失败,原因:" + ex); - } - } - - /// - /// 获取数据库的菜单集合(线性) - /// - /// - public virtual async Task> GetMenuDtoByDbAsync() - { - List selectListItems = null; - IConfigurationProvider configurationProvider = _serviceProvider.GetService().ConfigurationProvider; - - var sysMenuList = await this.GetFullListAsync(z => true); - - selectListItems = this.Mapping(sysMenuList); //await _serviceProvider.GetService().Set().OrderByDescending(_ => _.AddTime).ProjectTo(configurationProvider).ToListAsync(); - - //List sysMenus = (await GetFullListAsync(_ => _.Visible).ConfigureAwait(false)).OrderByDescending(z => z.Sort).ToList(); - //List sysButtons = (await _sysButtonService.GetFullListAsync(_ => true).ConfigureAwait(false)).OrderBy(z => z.Id).ToList(); - //selectListItems = Mapper.Map>(sysMenus); - //List buttons = _sysButtonService.Mapper.Map>(sysButtons); - //selectListItems.AddRange(buttons); - return selectListItems; - } - } -} +using AutoMapper; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.Extensions.Caching.Distributed; +using Senparc.Ncf.Repository; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.EntityFrameworkCore; +using Senparc.Ncf.Core.Models.DataBaseModel; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Core.Exceptions; +using AutoMapper.QueryableExtensions; +using Senparc.Ncf.Core.MultiTenant; +using Senparc.Ncf.Core.Config; +using Senparc.Ncf.Service; +using Microsoft.AspNetCore.Authentication; + +namespace Senparc.Ncf.Service +{ + /// + /// + /// + public class SysMenuService : ClientServiceBase + { + private readonly SysButtonService _sysButtonService; + private readonly IDistributedCache _distributedCache; + private readonly SysRoleService sysRoleService; + private readonly ClientRepositoryBase sysRolePermissionService; + private readonly SysRoleAdminUserInfoService sysRoleAdminUserInfoService; + public const string MenuCacheKey = "AllMenus"; + public const string MenuTreeCacheKey = "AllMenusTree"; + + //private readonly SenparcEntitiesBase _senparcEntities; + + public SysMenuService(ClientRepositoryBase repo, IServiceProvider serviceProvider, + SysRoleService sysRoleService, ClientRepositoryBase sysRolePermissionService, + SysRoleAdminUserInfoService sysRoleAdminUserInfoService, SysButtonService sysButtonService + ) : base(repo, serviceProvider) + { + _sysButtonService = sysButtonService; + _distributedCache = _serviceProvider.GetService(); + this.sysRoleService = sysRoleService; + this.sysRolePermissionService = sysRolePermissionService; + this.sysRoleAdminUserInfoService = sysRoleAdminUserInfoService; + //_senparcEntities = _serviceProvider.GetService(); + } + + public void SetTenantInfoForAllServices(RequestTenantInfo requestTenantInfo) + { + if (SiteConfig.SenparcCoreSetting.EnableMultiTenant) + { + base.SetTenantInfo(requestTenantInfo); + _sysButtonService.SetTenantInfo(requestTenantInfo); + sysRoleService.SetTenantInfo(requestTenantInfo); + (sysRolePermissionService.BaseDB.BaseDataContext as ISenparcEntitiesDbContext).TenantInfo = requestTenantInfo; + sysRoleAdminUserInfoService.SetTenantInfo(requestTenantInfo); + } + } + + /// + ///TODO...Rebuild menu character cache + /// + /// + /// + public virtual async Task CreateOrUpdateAsync(SysMenuDto sysMenuDto) + { + SysMenu menu; + ICollection sysButtons = new List(); + bool isRepeat; + if (!string.IsNullOrEmpty(sysMenuDto.Id)) + { + menu = await GetObjectAsync(_ => _.Id == sysMenuDto.Id); + if (menu.IsLocked) + { + return menu;//TODO: Need to give hints + } + + isRepeat = await this.GetObjectAsync(_ => _.ResourceCode == sysMenuDto.ResourceCode && _.Id != sysMenuDto.Id) != null; + + //isRepeat = await _serviceProvider.GetService().Set().AnyAsync(_ => _.ResourceCode == sysMenuDto.ResourceCode && _.Id != sysMenuDto.Id); + + menu.Update(sysMenuDto); + } + else + { + menu = new SysMenu(sysMenuDto); + + isRepeat = await this.GetObjectAsync(_ => _.ResourceCode == sysMenuDto.ResourceCode) != null; + + //isRepeat = await _serviceProvider.GetService().Set().AnyAsync(_ => _.ResourceCode == sysMenuDto.ResourceCode); + } + if (isRepeat && sysMenuDto.MenuType == MenuType.按钮) + { + throw new NcfExceptionBase($"ResourceCode:{sysMenuDto.ResourceCode}已重复"); + } + menu.ResourceCode = sysMenuDto.MenuType == MenuType.按钮 ? menu.ResourceCode : string.Empty; + await SaveObjectAsync(menu); + await GetMenuDtoByCacheAsync(true); + return menu; + } + + /// + ///TODO...Rebuild menu character cache + /// + /// + /// + /// + public virtual async Task CreateOrUpdateAsync(SysMenuDto sysMenuDto, IEnumerable buttons) + { + SysMenu menu; + List sysButtons = new List(); + if (!string.IsNullOrEmpty(sysMenuDto.Id)) + { + menu = await GetObjectAsync(_ => _.Id == sysMenuDto.Id); + menu.Update(sysMenuDto); + } + else + { + menu = new SysMenu(sysMenuDto); + } + + IEnumerable modifySysButtons = buttons.Where(_ => !string.IsNullOrEmpty(_.Id)).Select(_ => _.Id); + IEnumerable updateBUttons; + if (modifySysButtons.Any()) + { + updateBUttons = await _sysButtonService.GetFullListAsync(_ => modifySysButtons.Contains(_.Id)); + } + else + { + updateBUttons = new List(); + } + foreach (var item in buttons) + { + if (string.IsNullOrEmpty(item.ButtonName)) + { + continue; + } + SysButton sysButton; + if (string.IsNullOrEmpty(item.Id)) + { + sysButton = new SysButton(item); + sysButton.MenuId = menu.Id; + } + else + { + sysButton = updateBUttons.FirstOrDefault(_ => _.Id == item.Id); + sysButton.Update(item); + } + sysButtons.Add(sysButton); + } + await BeginTransactionAsync(async () => + { + if (sysButtons.Any()) + { + await _sysButtonService.SaveObjectListAsync(sysButtons); + } + await SaveObjectAsync(menu); + }); + await GetMenuDtoByCacheAsync(true); + } + + /// + /// Get data from cache + /// + /// + /// + public virtual async Task RemoveMenuAsync() + { + await _distributedCache.RemoveAsync(MenuCacheKey); + } + + /// + /// Get the data in the cache TODO... + /// + /// + /// + public virtual async Task> GetMenuDtoByCacheAsync(bool isRefresh = false) + { + List selectListItems = null; + byte[] selectLiteItemBytes = await _distributedCache.GetAsync(MenuCacheKey); + if (selectLiteItemBytes == null || isRefresh) + { + List sysMenus = (await GetFullListAsync(_ => _.Visible).ConfigureAwait(false)).OrderByDescending(z => z.Sort).ToList(); + List sysButtons = (await _sysButtonService.GetFullListAsync(_ => true).ConfigureAwait(false)).OrderBy(z => z.Id).ToList(); + selectListItems = Mapper.Map>(sysMenus); + List buttons = _sysButtonService.Mapper.Map>(sysButtons); + selectListItems.AddRange(buttons); + string jsonStr = Newtonsoft.Json.JsonConvert.SerializeObject(selectListItems); + await _distributedCache.RemoveAsync(MenuCacheKey); + await _distributedCache.RemoveAsync(MenuTreeCacheKey); + await _distributedCache.SetAsync(MenuCacheKey, System.Text.Encoding.UTF8.GetBytes(jsonStr)); + await _distributedCache.SetStringAsync(MenuTreeCacheKey, Newtonsoft.Json.JsonConvert.SerializeObject(GetSysMenuTreesMainRecursive(selectListItems))); + } + else + { + selectListItems = Newtonsoft.Json.JsonConvert.DeserializeObject>(System.Text.Encoding.UTF8.GetString(selectLiteItemBytes)); + } + return selectListItems; + } + + public virtual IEnumerable GetSysMenuTreesMainRecursive(IEnumerable sysMenuTreeItems) + { + List sysMenuTrees = new List(); + GetSysMenuTreesRecursive(sysMenuTreeItems, sysMenuTrees, null); + return sysMenuTrees; + } + + + private void GetSysMenuTreesRecursive(IEnumerable sysMenuTreeItems, IList sysMenuTrees, string parentId) + { + foreach (var item in sysMenuTreeItems.Where(_ => _.ParentId == parentId && _.IsMenu)) + { + SysMenuTreeItemDto sysMenu = new SysMenuTreeItemDto() { MenuName = item.MenuName, Id = item.Id, IsMenu = item.IsMenu, Icon = item.Icon, Url = item.Url, Children = new List() }; + sysMenuTrees.Add(sysMenu); + GetSysMenuTreesRecursive(sysMenuTreeItems, sysMenu.Children, item.Id); + } + } + + /// + /// Get data from cache + /// + /// + public async Task> GetMenuTreeDtoByCacheAsync() + { + IEnumerable sysMenuTreeItems = null;// + string jsonStr = await _distributedCache.GetStringAsync(MenuTreeCacheKey); + if (string.IsNullOrEmpty(jsonStr)) + { + await GetMenuDtoByCacheAsync(true); + } + jsonStr = await _distributedCache.GetStringAsync(MenuTreeCacheKey); + sysMenuTreeItems = Newtonsoft.Json.JsonConvert.DeserializeObject>(jsonStr); + return sysMenuTreeItems; + } + + /// + ///Initialize menu and its permissions + /// + public async void Init(RequestTenantInfo requestTenantInfo, int adminUserInfoId) + { + this.SetTenantInfoForAllServices(requestTenantInfo); + + //Set TenantId prefix to avoid ID conflicts between different tenants + string tenantId = null; + if (SiteConfig.SenparcCoreSetting.EnableMultiTenant) + { + requestTenantInfo ??= _serviceProvider.GetRequiredService(); + tenantId = $"{requestTenantInfo.Id}-"; + } + + IEnumerable sysMenus = new List() + { + new SysMenu(){ Id = tenantId+"1", MenuName = "系统管理", Url = null, Icon = "fa fa-cog", Visible = true, IsLocked = true, Sort = 300, MenuType = MenuType.菜单}, + +#region 管理员管理 + new SysMenu(){ Id = tenantId+"1.1", MenuName = "管理员管理", Url = "/Admin/AdminUserInfo/Index", Icon = "fa fa-user-secret", Visible = true, IsLocked = true, Sort = 300, ParentId = tenantId+"1", MenuType = MenuType.菜单}, + + new SysMenu() { Id =tenantId+"1.1.1", MenuName = "新增", Visible = true,ResourceCode = "admin-add", ParentId =tenantId+"1.1", MenuType = MenuType.按钮 }, + new SysMenu() { Id =tenantId+"1.1.2", MenuName = "查询", Visible = true,ResourceCode = "admin-search", ParentId =tenantId+ "1.1", MenuType = MenuType.按钮 }, + new SysMenu() { Id =tenantId+"1.1.3", MenuName = "分配角色", Visible = true,ResourceCode = "admin-grant", ParentId = tenantId+"1.1", MenuType = MenuType.按钮 }, + new SysMenu() { Id =tenantId+"1.1.4", MenuName = "编辑", Visible = true,ResourceCode = "admin-edit", ParentId = tenantId+"1.1", MenuType = MenuType.按钮 }, + new SysMenu() { Id =tenantId+"1.1.5", MenuName = "删除", Visible = true,ResourceCode = "admin-delete", ParentId = tenantId+"1.1", MenuType = MenuType.按钮 }, + new SysMenu() { Id =tenantId+"1.1.6", MenuName = "查看", Visible = true,ResourceCode = "admin-detail", ParentId = tenantId+"1.1", MenuType = MenuType.按钮 }, +#endregion + +#region 角色管理 + new SysMenu(){ Id = tenantId+"1.2", MenuName = "角色管理", Url = "/Admin/Role/Index", Icon = "fa fa-user", Visible = true, IsLocked = true, Sort = 275, ParentId =tenantId+ "1", MenuType = MenuType.菜单}, + + new SysMenu() { Id =tenantId+"1.2.1", MenuName = "新增", Visible = true, ResourceCode = "role-add", ParentId = tenantId+"1.2", MenuType = MenuType.按钮 }, + new SysMenu() { Id =tenantId+"1.2.2", MenuName = "查询", Visible = true,ResourceCode = "role-search", ParentId = tenantId+"1.2", MenuType = MenuType.按钮 }, + new SysMenu() { Id =tenantId+"1.2.3", MenuName = "授权", Visible = true,ResourceCode = "role-grant", ParentId = tenantId+"1.2", MenuType = MenuType.按钮 }, + new SysMenu() { Id =tenantId+"1.2.4", MenuName = "删除", Visible = true,ResourceCode = "role-delete", ParentId = tenantId+"1.2", MenuType = MenuType.按钮 }, + new SysMenu() { Id =tenantId+"1.2.5", MenuName = "编辑", Visible = true,ResourceCode = "role-edit", ParentId =tenantId+ "1.2", MenuType = MenuType.按钮 }, + new SysMenu() { Id =tenantId+"1.2.6", MenuName = "查看", Visible = true,ResourceCode = "role-detail", ParentId =tenantId+ "1.2", MenuType = MenuType.按钮 }, +#endregion + + new SysMenu(){ Id = tenantId+"1.3", MenuName = "菜单管理", Url = "/Admin/Menu/Index", Icon = "fa fa-bars", Visible = true, IsLocked = true, Sort = 250, ParentId =tenantId+ "1", MenuType = MenuType.菜单}, + + new SysMenu(){ Id = tenantId+"1.4", MenuName = "系统信息", Url = "/Admin/SystemConfig/Index", Icon = "fa fa-flag", Visible = true, IsLocked = true, Sort = 225, ParentId =tenantId+ "1", MenuType = MenuType.菜单}, + new SysMenu(){ Id = tenantId+"1.5", MenuName = "多租户信息", Url = "/Admin/TenantInfo/Index", Icon = "fa fa-group", Visible = true, IsLocked = true, Sort = 210, ParentId = tenantId+"1", MenuType = MenuType.菜单}, + + new SysMenu(){ Id =tenantId+"2", MenuName = "扩展模块", Url = null, Icon = "fa fa-cog", Visible = true, IsLocked = true, Sort = 200, MenuType = MenuType.菜单}, + new SysMenu(){ Id =tenantId+"2.1", MenuName = "模块管理", Url = "/Admin/XncfModule/Index", Icon = "fa fa-user-secret", Visible = true, IsLocked = true, Sort = 175, ParentId =tenantId+ "2", MenuType = MenuType.菜单}, + }; + + IEnumerable sysRoles = new List() + { + new SysRole() { Id = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, RoleName = "超级管理员", Enabled = true } + }; + + + IEnumerable sysPermissions = new List() + { + new SysRolePermission() { PermissionId = tenantId+"1", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, + new SysRolePermission() { PermissionId = tenantId+"1.1", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, + new SysRolePermission() { PermissionId = tenantId+"1.1.1", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, + new SysRolePermission() { PermissionId = tenantId+"1.1.2", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, + new SysRolePermission() { PermissionId = tenantId+"1.1.3", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, + new SysRolePermission() { PermissionId = tenantId+"1.1.4", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, + new SysRolePermission() { PermissionId = tenantId+"1.1.5", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true } , + new SysRolePermission() { PermissionId = tenantId+"1.1.6", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true } , + new SysRolePermission() { PermissionId = tenantId+"1.2", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, + new SysRolePermission() { PermissionId = tenantId+"1.2.1", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, + new SysRolePermission() { PermissionId = tenantId+"1.2.2", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, + new SysRolePermission() { PermissionId = tenantId+"1.2.3", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, + new SysRolePermission() { PermissionId = tenantId+"1.2.4", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, + new SysRolePermission() { PermissionId = tenantId+"1.2.5", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, + new SysRolePermission() { PermissionId = tenantId+"1.2.6", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, + new SysRolePermission() { PermissionId = tenantId+"1.3", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, + new SysRolePermission() { PermissionId = tenantId+"1.4", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, + new SysRolePermission() { PermissionId = tenantId+"1.5", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, + new SysRolePermission() { PermissionId = tenantId+"2", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true }, + new SysRolePermission() { PermissionId = tenantId+"2.1", ResourceCode = string.Empty, RoleId = tenantId+"1", RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, IsMenu = true } + }; + + IEnumerable sysRoleAdminUserInfos = new List() + { + new SysRoleAdminUserInfo() { RoleCode = Config.SYSROLE_ADMINISTRATOR_ROLE_CODE, RoleId =tenantId+ "1", AccountId = adminUserInfoId } + }; + + try + { + //Ensure DbContext instances are consistent + //Console.WriteLine("sysRoleService DbConte HashCode: " + sysRoleService.BaseData.BaseDB.BaseDataContext.GetHashCode()); + //Console.WriteLine("sysRolePermissionService DbConte HashCode: " + sysRolePermissionService.BaseDB.BaseDataContext.GetHashCode()); + //Console.WriteLine("sysRoleAdminUserInfoService DbConte HashCode: " + sysRoleAdminUserInfoService.BaseData.BaseDB.BaseDataContext.GetHashCode()); + //Console.WriteLine("sysMenus DbConte HashCode: " + this.BaseData.BaseDB.BaseDataContext.GetHashCode()); + + + await base.SaveObjectListAsync(sysMenus) + .ContinueWith(async t => + { + await sysRoleService.SaveObjectListAsync(sysRoles); + await sysRolePermissionService.SaveObjectListAsync(sysPermissions); + await sysRoleAdminUserInfoService.SaveObjectListAsync(sysRoleAdminUserInfos); + }); + + + + //_senparcEntities.Set().AddRange(sysRoles); + //_senparcEntities.Set().AddRange(sysMenus); + //_senparcEntities.Set().AddRange(sysPermissions); + //_senparcEntities.Set().AddRange(sysRoleAdminUserInfos); + //_senparcEntities.SaveChanges(); + } + catch (Exception ex) + { + + throw new Exception("初始化数据失败,原因:" + ex); + } + } + + /// + /// Get the menu collection of the database (linear) + /// + /// + public virtual async Task> GetMenuDtoByDbAsync() + { + List selectListItems = null; + IConfigurationProvider configurationProvider = _serviceProvider.GetService().ConfigurationProvider; + + var sysMenuList = await this.GetFullListAsync(z => true); + + selectListItems = this.Mapping(sysMenuList); //await _serviceProvider.GetService().Set().OrderByDescending(_ => _.AddTime).ProjectTo(configurationProvider).ToListAsync(); + + //List sysMenus = (await GetFullListAsync(_ => _.Visible).ConfigureAwait(false)).OrderByDescending(z => z.Sort).ToList(); + //List sysButtons = (await _sysButtonService.GetFullListAsync(_ => true).ConfigureAwait(false)).OrderBy(z => z.Id).ToList(); + //selectListItems = Mapper.Map>(sysMenus); + //List buttons = _sysButtonService.Mapper.Map>(sysButtons); + //selectListItems.AddRange(buttons); + return selectListItems; + } + } +} diff --git a/src/Basic/Senparc.Ncf.Service/System/SysRoleAdminUserInfoService.cs b/src/Basic/Senparc.Ncf.Service/System/SysRoleAdminUserInfoService.cs index b4fd15c94..8bf85bdd9 100644 --- a/src/Basic/Senparc.Ncf.Service/System/SysRoleAdminUserInfoService.cs +++ b/src/Basic/Senparc.Ncf.Service/System/SysRoleAdminUserInfoService.cs @@ -1,53 +1,53 @@ -using AutoMapper; -using Senparc.Ncf.Repository; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Senparc.Ncf.Core.Models.DataBaseModel; - -namespace Senparc.Ncf.Service -{ - public class SysRoleAdminUserInfoService : ClientServiceBase - { - private readonly SysRoleService _sysRoleService; - - public SysRoleAdminUserInfoService(ClientRepositoryBase repo, IServiceProvider serviceProvider) : base(repo, serviceProvider) - { - _sysRoleService = _serviceProvider.GetService(); - } - - /// - /// 添加角色信息 - /// - /// - /// - /// - public async Task AddAsync(IEnumerable roleId, int accountId) - { - IList sysRoleAdminUserInfos = new List(); - - IEnumerable sysRoles = await _sysRoleService.GetFullListAsync(_ => roleId.Contains(_.Id)); - foreach (var item in roleId) - { - SysRoleAdminUserInfo sysRoleAdminUserInfo = new SysRoleAdminUserInfo(accountId, item, sysRoles.FirstOrDefault(_ => _.Id == item)?.RoleCode); - sysRoleAdminUserInfos.Add(sysRoleAdminUserInfo); - } - - IEnumerable sysRoleAdmins = await GetFullListAsync(_ => _.AccountId == accountId); - await ServiceBase.ResilientTransaction.New(BaseData.BaseDB.BaseDataContext).ExecuteAsync(async () => - { - if (sysRoleAdmins.Any()) - { - await DeleteAllAsync(sysRoleAdmins); - } - if (sysRoleAdminUserInfos.Any()) - { - await SaveObjectListAsync(sysRoleAdminUserInfos); - } - }); - } - } -} +using AutoMapper; +using Senparc.Ncf.Repository; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Senparc.Ncf.Core.Models.DataBaseModel; + +namespace Senparc.Ncf.Service +{ + public class SysRoleAdminUserInfoService : ClientServiceBase + { + private readonly SysRoleService _sysRoleService; + + public SysRoleAdminUserInfoService(ClientRepositoryBase repo, IServiceProvider serviceProvider) : base(repo, serviceProvider) + { + _sysRoleService = _serviceProvider.GetService(); + } + + /// + ///Add character information + /// + /// + /// + /// + public async Task AddAsync(IEnumerable roleId, int accountId) + { + IList sysRoleAdminUserInfos = new List(); + + IEnumerable sysRoles = await _sysRoleService.GetFullListAsync(_ => roleId.Contains(_.Id)); + foreach (var item in roleId) + { + SysRoleAdminUserInfo sysRoleAdminUserInfo = new SysRoleAdminUserInfo(accountId, item, sysRoles.FirstOrDefault(_ => _.Id == item)?.RoleCode); + sysRoleAdminUserInfos.Add(sysRoleAdminUserInfo); + } + + IEnumerable sysRoleAdmins = await GetFullListAsync(_ => _.AccountId == accountId); + await ServiceBase.ResilientTransaction.New(BaseData.BaseDB.BaseDataContext).ExecuteAsync(async () => + { + if (sysRoleAdmins.Any()) + { + await DeleteAllAsync(sysRoleAdmins); + } + if (sysRoleAdminUserInfos.Any()) + { + await SaveObjectListAsync(sysRoleAdminUserInfos); + } + }); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Service/System/SysRolePermissionService.cs b/src/Basic/Senparc.Ncf.Service/System/SysRolePermissionService.cs index 692328c61..2492e9900 100644 --- a/src/Basic/Senparc.Ncf.Service/System/SysRolePermissionService.cs +++ b/src/Basic/Senparc.Ncf.Service/System/SysRolePermissionService.cs @@ -1,330 +1,330 @@ -using AutoMapper; -using Microsoft.Extensions.Caching.Distributed; -using Senparc.Ncf.Repository; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Senparc.Ncf.Core.Models.DataBaseModel; -using Senparc.Ncf.Core.WorkContext.Provider; -using Senparc.Ncf.Core.Models; -using Microsoft.EntityFrameworkCore; -using AutoMapper.QueryableExtensions; -using Microsoft.AspNetCore.Mvc.Infrastructure; -using Senparc.Respository; - -namespace Senparc.Ncf.Service -{ - public class SysRolePermissionService : ClientServiceBase, Core.Authorization.ICheckPermission - { - private readonly IDistributedCache _distributedCache; - - public SysMenuService _sysMenuService { get; } - - private readonly IAdminWorkContextProvider _adminWorkContextProvider; - private readonly SysRoleService _sysRoleService; - private const string PermissionKey = "Permission"; - private readonly ISysRolePermissionRepository _repo; - public SysRolePermissionService(ISysRolePermissionRepository repo, IServiceProvider serviceProvider) : base(repo, serviceProvider) - { - this._distributedCache = _serviceProvider.GetService(); - this._sysMenuService = _serviceProvider.GetService(); - this._adminWorkContextProvider = _serviceProvider.GetService(); - this._sysRoleService = _serviceProvider.GetService(); - _repo = repo; - } - - public async Task HasPermissionAsync(string url) - { - IEnumerable roleIds = _adminWorkContextProvider.GetAdminWorkContext().RoleCodes; - string str = await _distributedCache.GetStringAsync(PermissionKey); - IEnumerable permissions; - if (string.IsNullOrEmpty(str)) - { - permissions = await DbToCacheAsync(); - } - else - { - permissions = Newtonsoft.Json.JsonConvert.DeserializeObject>(str); - } - permissions.Where(_ => roleIds.Contains(_.RoleCode)); - if (!permissions.Any()) - { - return false; - } - IEnumerable sysMenus = await _sysMenuService.GetMenuDtoByCacheAsync(); - - Func getUrlPath = u => - { - var index = u.IndexOf("?"); - return index >= 0 ? u.Substring(0, index) : u; - }; - - var urls = from menu in sysMenus - join permission in permissions on menu.Id equals permission.PermissionId - where !string.IsNullOrEmpty(menu.Url) - select getUrlPath(menu.Url).ToLower(); - - if (!urls.Any() || !urls.Contains(url.ToLower())) - { - return false; - } - return true; - } - - //public async Task> GetUserMenuDtosAsync() - //{ - // IEnumerable roleIds = _adminWorkContextProvider.GetAdminWorkContext().RoleCodes; - // string str = await _distributedCache.GetStringAsync(PermissionKey); - // IEnumerable permissions = Newtonsoft.Json.JsonConvert.DeserializeObject>(str).Where(_ => roleIds.Contains(_.RoleCode)); - // IEnumerable sysMenus = await _sysMenuService.GetMenuDtoByCacheAsync(); - // var urls = from menu in sysMenus - // join permission in permissions on menu.Id equals permission.PermissionId - // where !string.IsNullOrEmpty(menu.Url) - // select menu; - - // return urls; - //} - - public async Task> DbToCacheAsync() - { - IEnumerable permissions = await GetFullListAsync(_ => true); - IEnumerable permissionDtos = Mapper.Map>(permissions); - await _distributedCache.RemoveAsync(PermissionKey); - await _distributedCache.SetStringAsync(PermissionKey, Newtonsoft.Json.JsonConvert.SerializeObject(permissionDtos)); - return permissionDtos; - } - - public async Task InitPermissionCache() - { - await DbToCacheAsync(); - await _sysMenuService.GetMenuDtoByCacheAsync(true); - } - - /// - /// 添加权限信息 - /// - /// - /// - public async Task AddAsync(IEnumerable sysMenuDto) - { - List sysRoleMenus = new List(); - string roleId = sysMenuDto.FirstOrDefault().RoleId; - SysRole sysRole = await _sysRoleService.GetObjectAsync(_ => _.Id == roleId); - // - foreach (var item in sysMenuDto) - { - item.RoleCode = sysRole.RoleCode; - SysRolePermission sysPermission = new SysRolePermission(item); - sysRoleMenus.Add(sysPermission); - } - #region 正确方式1 传统方式 - //var db = BaseData.BaseDB.BaseDataContext; - //IEnumerable entitis = await db.Set().Where(o => o.RoleId == sysMenuDto.FirstOrDefault().RoleId).ToListAsync(); - //db.Set().RemoveRange(entitis); - //db.Set().AddRange(sysRoleMenus); - //await db.SaveChangesAsync(); - //await DbToCacheAsync();//暂时 - #endregion - - #region 异常方法 - //await BeginTransactionAsync(async () => - //{ - // IEnumerable entitis = await GetFullListAsync(_ => _.RoleId == sysMenuDto.FirstOrDefault().RoleId); - // await DeleteAllAsync(entitis); - // await SaveObjectListAsync(sysRoleMenus); - // await DbToCacheAsync();//暂时 - //}); - #endregion - #region 正确方式2 多次调用DbContext.SaveChanges 方式 - // https://docs.microsoft.com/zh-cn/ef/core/miscellaneous/connection-resiliency#execution-strategies-and-transactions - await ServiceBase.ResilientTransaction.New(BaseData.BaseDB.BaseDataContext).ExecuteAsync(async () => - { - IEnumerable entitis = await GetFullListAsync(_ => _.RoleId == sysMenuDto.FirstOrDefault().RoleId); - await DeleteAllAsync(entitis); // 此处会调用SaveChangeAsync - await SaveObjectListAsync(sysRoleMenus); // 此处会调用SaveChangeAsync - await DbToCacheAsync();//暂时 - }); - #endregion - } - - /// - /// 获取当前用户的权限 - /// - /// - public async Task> GetUserSysPermissionDtosAsync() - { - IEnumerable roleIds = _adminWorkContextProvider.GetAdminWorkContext().RoleCodes; - string str = await _distributedCache.GetStringAsync(PermissionKey); - IEnumerable permissions; - if (string.IsNullOrEmpty(str)) - { - permissions = await DbToCacheAsync(); - } - else - { - permissions = Newtonsoft.Json.JsonConvert.DeserializeObject>(str); - } - return permissions.Where(_ => roleIds.Contains(_.RoleCode)); - } - - [Obsolete("HasPermissionAsync(IEnumerable codes, string url, bool isAjax)")] - public async Task HasPermissionByButtonCodeAsync(string code, string url) - { - if (string.IsNullOrEmpty(code)) - { - return false; - } - - IEnumerable roleIds = _adminWorkContextProvider.GetAdminWorkContext().RoleCodes; - string str = await _distributedCache.GetStringAsync(PermissionKey); - IEnumerable permissions; - if (string.IsNullOrEmpty(str)) - { - permissions = await DbToCacheAsync(); - } - else - { - permissions = Newtonsoft.Json.JsonConvert.DeserializeObject>(str); - } - permissions.Where(_ => roleIds.Contains(_.RoleCode)); - if (!permissions.Any()) - { - return false; - } - IEnumerable sysMenus = await _sysMenuService.GetMenuDtoByCacheAsync(); - var resourceCodes = from menu in sysMenus - join permission in permissions on menu.Id equals permission.PermissionId - where !string.IsNullOrEmpty(menu.ResourceCode) && !permission.IsMenu - select menu.ResourceCode; - - IEnumerable sysMenuDtos = await _sysMenuService.GetMenuDtoByCacheAsync(); - SysMenuDto sysMenuDto = sysMenuDtos.FirstOrDefault(_ => _.Url?.ToLower() == url.ToLower() && _.IsMenu); - if (sysMenuDto == null) - { - return false; - } - bool isInUrl = sysMenuDtos.Any(_ => _.ParentId == sysMenuDto.Id && !_.IsMenu); - bool has = resourceCodes.Any(_ => _ == code); - return has && isInUrl; - } - - /// - /// 验证权限 - /// - /// - /// - public async Task HasPermissionAsync(IEnumerable codes, string url, bool isAjax) - { - int adminUserId = _adminWorkContextProvider.GetAdminWorkContext().AdminUserId; - IQueryable permissions = GetUserPermissions(adminUserId); - if (isAjax) - { - var userPermissionCodes = await permissions.Where(_ => _.ResourceCode != string.Empty).Select(_ => _.ResourceCode).Distinct().ToListAsync(); - bool result = codes.Intersect(userPermissionCodes).Any(); - return result; - } - else - { - return await permissions.AnyAsync(_ => _.Url == url && _.ResourceCode == string.Empty); - } - } - - /// - /// 获取当前用户可以看见的菜单(可见)树形结构 - /// - /// - /// - public async Task> GetCurrentUserMenuTreeDtoAsync() - { - //IEnumerable sysMenuTreeItems = null;// - int currentAdminId = _adminWorkContextProvider.GetAdminWorkContext().AdminUserId; - SenparcEntitiesBase db = _serviceProvider.GetService(); - List sysMenuDtos = await GetUserPermissions(currentAdminId).Where(_ => _.MenuType == MenuType.菜单).OrderByDescending(_ => _.Sort).ToListAsync(); - return _sysMenuService.GetSysMenuTreesMainRecursive(sysMenuDtos); - } - - /// - /// 获取当前用户可以看见的菜单(可见) - /// - /// - public async Task> GetCurrentUserMenuDtoAsync(MenuType menuType = MenuType.菜单) - { - //IEnumerable sysMenuTreeItems = null;// - int currentAdminId = _adminWorkContextProvider.GetAdminWorkContext().AdminUserId; - SenparcEntitiesBase db = _serviceProvider.GetService(); - List sysMenuDtos = await GetUserPermissions(currentAdminId).Where(_ => _.MenuType == menuType).OrderByDescending(_ => _.Sort).ToListAsync(); - return sysMenuDtos;// _sysMenuService.GetSysMenuTreesMainRecursive(sysMenuDtos); - } - - /// - /// 获取用户可见的所有资源(除菜单外) - /// - /// - public async Task> GetCurrentUserResourcesDtoAsync() - { - //IEnumerable sysMenuTreeItems = null;// - int currentAdminId = _adminWorkContextProvider.GetAdminWorkContext().AdminUserId; - SenparcEntitiesBase db = _serviceProvider.GetService(); - List sysMenuDtos = await GetUserPermissions(currentAdminId).Where(_ => _.MenuType > MenuType.菜单).OrderByDescending(_ => _.Sort).ToListAsync(); - return sysMenuDtos;// _sysMenuService.GetSysMenuTreesMainRecursive(sysMenuDtos); - } - - /// - /// 获取用户得权限(包括按钮) - /// - /// - /// - private IQueryable GetUserPermissions(int currentAdminUserId) - { - IConfigurationProvider autoMapConfigurationProvider = _serviceProvider.GetService().ConfigurationProvider; - SenparcEntitiesBase db = _serviceProvider.GetService(); - IQueryable roleIds = from roleAdmin in db.Set() - where roleAdmin.AccountId == currentAdminUserId && db.Set().Any(role => role.Id == roleAdmin.RoleId && role.Enabled) - select roleAdmin.RoleId;// db.SysRoleAdminUserInfos.Where(_ => _.AccountId == currentAdminUserId).Select(_ => _.RoleId); - IQueryable menuIds = from permission in db.Set() - where roleIds.Any(_ => _ == permission.RoleId) - select permission.PermissionId; - return db.Set().Where(_ => _.Visible && menuIds.Contains(_.Id)).ProjectTo(autoMapConfigurationProvider); - } - - /// - /// 检查当前用户是否权限 - /// - /// 资源codes - /// 当前用户Id - /// - public async Task HasPermissionAsync(string[] resourceCodes, int adminUserInfoId) - { - if (!resourceCodes.Any()) - { - return false; - } - string cacheKey = string.Format("adminUserInfoPermission:{0}", adminUserInfoId); // 缓存当前用户的所有资源 - var distributedCache = _serviceProvider.GetService(); - string codesJsonValue = await distributedCache.GetStringAsync(cacheKey); // 尝试从缓存读取用户的资源 - IEnumerable codes = null; - if (string.IsNullOrEmpty(codesJsonValue)) - { - codes = await _repo.GetAllResouceCodesByAccountIdAsync(adminUserInfoId); - codesJsonValue = Newtonsoft.Json.JsonConvert.SerializeObject(codes); - await distributedCache.SetStringAsync(cacheKey, codesJsonValue, new DistributedCacheEntryOptions() - { - SlidingExpiration = TimeSpan.FromHours(8) - }); // 缓存 8 小时 - } - else - { - codes = Newtonsoft.Json.JsonConvert.DeserializeObject>(codesJsonValue); - } - if (!codes.Any()) - { - return false; - } - //获取当前用户的所有资源code - return codes.Intersect(resourceCodes).Any(); - } - } -} +using AutoMapper; +using Microsoft.Extensions.Caching.Distributed; +using Senparc.Ncf.Repository; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Senparc.Ncf.Core.Models.DataBaseModel; +using Senparc.Ncf.Core.WorkContext.Provider; +using Senparc.Ncf.Core.Models; +using Microsoft.EntityFrameworkCore; +using AutoMapper.QueryableExtensions; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using Senparc.Respository; + +namespace Senparc.Ncf.Service +{ + public class SysRolePermissionService : ClientServiceBase, Core.Authorization.ICheckPermission + { + private readonly IDistributedCache _distributedCache; + + public SysMenuService _sysMenuService { get; } + + private readonly IAdminWorkContextProvider _adminWorkContextProvider; + private readonly SysRoleService _sysRoleService; + private const string PermissionKey = "Permission"; + private readonly ISysRolePermissionRepository _repo; + public SysRolePermissionService(ISysRolePermissionRepository repo, IServiceProvider serviceProvider) : base(repo, serviceProvider) + { + this._distributedCache = _serviceProvider.GetService(); + this._sysMenuService = _serviceProvider.GetService(); + this._adminWorkContextProvider = _serviceProvider.GetService(); + this._sysRoleService = _serviceProvider.GetService(); + _repo = repo; + } + + public async Task HasPermissionAsync(string url) + { + IEnumerable roleIds = _adminWorkContextProvider.GetAdminWorkContext().RoleCodes; + string str = await _distributedCache.GetStringAsync(PermissionKey); + IEnumerable permissions; + if (string.IsNullOrEmpty(str)) + { + permissions = await DbToCacheAsync(); + } + else + { + permissions = Newtonsoft.Json.JsonConvert.DeserializeObject>(str); + } + permissions.Where(_ => roleIds.Contains(_.RoleCode)); + if (!permissions.Any()) + { + return false; + } + IEnumerable sysMenus = await _sysMenuService.GetMenuDtoByCacheAsync(); + + Func getUrlPath = u => + { + var index = u.IndexOf("?"); + return index >= 0 ? u.Substring(0, index) : u; + }; + + var urls = from menu in sysMenus + join permission in permissions on menu.Id equals permission.PermissionId + where !string.IsNullOrEmpty(menu.Url) + select getUrlPath(menu.Url).ToLower(); + + if (!urls.Any() || !urls.Contains(url.ToLower())) + { + return false; + } + return true; + } + + //public async Task> GetUserMenuDtosAsync() + //{ + // IEnumerable roleIds = _adminWorkContextProvider.GetAdminWorkContext().RoleCodes; + // string str = await _distributedCache.GetStringAsync(PermissionKey); + // IEnumerable permissions = Newtonsoft.Json.JsonConvert.DeserializeObject>(str).Where(_ => roleIds.Contains(_.RoleCode)); + // IEnumerable sysMenus = await _sysMenuService.GetMenuDtoByCacheAsync(); + // var urls = from menu in sysMenus + // join permission in permissions on menu.Id equals permission.PermissionId + // where !string.IsNullOrEmpty(menu.Url) + // select menu; + + // return urls; + //} + + public async Task> DbToCacheAsync() + { + IEnumerable permissions = await GetFullListAsync(_ => true); + IEnumerable permissionDtos = Mapper.Map>(permissions); + await _distributedCache.RemoveAsync(PermissionKey); + await _distributedCache.SetStringAsync(PermissionKey, Newtonsoft.Json.JsonConvert.SerializeObject(permissionDtos)); + return permissionDtos; + } + + public async Task InitPermissionCache() + { + await DbToCacheAsync(); + await _sysMenuService.GetMenuDtoByCacheAsync(true); + } + + /// + ///Add permission information + /// + /// + /// + public async Task AddAsync(IEnumerable sysMenuDto) + { + List sysRoleMenus = new List(); + string roleId = sysMenuDto.FirstOrDefault().RoleId; + SysRole sysRole = await _sysRoleService.GetObjectAsync(_ => _.Id == roleId); + // + foreach (var item in sysMenuDto) + { + item.RoleCode = sysRole.RoleCode; + SysRolePermission sysPermission = new SysRolePermission(item); + sysRoleMenus.Add(sysPermission); + } + #region 正确方式1 传统方式 + //var db = BaseData.BaseDB.BaseDataContext; + //IEnumerable entitis = await db.Set().Where(o => o.RoleId == sysMenuDto.FirstOrDefault().RoleId).ToListAsync(); + //db.Set().RemoveRange(entitis); + //db.Set().AddRange(sysRoleMenus); + //await db.SaveChangesAsync(); + //await DbToCacheAsync();//temporarily + #endregion + + #region 异常方法 + //await BeginTransactionAsync(async () => + //{ + // IEnumerable entitis = await GetFullListAsync(_ => _.RoleId == sysMenuDto.FirstOrDefault().RoleId); + // await DeleteAllAsync(entitis); + // await SaveObjectListAsync(sysRoleMenus); + // await DbToCacheAsync();//temporarily + //}); + #endregion + #region 正确方式2 多次调用DbContext.SaveChanges 方式 + // https://docs.microsoft.com/zh-cn/ef/core/miscellaneous/connection-resiliency#execution-strategies-and-transactions + await ServiceBase.ResilientTransaction.New(BaseData.BaseDB.BaseDataContext).ExecuteAsync(async () => + { + IEnumerable entitis = await GetFullListAsync(_ => _.RoleId == sysMenuDto.FirstOrDefault().RoleId); + await DeleteAllAsync(entitis); // SaveChangeAsync will be called here + await SaveObjectListAsync(sysRoleMenus); // SaveChangeAsync will be called here + await DbToCacheAsync();//temporary + }); + #endregion + } + + /// + /// Get the permissions of the current user + /// + /// + public async Task> GetUserSysPermissionDtosAsync() + { + IEnumerable roleIds = _adminWorkContextProvider.GetAdminWorkContext().RoleCodes; + string str = await _distributedCache.GetStringAsync(PermissionKey); + IEnumerable permissions; + if (string.IsNullOrEmpty(str)) + { + permissions = await DbToCacheAsync(); + } + else + { + permissions = Newtonsoft.Json.JsonConvert.DeserializeObject>(str); + } + return permissions.Where(_ => roleIds.Contains(_.RoleCode)); + } + + [Obsolete("HasPermissionAsync(IEnumerable codes, string url, bool isAjax)")] + public async Task HasPermissionByButtonCodeAsync(string code, string url) + { + if (string.IsNullOrEmpty(code)) + { + return false; + } + + IEnumerable roleIds = _adminWorkContextProvider.GetAdminWorkContext().RoleCodes; + string str = await _distributedCache.GetStringAsync(PermissionKey); + IEnumerable permissions; + if (string.IsNullOrEmpty(str)) + { + permissions = await DbToCacheAsync(); + } + else + { + permissions = Newtonsoft.Json.JsonConvert.DeserializeObject>(str); + } + permissions.Where(_ => roleIds.Contains(_.RoleCode)); + if (!permissions.Any()) + { + return false; + } + IEnumerable sysMenus = await _sysMenuService.GetMenuDtoByCacheAsync(); + var resourceCodes = from menu in sysMenus + join permission in permissions on menu.Id equals permission.PermissionId + where !string.IsNullOrEmpty(menu.ResourceCode) && !permission.IsMenu + select menu.ResourceCode; + + IEnumerable sysMenuDtos = await _sysMenuService.GetMenuDtoByCacheAsync(); + SysMenuDto sysMenuDto = sysMenuDtos.FirstOrDefault(_ => _.Url?.ToLower() == url.ToLower() && _.IsMenu); + if (sysMenuDto == null) + { + return false; + } + bool isInUrl = sysMenuDtos.Any(_ => _.ParentId == sysMenuDto.Id && !_.IsMenu); + bool has = resourceCodes.Any(_ => _ == code); + return has && isInUrl; + } + + /// + ///Verify permissions + /// + /// + /// + public async Task HasPermissionAsync(IEnumerable codes, string url, bool isAjax) + { + int adminUserId = _adminWorkContextProvider.GetAdminWorkContext().AdminUserId; + IQueryable permissions = GetUserPermissions(adminUserId); + if (isAjax) + { + var userPermissionCodes = await permissions.Where(_ => _.ResourceCode != string.Empty).Select(_ => _.ResourceCode).Distinct().ToListAsync(); + bool result = codes.Intersect(userPermissionCodes).Any(); + return result; + } + else + { + return await permissions.AnyAsync(_ => _.Url == url && _.ResourceCode == string.Empty); + } + } + + /// + /// Get the menu (visible) tree structure visible to the current user + /// + /// + /// + public async Task> GetCurrentUserMenuTreeDtoAsync() + { + //IEnumerable sysMenuTreeItems = null;// + int currentAdminId = _adminWorkContextProvider.GetAdminWorkContext().AdminUserId; + SenparcEntitiesBase db = _serviceProvider.GetService(); + List sysMenuDtos = await GetUserPermissions(currentAdminId).Where(_ => _.MenuType == MenuType.菜单).OrderByDescending(_ => _.Sort).ToListAsync(); + return _sysMenuService.GetSysMenuTreesMainRecursive(sysMenuDtos); + } + + /// + /// Get the menu that the current user can see (visible) + /// + /// + public async Task> GetCurrentUserMenuDtoAsync(MenuType menuType = MenuType.菜单) + { + //IEnumerable sysMenuTreeItems = null;// + int currentAdminId = _adminWorkContextProvider.GetAdminWorkContext().AdminUserId; + SenparcEntitiesBase db = _serviceProvider.GetService(); + List sysMenuDtos = await GetUserPermissions(currentAdminId).Where(_ => _.MenuType == menuType).OrderByDescending(_ => _.Sort).ToListAsync(); + return sysMenuDtos;// _sysMenuService.GetSysMenuTreesMainRecursive(sysMenuDtos); + } + + /// + /// Get all resources visible to the user (except menus) + /// + /// + public async Task> GetCurrentUserResourcesDtoAsync() + { + //IEnumerable sysMenuTreeItems = null;// + int currentAdminId = _adminWorkContextProvider.GetAdminWorkContext().AdminUserId; + SenparcEntitiesBase db = _serviceProvider.GetService(); + List sysMenuDtos = await GetUserPermissions(currentAdminId).Where(_ => _.MenuType > MenuType.菜单).OrderByDescending(_ => _.Sort).ToListAsync(); + return sysMenuDtos;// _sysMenuService.GetSysMenuTreesMainRecursive(sysMenuDtos); + } + + /// + /// Get user permissions (including buttons) + /// + /// + /// + private IQueryable GetUserPermissions(int currentAdminUserId) + { + IConfigurationProvider autoMapConfigurationProvider = _serviceProvider.GetService().ConfigurationProvider; + SenparcEntitiesBase db = _serviceProvider.GetService(); + IQueryable roleIds = from roleAdmin in db.Set() + where roleAdmin.AccountId == currentAdminUserId && db.Set().Any(role => role.Id == roleAdmin.RoleId && role.Enabled) + select roleAdmin.RoleId;// db.SysRoleAdminUserInfos.Where(_ => _.AccountId == currentAdminUserId).Select(_ => _.RoleId); + IQueryable menuIds = from permission in db.Set() + where roleIds.Any(_ => _ == permission.RoleId) + select permission.PermissionId; + return db.Set().Where(_ => _.Visible && menuIds.Contains(_.Id)).ProjectTo(autoMapConfigurationProvider); + } + + /// + /// Check whether the current user has permissions + /// + /// Resource codes + /// Current user Id + /// + public async Task HasPermissionAsync(string[] resourceCodes, int adminUserInfoId) + { + if (!resourceCodes.Any()) + { + return false; + } + string cacheKey = string.Format("adminUserInfoPermission:{0}", adminUserInfoId); // Cache all resources of the current user + var distributedCache = _serviceProvider.GetService(); + string codesJsonValue = await distributedCache.GetStringAsync(cacheKey); // Try to read the user's resource from cache + IEnumerable codes = null; + if (string.IsNullOrEmpty(codesJsonValue)) + { + codes = await _repo.GetAllResouceCodesByAccountIdAsync(adminUserInfoId); + codesJsonValue = Newtonsoft.Json.JsonConvert.SerializeObject(codes); + await distributedCache.SetStringAsync(cacheKey, codesJsonValue, new DistributedCacheEntryOptions() + { + SlidingExpiration = TimeSpan.FromHours(8) + }); // Cache for 8 hours + } + else + { + codes = Newtonsoft.Json.JsonConvert.DeserializeObject>(codesJsonValue); + } + if (!codes.Any()) + { + return false; + } + //Get all resource codes of the current user + return codes.Intersect(resourceCodes).Any(); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Service/System/XncfModuleService.cs b/src/Basic/Senparc.Ncf.Service/System/XncfModuleService.cs index 15030dcfc..901ee81fe 100644 --- a/src/Basic/Senparc.Ncf.Service/System/XncfModuleService.cs +++ b/src/Basic/Senparc.Ncf.Service/System/XncfModuleService.cs @@ -1,131 +1,131 @@ -using Senparc.Ncf.Core.Cache; -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.Core.Models.DataBaseModel; -using Senparc.Ncf.Repository; -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Log; -using Microsoft.Extensions.DependencyInjection; - -namespace Senparc.Ncf.Service -{ - public class XncfModuleService : ServiceBase - { - public XncfModuleService(IRepositoryBase repo, IServiceProvider serviceProvider) : base(repo, serviceProvider) - { - } - - /// - /// 检查并更新版本 - /// - /// - /// - /// 返回是否需要新增或更新 - public async Task CheckAndUpdateVersionAsync(CreateOrUpdate_XncfModuleDto storedDto, UpdateVersion_XncfModuleDto assemblyDto) - { - if (storedDto == null) - { - //新增模块 - var xncfModule = new XncfModule(assemblyDto.Name, assemblyDto.Uid, assemblyDto.MenuName, assemblyDto.Version, assemblyDto.Description, "", true, "", assemblyDto.Icon, Core.Enums.XncfModules_State.新增待审核); - xncfModule.Create(); - await base.SaveObjectAsync(xncfModule).ConfigureAwait(false); - return InstallOrUpdate.Install; - } - else - { - //检查更新 - if (storedDto.Version != assemblyDto.Version) - { - var xncfModule = base.GetObject(z => z.Uid == storedDto.Uid); - xncfModule.UpdateVersion(assemblyDto.Version, assemblyDto.MenuName, assemblyDto.Description); - await base.SaveObjectAsync(xncfModule).ConfigureAwait(false); - return InstallOrUpdate.Update; - } - return null; - } - } - - /// - /// 跟新菜单Id - /// - /// - /// - public async Task UpdateMenuIdAsync(UpdateMenuId_XncfModuleDto dto) - { - var xncfModule = await base.GetObjectAsync(z => z.Uid == dto.Uid).ConfigureAwait(false); - xncfModule.UpdateMenuId(dto.MenuId); - await SaveObjectAsync(xncfModule).ConfigureAwait(false); - } - - public override void SaveObject(XncfModule obj) - { - var isInsert = base.IsInsert(obj); - if (isInsert) - { - obj.Flag = false; - } - base.SaveObject(obj); - LogUtility.WebLogger.InfoFormat("XncfModule{2}:{0}(ID:{1})", obj.Name, obj.Id, isInsert ? "新增" : "编辑"); - - //清除缓存 - var fullUserCache = _serviceProvider.GetService(); - //同步缓存锁 - using (fullUserCache.Cache.BeginCacheLock(FullXncfModuleCache.CACHE_KEY, obj.Id.ToString())) - { - fullUserCache.RemoveObject(obj.Name); - } - } - - public override async Task SaveObjectAsync(XncfModule obj) - { - var isInsert = base.IsInsert(obj); - if (isInsert) - { - obj.Flag = false; - } - await base.SaveObjectAsync(obj); - LogUtility.WebLogger.InfoFormat("XncfModule{2}:{0}(ID:{1})", obj.Name, obj.Id, isInsert ? "新增" : "编辑"); - - //清除缓存 - var fullUserCache = _serviceProvider.GetService(); - //同步缓存锁 - using (await fullUserCache.Cache.BeginCacheLockAsync(FullXncfModuleCache.CACHE_KEY, obj.Id.ToString()).ConfigureAwait(false)) - { - await fullUserCache.RemoveObjectAsync(obj.Name); - } - } - - public override void DeleteObject(XncfModule obj) - { - obj.Flag = true; - base.SaveObject(obj); - LogUtility.WebLogger.Info($"XncfModule被删除:{obj.Name}(ID:{obj.Id})"); - - //清除缓存 - var fullUserCache = _serviceProvider.GetService(); - //同步缓存锁 - using (fullUserCache.Cache.BeginCacheLock(FullXncfModuleCache.CACHE_KEY, obj.Id.ToString())) - { - fullUserCache.RemoveObject(obj.Name); - } - } - - public override async Task DeleteObjectAsync(XncfModule obj) - { - obj.Flag = true; - await base.SaveObjectAsync(obj).ConfigureAwait(false); - LogUtility.WebLogger.Info($"XncfModule被删除:{obj.Name}(ID:{obj.Id})"); - - //清除缓存 - var fullUserCache = _serviceProvider.GetService(); - //同步缓存锁 - using (await fullUserCache.Cache.BeginCacheLockAsync(FullXncfModuleCache.CACHE_KEY, obj.Id.ToString()).ConfigureAwait(false)) - { - await fullUserCache.RemoveObjectAsync(obj.Name); - } - } - } -} +using Senparc.Ncf.Core.Cache; +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.Core.Models.DataBaseModel; +using Senparc.Ncf.Repository; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Log; +using Microsoft.Extensions.DependencyInjection; + +namespace Senparc.Ncf.Service +{ + public class XncfModuleService : ServiceBase + { + public XncfModuleService(IRepositoryBase repo, IServiceProvider serviceProvider) : base(repo, serviceProvider) + { + } + + /// + /// Check and update version + /// + /// + /// + /// Return whether new addition or update is required + public async Task CheckAndUpdateVersionAsync(CreateOrUpdate_XncfModuleDto storedDto, UpdateVersion_XncfModuleDto assemblyDto) + { + if (storedDto == null) + { + //New module + var xncfModule = new XncfModule(assemblyDto.Name, assemblyDto.Uid, assemblyDto.MenuName, assemblyDto.Version, assemblyDto.Description, "", true, "", assemblyDto.Icon, Core.Enums.XncfModules_State.新增待审核); + xncfModule.Create(); + await base.SaveObjectAsync(xncfModule).ConfigureAwait(false); + return InstallOrUpdate.Install; + } + else + { + //Check for updates + if (storedDto.Version != assemblyDto.Version) + { + var xncfModule = base.GetObject(z => z.Uid == storedDto.Uid); + xncfModule.UpdateVersion(assemblyDto.Version, assemblyDto.MenuName, assemblyDto.Description); + await base.SaveObjectAsync(xncfModule).ConfigureAwait(false); + return InstallOrUpdate.Update; + } + return null; + } + } + + /// + /// Follow the new menu ID + /// + /// + /// + public async Task UpdateMenuIdAsync(UpdateMenuId_XncfModuleDto dto) + { + var xncfModule = await base.GetObjectAsync(z => z.Uid == dto.Uid).ConfigureAwait(false); + xncfModule.UpdateMenuId(dto.MenuId); + await SaveObjectAsync(xncfModule).ConfigureAwait(false); + } + + public override void SaveObject(XncfModule obj) + { + var isInsert = base.IsInsert(obj); + if (isInsert) + { + obj.Flag = false; + } + base.SaveObject(obj); + LogUtility.WebLogger.InfoFormat("XncfModule{2}:{0}(ID:{1})", obj.Name, obj.Id, isInsert ? "新增" : "编辑"); + + //clear cache + var fullUserCache = _serviceProvider.GetService(); + //Synchronous cache lock + using (fullUserCache.Cache.BeginCacheLock(FullXncfModuleCache.CACHE_KEY, obj.Id.ToString())) + { + fullUserCache.RemoveObject(obj.Name); + } + } + + public override async Task SaveObjectAsync(XncfModule obj) + { + var isInsert = base.IsInsert(obj); + if (isInsert) + { + obj.Flag = false; + } + await base.SaveObjectAsync(obj); + LogUtility.WebLogger.InfoFormat("XncfModule{2}:{0}(ID:{1})", obj.Name, obj.Id, isInsert ? "新增" : "编辑"); + + //clear cache + var fullUserCache = _serviceProvider.GetService(); + //Synchronous cache lock + using (await fullUserCache.Cache.BeginCacheLockAsync(FullXncfModuleCache.CACHE_KEY, obj.Id.ToString()).ConfigureAwait(false)) + { + await fullUserCache.RemoveObjectAsync(obj.Name); + } + } + + public override void DeleteObject(XncfModule obj) + { + obj.Flag = true; + base.SaveObject(obj); + LogUtility.WebLogger.Info($"XncfModule被删除:{obj.Name}(ID:{obj.Id})"); + + //clear cache + var fullUserCache = _serviceProvider.GetService(); + //Synchronous cache lock + using (fullUserCache.Cache.BeginCacheLock(FullXncfModuleCache.CACHE_KEY, obj.Id.ToString())) + { + fullUserCache.RemoveObject(obj.Name); + } + } + + public override async Task DeleteObjectAsync(XncfModule obj) + { + obj.Flag = true; + await base.SaveObjectAsync(obj).ConfigureAwait(false); + LogUtility.WebLogger.Info($"XncfModule被删除:{obj.Name}(ID:{obj.Id})"); + + //clear cache + var fullUserCache = _serviceProvider.GetService(); + //Synchronous cache lock + using (await fullUserCache.Cache.BeginCacheLockAsync(FullXncfModuleCache.CACHE_KEY, obj.Id.ToString()).ConfigureAwait(false)) + { + await fullUserCache.RemoveObjectAsync(obj.Name); + } + } + } +} diff --git a/src/Basic/Senparc.Ncf.ServiceTests/ServiceBase/ServiceDataBaseTests.cs b/src/Basic/Senparc.Ncf.ServiceTests/ServiceBase/ServiceDataBaseTests.cs index 94dd227a9..e0bed4e82 100644 --- a/src/Basic/Senparc.Ncf.ServiceTests/ServiceBase/ServiceDataBaseTests.cs +++ b/src/Basic/Senparc.Ncf.ServiceTests/ServiceBase/ServiceDataBaseTests.cs @@ -1,127 +1,127 @@ -//using Microsoft.VisualStudio.TestTools.UnitTesting; -//using Senparc.Ncf.Service; -//using Senparc.Ncf.UnitTestExtension; -//using Senparc.Xncf.PromptRange.Domain.Models.DatabaseModel; -//using Senparc.Xncf.PromptRange.Domain.Services; -//using System; -//using System.Collections.Generic; -//using System.Linq; -//using System.Text; -//using System.Threading.Tasks; - -//namespace Senparc.Ncf.Service.Tests -//{ -// [TestClass()] -// public class ServiceDataBaseTests : BaseNcfUnitTest -// { -// /// -// /// 测试保存单个对象 -// /// -// /// -// [TestMethod] -// public async Task AfterSaveObjectTest() -// { -// string msg = null; -// string dataDb = null; -// string senparcEntities = null; - -// //设置保存对象后的行为 -// ServiceDataBase.AfterSaveObject = (dataBase, obj) => -// { -// dataDb = dataBase.BaseDB.GetType().Name; -// senparcEntities = dataBase.BaseDB.BaseDataContext.GetType().Name; -// msg = obj.GetType().Name; - -// if (obj is PromptRange promptRange) -// { -// msg += "+" + promptRange.RangeName; -// } -// }; - -// var promptRangeRepo = base.GetRespository(); -// var promptRangeService = new PromptRangeService(promptRangeRepo.MockRepository.Object, base._serviceProvider); - -// var promptRange = new PromptRange("Name", "Alias"); -// await promptRangeService.SaveObjectAsync(promptRange); - -// Assert.AreEqual("PromptRange+Name", msg); -// Assert.AreEqual("NcfUnitTestDataDb", dataDb); -// Assert.AreEqual("NcfUnitTestEntities", senparcEntities); -// } - -// /// -// /// 测试批量保存对象 -// /// -// /// -// [TestMethod] -// public async Task AfterSaveObjectLListTest() -// { -// List result = new List(); - -// //设置保存对象后的行为 -// ServiceDataBase.AfterSaveObject = (dataBase, obj) => -// { -// if (obj is PromptRange promptRange) -// { -// result.Add(promptRange.RangeName); -// } -// }; - -// var promptRangeRepo = base.GetRespository(); -// var promptRangeService = new PromptRangeService(promptRangeRepo.MockRepository.Object, base._serviceProvider); - -// List promptRanges = new(); -// for (int i = 0; i < 5; i++) -// { -// var promptRange = new PromptRange($"Name-{i}", $"Alias-{i}"); -// promptRanges.Add(promptRange); -// } - -// await promptRangeService.SaveObjectListAsync(promptRanges); - -// for (int i = 0; i < 5; i++) -// { -// Assert.AreEqual($"Name-{i}", promptRanges[i].RangeName); -// Assert.AreEqual($"Alias-{i}", promptRanges[i].Alias); -// } -// } - -// /// -// /// 测试保存所有变化 -// /// -// /// -// [TestMethod] -// public async Task AfterSaveChangesTest() -// { -// string dataDb = null; -// string senparcEntities = null; - -// //设置保存对象后的行为 -// ServiceDataBase.AfterSaveChanges = (dataBase) => -// { -// dataDb = dataBase.BaseDB.GetType().Name; -// senparcEntities = dataBase.BaseDB.BaseDataContext.GetType().Name; -// }; - -// var promptRangeRepo = base.GetRespository(); -// var promptRangeService = new PromptRangeService(promptRangeRepo.MockRepository.Object, base._serviceProvider); - -// var promptRange = new PromptRange("Name", "Alias"); -// await promptRangeService.SaveChangesAsync(); - -// Assert.AreEqual("NcfUnitTestDataDb", dataDb); -// Assert.AreEqual("NcfUnitTestEntities", senparcEntities); -// } - -// [TestMethod()] -// public void ServiceDataBaseTest() -// { - -// } - -// public void CloseConnectionTest() -// { - -// } -// } +//using Microsoft.VisualStudio.TestTools.UnitTesting; +//using Senparc.Ncf.Service; +//using Senparc.Ncf.UnitTestExtension; +//using Senparc.Xncf.PromptRange.Domain.Models.DatabaseModel; +//using Senparc.Xncf.PromptRange.Domain.Services; +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using System.Threading.Tasks; + +//namespace Senparc.Ncf.Service.Tests +//{ +// [TestClass()] +// public class ServiceDataBaseTests : BaseNcfUnitTest +// { +// /// +// /// Test saving a single object +// /// +// /// +// [TestMethod] +// public async Task AfterSaveObjectTest() +// { +// string msg = null; +// string dataDb = null; +// string senparcEntities = null; + +// //Set the behavior after saving the object +// ServiceDataBase.AfterSaveObject = (dataBase, obj) => +// { +// dataDb = dataBase.BaseDB.GetType().Name; +// senparcEntities = dataBase.BaseDB.BaseDataContext.GetType().Name; +// msg = obj.GetType().Name; + +// if (obj is PromptRange promptRange) +// { +// msg += "+" + promptRange.RangeName; +// } +// }; + +// var promptRangeRepo = base.GetRespository(); +// var promptRangeService = new PromptRangeService(promptRangeRepo.MockRepository.Object, base._serviceProvider); + +// var promptRange = new PromptRange("Name", "Alias"); +// await promptRangeService.SaveObjectAsync(promptRange); + +// Assert.AreEqual("PromptRange+Name", msg); +// Assert.AreEqual("NcfUnitTestDataDb", dataDb); +// Assert.AreEqual("NcfUnitTestEntities", senparcEntities); +// } + +// /// +// /// Test batch saving objects +// /// +// /// +// [TestMethod] +// public async Task AfterSaveObjectLListTest() +// { +// List result = new List(); + +// //Set the behavior after saving the object +// ServiceDataBase.AfterSaveObject = (dataBase, obj) => +// { +// if (obj is PromptRange promptRange) +// { +// result.Add(promptRange.RangeName); +// } +// }; + +// var promptRangeRepo = base.GetRespository(); +// var promptRangeService = new PromptRangeService(promptRangeRepo.MockRepository.Object, base._serviceProvider); + +// List promptRanges = new(); +// for (int i = 0; i < 5; i++) +// { +// var promptRange = new PromptRange($"Name-{i}", $"Alias-{i}"); +// promptRanges.Add(promptRange); +// } + +// await promptRangeService.SaveObjectListAsync(promptRanges); + +// for (int i = 0; i < 5; i++) +// { +// Assert.AreEqual($"Name-{i}", promptRanges[i].RangeName); +// Assert.AreEqual($"Alias-{i}", promptRanges[i].Alias); +// } +// } + +// /// +// /// Test save all changes +// /// +// /// +// [TestMethod] +// public async Task AfterSaveChangesTest() +// { +// string dataDb = null; +// string senparcEntities = null; + +// //Set the behavior after saving the object +// ServiceDataBase.AfterSaveChanges = (dataBase) => +// { +// dataDb = dataBase.BaseDB.GetType().Name; +// senparcEntities = dataBase.BaseDB.BaseDataContext.GetType().Name; +// }; + +// var promptRangeRepo = base.GetRespository(); +// var promptRangeService = new PromptRangeService(promptRangeRepo.MockRepository.Object, base._serviceProvider); + +// var promptRange = new PromptRange("Name", "Alias"); +// await promptRangeService.SaveChangesAsync(); + +// Assert.AreEqual("NcfUnitTestDataDb", dataDb); +// Assert.AreEqual("NcfUnitTestEntities", senparcEntities); +// } + +// [TestMethod()] +// public void ServiceDataBaseTest() +// { + +// } + +// public void CloseConnectionTest() +// { + +// } +// } //} \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Shared.Abstractions/Events/IEventBus.cs b/src/Basic/Senparc.Ncf.Shared.Abstractions/Events/IEventBus.cs index 509d8f2cb..00f12bd1a 100644 --- a/src/Basic/Senparc.Ncf.Shared.Abstractions/Events/IEventBus.cs +++ b/src/Basic/Senparc.Ncf.Shared.Abstractions/Events/IEventBus.cs @@ -4,18 +4,18 @@ namespace Senparc.Ncf.Shared.Abstractions.Events { /// - /// 事件总线发布接口 + ///Event bus publishing interface /// public interface IEventBus { /// - /// 异步发布事件(非阻塞,写入 Channel 即返回) + /// Publish events asynchronously (non-blocking, write to Channel and return) /// ValueTask PublishAsync(TEvent @event, CancellationToken cancellationToken = default) where TEvent : IIntegrationEvent; /// - /// 异步发布派生事件(自动继承父事件的链信息,用于防止循环引用) + /// Publish derived events asynchronously (automatically inherit the chain information of the parent event to prevent circular references) /// ValueTask PublishDerivedAsync(TEvent @event, IIntegrationEvent parentEvent, CancellationToken cancellationToken = default) where TEvent : IIntegrationEvent; diff --git a/src/Basic/Senparc.Ncf.Shared.Abstractions/Events/IIntegrationEvent.cs b/src/Basic/Senparc.Ncf.Shared.Abstractions/Events/IIntegrationEvent.cs index 6cc27a011..3fe800955 100644 --- a/src/Basic/Senparc.Ncf.Shared.Abstractions/Events/IIntegrationEvent.cs +++ b/src/Basic/Senparc.Ncf.Shared.Abstractions/Events/IIntegrationEvent.cs @@ -5,7 +5,7 @@ namespace Senparc.Ncf.Shared.Abstractions.Events { /// - /// 集成事件标记接口 + /// Integrated event marking interface /// public interface IIntegrationEvent { @@ -13,23 +13,23 @@ public interface IIntegrationEvent DateTime CreationDate { get; } /// - /// 父事件 ID(用于追踪事件链和检测循环引用) + /// Parent event ID (used for tracing event chains and detecting circular references) /// Guid? ParentEventId { get; } /// - /// 事件链深度(根事件为 0,每次派生 +1) + /// Event chain depth (root event is 0, each fork +1) /// int Depth { get; } /// - /// 事件类型链路径(用于检测循环,格式:EventType1→EventType2→...) + /// Event type chain path (used to detect loops, format: EventType1→EventType2→...) /// string EventChain { get; } } /// - /// 基础事件类(建议所有具体事件继承此类) + ///Basic event class (it is recommended that all specific events inherit this class) /// public abstract record IntegrationEvent : IIntegrationEvent { @@ -37,22 +37,22 @@ public abstract record IntegrationEvent : IIntegrationEvent public DateTime CreationDate { get; init; } = DateTime.UtcNow; /// - /// 父事件 ID(用于追踪事件链) + /// Parent event ID (used to track event chains) /// public Guid? ParentEventId { get; init; } /// - /// 事件链深度(根事件为 0) + /// Event chain depth (root event is 0) /// public int Depth { get; init; } /// - /// 事件类型链路径(格式:EventType1→EventType2→...) + /// Event type chain path (format: EventType1→EventType2→...) /// public string EventChain { get; init; } = string.Empty; /// - /// 用于调试和日志记录的事件摘要信息 + /// Event summary information for debugging and logging /// public virtual string GetEventSummary() { @@ -60,7 +60,7 @@ public virtual string GetEventSummary() } /// - /// 从当前事件派生新事件(继承事件链信息) + /// Derive new events from current events (inherit event chain information) /// public EventMetadata DeriveMetadata() { @@ -73,7 +73,7 @@ public EventMetadata DeriveMetadata() } /// - /// 检查事件链中是否存在循环(同一事件类型出现两次) + /// Check if there is a loop in the event chain (the same event type appears twice) /// public bool HasCircularReference(string newEventType) { @@ -85,14 +85,14 @@ public bool HasCircularReference(string newEventType) var eventTypes = EventChain.Split('→').ToList(); eventTypes.Add(newEventType); - // 检查是否有重复的事件类型 + // Check if there are duplicate event types var duplicates = eventTypes.GroupBy(x => x).Where(g => g.Count() > 1).ToList(); return duplicates.Any(); } } /// - /// 事件元数据(用于创建派生事件) + ///Event metadata (used to create derived events) /// public record EventMetadata( Guid ParentEventId, diff --git a/src/Basic/Senparc.Ncf.Shared.Abstractions/Events/IIntegrationEventHandler.cs b/src/Basic/Senparc.Ncf.Shared.Abstractions/Events/IIntegrationEventHandler.cs index f04e447fa..caee88075 100644 --- a/src/Basic/Senparc.Ncf.Shared.Abstractions/Events/IIntegrationEventHandler.cs +++ b/src/Basic/Senparc.Ncf.Shared.Abstractions/Events/IIntegrationEventHandler.cs @@ -4,9 +4,9 @@ namespace Senparc.Ncf.Shared.Abstractions.Events { /// - /// 事件处理器泛型接口 + ///Event handler generic interface /// - /// 具体的事件类型 + /// Specific event type public interface IIntegrationEventHandler where TIntegrationEvent : IIntegrationEvent { diff --git a/src/Basic/Senparc.Ncf.UnitTestExtension/BaseNcfUnitTest.MockRepository.cs b/src/Basic/Senparc.Ncf.UnitTestExtension/BaseNcfUnitTest.MockRepository.cs index 5b7bdfa41..d83d2a174 100644 --- a/src/Basic/Senparc.Ncf.UnitTestExtension/BaseNcfUnitTest.MockRepository.cs +++ b/src/Basic/Senparc.Ncf.UnitTestExtension/BaseNcfUnitTest.MockRepository.cs @@ -1,280 +1,280 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Ncf.UnitTestExtension -{ - //public partial class BaseNcfUnitTest - //{ - //public (IClientRepositoryBase Repository, List DataList) GetRespositoryBase() where T : EntityBase - //{ - // var dataList = dataLists[typeof(T)].Cast().ToList(); - - // //var mockRepository = new Mock>(); - - // //设置 RepositoryBase.BaseDB.ManualDetectChangeObject - // var mockBaseDB = new Mock(); - // //mockBaseDB.Setup(z => z.ManualDetectChangeObject).Returns(true); - - - // mockBaseDB.Setup(z => z.DataContext.Set()) - // .Returns>(z => - // { - // var mockDbSet = UnitTestHelper.CreateMockDbSet(dataList); - // return mockDbSet.Object; - // }); - - // var repo = new ClientRepositoryBase(mockBaseDB.Object); - - // return (Repository: repo, DataList: dataList); - //} - - ///// - ///// 获取指定类型的仓储实例,带有预设的 Mock 行为 - ///// - ///// 实体类型 - ///// 仓储实例 - //public (Mock> MockRepository, List DataList) GetRespository() where T : EntityBase - //{ - // var repo = GetRespository(); - - // var dataList = repo.DataList; - - // // Mock Update 方法 - // repo.MockRepository.Setup(z => z.Update(It.IsAny())) - // .Callback(obj => - // { - // var existing = dataList.FirstOrDefault(o => o == obj); - // if (existing != null) - // { - // var index = dataList.IndexOf(existing); - // dataList[index] = obj; - // } - // }); - - // // Mock SaveAsync 方法 - // repo.MockRepository.Setup(z => z.SaveAsync(It.IsAny())) - // .Returns(obj => - // { - // var existing = dataList.FirstOrDefault(o => o == obj); - // if (existing != null) - // { - // var index = dataList.IndexOf(existing); - // dataList[index] = obj; - // } - // else - // { - // dataList.Add(obj); - // } - // return Task.CompletedTask; - // }); - - // return repo; - //} - - //public (Mock> MockRepository, List DataList) GetRespository() where T : EntityBase - //{ - // TryInitSeedData(); - - // var dataList = dataLists[typeof(T)].Cast().ToList(); - - // var mockRepository = new Mock>(); - - // //设置 RepositoryBase.BaseDB.ManualDetectChangeObject - // var mockBaseDB = new Mock(); - // //mockBaseDB.Setup(z => z.ManualDetectChangeObject).Returns(true); - - - // //mockBaseDB.Setup(z => z.DataContext.Set()) - // // .Returns>(z => - // // { - // // var mockDbSet = UnitTestHelper.CreateMockDbSet(dataList); - // // return mockDbSet.Object; - // // }); - - - // mockBaseDB.SetupProperty(z => z.ManualDetectChangeObject, true); - // mockRepository.Setup(z => z.BaseDB).Returns(mockBaseDB.Object); - - - // // Mock GetFirstOrDefaultObjectAsync 方法 - // mockRepository.Setup(z => z.GetFirstOrDefaultObjectAsync(It.IsAny>>(), It.IsAny())) - // .Returns>, string[]>((expr, includes) => - // { - // var func = expr.Compile(); - // return Task.FromResult(dataList.FirstOrDefault(func)); - // }); - - // // Mock GetFirstOrDefaultObject 方法 - // mockRepository.Setup(z => z.GetFirstOrDefaultObject(It.IsAny>>(), It.IsAny())) - // .Returns>, string[]>((expr, includes) => - // { - // var func = expr.Compile(); - // return dataList.FirstOrDefault(func); - // }); - - // // Mock GeAll 方法 - // mockRepository.Setup(z => z.GeAll(It.IsAny>>(), It.IsAny(), It.IsAny())) - // .Returns>, OrderingType, string[]>((orderBy, orderingType, includes) => - // { - // return dataList.AsQueryable().OrderBy(orderBy); - // }); - - // // Mock ObjectCount 方法 - // mockRepository.Setup(z => z.ObjectCount(It.IsAny>>(), It.IsAny())) - // .Returns>, string[]>((where, includes) => - // { - // var func = where.Compile(); - // return dataList.Count(func); - // }); - - // // Mock Add 方法 - // mockRepository.Setup(z => z.Add(It.IsAny())) - // .Callback(obj => - // { - // dataList.Add(obj); - // }); - - // // Mock Delete 方法 - // mockRepository.Setup(z => z.Delete(It.IsAny(), It.IsAny())) - // .Callback((obj, softDelete) => - // { - // dataList.Remove(obj); - // }); - - // // Mock SaveChanges 方法 - // mockRepository.Setup(z => z.SaveChanges()) - // .Callback(() => { /* No-op for in-memory data */ }); - - // // Mock DeleteAsync 方法 - // mockRepository.Setup(z => z.DeleteAsync(It.IsAny(), It.IsAny())) - // .Returns((obj, softDelete) => - // { - // dataList.Remove(obj); - // return Task.CompletedTask; - // }); - - // // Mock SaveChangesAsync 方法 - // mockRepository.Setup(z => z.SaveChangesAsync()) - // .Returns(Task.CompletedTask); - - - // //// 泛型版本的Mock设置 - // //mockRepository.Setup(z => z.GetObjectListAsync(It.IsAny>>(), It.IsAny>>(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - // // .Returns>, Expression>, OrderingType, int, int, string[]>((where, orderBy, orderingType, pageIndex, pageCount, includes) => - // // { - // // Console.WriteLine("GetObjectListAsync run with TOrderProperty:" + typeof(T).Name); - // // var func = where.Compile(); - // // var orderedData = orderingType == OrderingType.Ascending ? - // // dataList.AsQueryable().Where(where).OrderBy(orderBy) : - // // dataList.AsQueryable().Where(where).OrderByDescending(orderBy); - // // return Task.FromResult(new PagedList(orderedData.Skip((pageIndex - 1) * pageCount).Take(pageCount).ToList(), pageIndex, pageCount, dataList.Count)); - // // }); - - - // // Mock GetObjectListAsync 方法 - // mockRepository.Setup(z => z.GetObjectListAsync(It.IsAny>>(), It.IsAny>>(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - // .Returns>, Expression>, OrderingType, int, int, string[]>((where, orderBy, orderingType, pageIndex, pageCount, includes) => - // { - // var func = where.Compile(); - // var orderedData = orderingType == OrderingType.Ascending ? - // dataList.AsQueryable().Where(where).OrderBy(orderBy) : - // dataList.AsQueryable().Where(where).OrderByDescending(orderBy); - - // var result = pageIndex <= 0 && pageCount <= 0 - // ? orderedData.ToList() - // : orderedData.Skip((pageIndex - 1) * pageCount).Take(pageCount).ToList(); - - // return Task.FromResult(new PagedList(result, pageIndex, pageCount, dataList.Count)); - // }); - - // //GetObjectListAsync(Expression> where, string OrderbyField, int pageIndex, int pageCount, params string[] includes) - // mockRepository.Setup(repo => repo.GetObjectListAsync( - // It.IsAny>>(), - // It.IsAny(), - // It.IsAny(), - // It.IsAny(), - // It.IsAny())) - // .Returns>, string, int, int, string[]>((where, OrderbyField, pageIndex, pageCount, includes) => - // { - // var func = where.Compile(); - // var orderedData = dataList.AsQueryable().Where(where).OrderByExtension(OrderbyField); - - // var result = pageIndex <= 0 && pageCount <= 0 - // ? orderedData.ToList() - // : orderedData.Skip((pageIndex - 1) * pageCount).Take(pageCount).ToList(); - - // return Task.FromResult(new PagedList(result, pageIndex, pageCount, dataList.Count)); - // }); ; - - // // Mock GetFirstOrDefaultObjectAsync 方法 (带 includesNavigationPropertyPathFunc) - // mockRepository.Setup(z => z.GetFirstOrDefaultObjectAsync(It.IsAny>>(), It.IsAny, IIncludableQueryable>>>())) - // .Returns>, Expression, IIncludableQueryable>>>((where, includesNavigationPropertyPathFunc) => - // { - // //TODO:待完善 - // var func = where.Compile(); - // return Task.FromResult(dataList.FirstOrDefault(func)); - // }); - - // mockRepository.Setup(z => z.GetFirstOrDefaultObjectAsync( - // It.IsAny>>(), - // It.IsAny>>(), - // It.IsAny(), - // It.IsAny, IIncludableQueryable>>>())) - // .Returns>, Expression>, OrderingType, Expression, IIncludableQueryable>>>( - // async (where, orderBy, orderingType, includesNavigationPropertyPathFunc) => - // { - // var mockDbSet = UnitTestHelper.CreateMockDbSet(dataList); - - // // 使用Mock DbSet - // var query = await includesNavigationPropertyPathFunc.Compile()(mockDbSet.Object) - // .OrderBy(orderBy, orderingType) - // .Where(where) - // .OrderBy(orderBy, orderingType).FirstOrDefaultAsync(); - - // return query; - // }); - - - // // Mock ObjectCountAsync 方法 - // mockRepository.Setup(z => z.ObjectCountAsync(It.IsAny>>(), It.IsAny())) - // .Returns>, string[]>((where, includes) => - // { - // var func = where.Compile(); - // return Task.FromResult(dataList.Count(func)); - // }); - - // return (MockRepository: mockRepository, DataList: dataList); - //} - - ///// - ///// 获取指定类型的仓储实例,带有预设的 Mock 行为 - ///// - ///// 实体类型(OrderBy 默认为 int 类型,include 默认为 object 类型) - ///// 仓储实例 - //public (Mock> MockRepository, List DataList) GetRespository() where T : EntityBase - //{ - // var result = this.GetRespository(); - // return result; - //} - - ///// - ///// 获取指定类型的仓储实例 - ///// - ///// - ///// - //public IClientRepositoryBase GetRespositoryObject() where T : EntityBase - //{ - // return this.GetRespository().MockRepository.Object; - //} - - //public Mock CreateMockForExtendedInterface(Mock baseMock) - // where TInterface : class, TBase - // where TBase : class - //{ - // return baseMock.As(); - //} -// } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Ncf.UnitTestExtension +{ + //public partial class BaseNcfUnitTest + //{ + //public (IClientRepositoryBase Repository, List DataList) GetRespositoryBase() where T : EntityBase + //{ + // var dataList = dataLists[typeof(T)].Cast().ToList(); + + // //var mockRepository = new Mock>(); + + // //Set RepositoryBase.BaseDB.ManualDetectChangeObject + // var mockBaseDB = new Mock(); + // //mockBaseDB.Setup(z => z.ManualDetectChangeObject).Returns(true); + + + // mockBaseDB.Setup(z => z.DataContext.Set()) + // .Returns>(z => + // { + // var mockDbSet = UnitTestHelper.CreateMockDbSet(dataList); + // return mockDbSet.Object; + // }); + + // var repo = new ClientRepositoryBase(mockBaseDB.Object); + + // return (Repository: repo, DataList: dataList); + //} + + ///// + ///// Get a repository instance of the specified type with preset Mock behavior + ///// + ///// Entity type + ///// Warehousing example + //public (Mock> MockRepository, List DataList) GetRespository() where T : EntityBase + //{ + // var repo = GetRespository(); + + // var dataList = repo.DataList; + + // // Mock Update method + // repo.MockRepository.Setup(z => z.Update(It.IsAny())) + // .Callback(obj => + // { + // var existing = dataList.FirstOrDefault(o => o == obj); + // if (existing != null) + // { + // var index = dataList.IndexOf(existing); + // dataList[index] = obj; + // } + // }); + + // // Mock SaveAsync method + // repo.MockRepository.Setup(z => z.SaveAsync(It.IsAny())) + // .Returns(obj => + // { + // var existing = dataList.FirstOrDefault(o => o == obj); + // if (existing != null) + // { + // var index = dataList.IndexOf(existing); + // dataList[index] = obj; + // } + // else + // { + // dataList.Add(obj); + // } + // return Task.CompletedTask; + // }); + + // return repo; + //} + + //public (Mock> MockRepository, List DataList) GetRespository() where T : EntityBase + //{ + // TryInitSeedData(); + + // var dataList = dataLists[typeof(T)].Cast().ToList(); + + // var mockRepository = new Mock>(); + + // //Set RepositoryBase.BaseDB.ManualDetectChangeObject + // var mockBaseDB = new Mock(); + // //mockBaseDB.Setup(z => z.ManualDetectChangeObject).Returns(true); + + + // //mockBaseDB.Setup(z => z.DataContext.Set()) + // // .Returns>(z => + // // { + // // var mockDbSet = UnitTestHelper.CreateMockDbSet(dataList); + // // return mockDbSet.Object; + // // }); + + + // mockBaseDB.SetupProperty(z => z.ManualDetectChangeObject, true); + // mockRepository.Setup(z => z.BaseDB).Returns(mockBaseDB.Object); + + + // // Mock GetFirstOrDefaultObjectAsync method + // mockRepository.Setup(z => z.GetFirstOrDefaultObjectAsync(It.IsAny>>(), It.IsAny())) + // .Returns>, string[]>((expr, includes) => + // { + // var func = expr.Compile(); + // return Task.FromResult(dataList.FirstOrDefault(func)); + // }); + + // // Mock GetFirstOrDefaultObject method + // mockRepository.Setup(z => z.GetFirstOrDefaultObject(It.IsAny>>(), It.IsAny())) + // .Returns>, string[]>((expr, includes) => + // { + // var func = expr.Compile(); + // return dataList.FirstOrDefault(func); + // }); + + // // Mock GeAll method + // mockRepository.Setup(z => z.GeAll(It.IsAny>>(), It.IsAny(), It.IsAny())) + // .Returns>, OrderingType, string[]>((orderBy, orderingType, includes) => + // { + // return dataList.AsQueryable().OrderBy(orderBy); + // }); + + // // Mock ObjectCount method + // mockRepository.Setup(z => z.ObjectCount(It.IsAny>>(), It.IsAny())) + // .Returns>, string[]>((where, includes) => + // { + // var func = where.Compile(); + // return dataList.Count(func); + // }); + + // // Mock Add method + // mockRepository.Setup(z => z.Add(It.IsAny())) + // .Callback(obj => + // { + // dataList.Add(obj); + // }); + + // // Mock Delete method + // mockRepository.Setup(z => z.Delete(It.IsAny(), It.IsAny())) + // .Callback((obj, softDelete) => + // { + // dataList.Remove(obj); + // }); + + // // Mock SaveChanges method + // mockRepository.Setup(z => z.SaveChanges()) + // .Callback(() => { /* No-op for in-memory data */ }); + + // // Mock DeleteAsync method + // mockRepository.Setup(z => z.DeleteAsync(It.IsAny(), It.IsAny())) + // .Returns((obj, softDelete) => + // { + // dataList.Remove(obj); + // return Task.CompletedTask; + // }); + + // // Mock SaveChangesAsync method + // mockRepository.Setup(z => z.SaveChangesAsync()) + // .Returns(Task.CompletedTask); + + + // //// Generic version of Mock settings + // //mockRepository.Setup(z => z.GetObjectListAsync(It.IsAny>>(), It.IsAny>>(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + // // .Returns>, Expression>, OrderingType, int, int, string[]>((where, orderBy, orderingType, pageIndex, pageCount, includes) => + // // { + // // Console.WriteLine("GetObjectListAsync run with TOrderProperty:" + typeof(T).Name); + // // var func = where.Compile(); + // // var orderedData = orderingType == OrderingType.Ascending ? + // // dataList.AsQueryable().Where(where).OrderBy(orderBy) : + // // dataList.AsQueryable().Where(where).OrderByDescending(orderBy); + // // return Task.FromResult(new PagedList(orderedData.Skip((pageIndex - 1) * pageCount).Take(pageCount).ToList(), pageIndex, pageCount, dataList.Count)); + // // }); + + + // // Mock GetObjectListAsync method + // mockRepository.Setup(z => z.GetObjectListAsync(It.IsAny>>(), It.IsAny>>(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + // .Returns>, Expression>, OrderingType, int, int, string[]>((where, orderBy, orderingType, pageIndex, pageCount, includes) => + // { + // var func = where.Compile(); + // var orderedData = orderingType == OrderingType.Ascending ? + // dataList.AsQueryable().Where(where).OrderBy(orderBy) : + // dataList.AsQueryable().Where(where).OrderByDescending(orderBy); + + // var result = pageIndex <= 0 && pageCount <= 0 + // ? orderedData.ToList() + // : orderedData.Skip((pageIndex - 1) * pageCount).Take(pageCount).ToList(); + + // return Task.FromResult(new PagedList(result, pageIndex, pageCount, dataList.Count)); + // }); + + // //GetObjectListAsync(Expression> where, string OrderbyField, int pageIndex, int pageCount, params string[] includes) + // mockRepository.Setup(repo => repo.GetObjectListAsync( + // It.IsAny>>(), + // It.IsAny(), + // It.IsAny(), + // It.IsAny(), + // It.IsAny())) + // .Returns>, string, int, int, string[]>((where, OrderbyField, pageIndex, pageCount, includes) => + // { + // var func = where.Compile(); + // var orderedData = dataList.AsQueryable().Where(where).OrderByExtension(OrderbyField); + + // var result = pageIndex <= 0 && pageCount <= 0 + // ? orderedData.ToList() + // : orderedData.Skip((pageIndex - 1) * pageCount).Take(pageCount).ToList(); + + // return Task.FromResult(new PagedList(result, pageIndex, pageCount, dataList.Count)); + // }); ; + + // // Mock GetFirstOrDefaultObjectAsync Method (with includesNavigationPropertyPathFunc) + // mockRepository.Setup(z => z.GetFirstOrDefaultObjectAsync(It.IsAny>>(), It.IsAny, IIncludableQueryable>>>())) + // .Returns>, Expression, IIncludableQueryable>>>((where, includesNavigationPropertyPathFunc) => + // { + // //TODO:To be improved + // var func = where.Compile(); + // return Task.FromResult(dataList.FirstOrDefault(func)); + // }); + + // mockRepository.Setup(z => z.GetFirstOrDefaultObjectAsync( + // It.IsAny>>(), + // It.IsAny>>(), + // It.IsAny(), + // It.IsAny, IIncludableQueryable>>>())) + // .Returns>, Expression>, OrderingType, Expression, IIncludableQueryable>>>( + // async (where, orderBy, orderingType, includesNavigationPropertyPathFunc) => + // { + // var mockDbSet = UnitTestHelper.CreateMockDbSet(dataList); + + // // Using Mock DbSet + // var query = await includesNavigationPropertyPathFunc.Compile()(mockDbSet.Object) + // .OrderBy(orderBy, orderingType) + // .Where(where) + // .OrderBy(orderBy, orderingType).FirstOrDefaultAsync(); + + // return query; + // }); + + + // // Mock ObjectCountAsync method + // mockRepository.Setup(z => z.ObjectCountAsync(It.IsAny>>(), It.IsAny())) + // .Returns>, string[]>((where, includes) => + // { + // var func = where.Compile(); + // return Task.FromResult(dataList.Count(func)); + // }); + + // return (MockRepository: mockRepository, DataList: dataList); + //} + + ///// + ///// Get a repository instance of the specified type with preset Mock behavior + ///// + ///// Entity type (OrderBy defaults to int type, include defaults to object type) + ///// Warehousing example + //public (Mock> MockRepository, List DataList) GetRespository() where T : EntityBase + //{ + // var result = this.GetRespository(); + // return result; + //} + + ///// + ///// Get the warehousing instance of the specified type + ///// + ///// + ///// + //public IClientRepositoryBase GetRespositoryObject() where T : EntityBase + //{ + // return this.GetRespository().MockRepository.Object; + //} + + //public Mock CreateMockForExtendedInterface(Mock baseMock) + // where TInterface : class, TBase + // where TBase : class + //{ + // return baseMock.As(); + //} +// } +} diff --git a/src/Basic/Senparc.Ncf.UnitTestExtension/BaseNcfUnitTest.cs b/src/Basic/Senparc.Ncf.UnitTestExtension/BaseNcfUnitTest.cs index 88e946392..17b6c6a95 100644 --- a/src/Basic/Senparc.Ncf.UnitTestExtension/BaseNcfUnitTest.cs +++ b/src/Basic/Senparc.Ncf.UnitTestExtension/BaseNcfUnitTest.cs @@ -1,267 +1,263 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Diagnostics; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Moq; -using Senparc.CO2NET; -using Senparc.CO2NET.RegisterServices; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Database; -using Senparc.Ncf.Database.InMemory; -using Senparc.Ncf.UnitTestExtension.Database; -using Senparc.Ncf.UnitTestExtension.Entities; -using Senparc.Ncf.XncfBase; -using Senparc.Xncf.SystemCore.Domain.Database; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace Senparc.Ncf.UnitTestExtension -{ - public class BaseNcfUnitTest - { - private IServiceCollection ServiceCollection { get; set; } - public IConfiguration Configuration { get; set; } - - public IHostEnvironment Env { get; set; } - - protected IRegisterService registerService; - protected SenparcSetting _senparcSetting; - - protected IServiceProvider _serviceProvider; - protected static DataList GlobalDataList = new DataList(Guid.NewGuid().ToString()); - protected static ConcurrentDictionary GlobalDataListCollection = new ConcurrentDictionary(); - - //public BaseNcfUnitTest() : this(null, null) - //{ - - //} - - /// - /// 构造函数,用于初始化服务提供者和种子数据 - /// - /// 在启动时注册 ServiceCollection 的委托 - /// 初始化种子数据的委托 - public BaseNcfUnitTest(Action servicesRegister = null, UnitTestSeedDataBuilder seedDataBuilder = null) - { - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - - var builder = WebApplication.CreateBuilder(); - ServiceCollection = builder.Services; - - var mockEnv = new Mock(); - mockEnv.Setup(z => z.ContentRootPath).Returns(() => UnitTestHelper.RootPath); - mockEnv.Setup(z => z.EnvironmentName).Returns(() => "Test"); - - Env = mockEnv.Object; - - servicesRegister?.Invoke(ServiceCollection); - - //Console.WriteLine("Test Data:" + dataLists.ToJson(true, new Newtonsoft.Json.JsonSerializerSettings() - //{ - // ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore - //})); - - //var configuration = builder.Configuration; - //configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - // .AddJsonFile($"appsettings.{Env.EnvironmentName}.json", optional: true, reloadOnChange: true); - - - BeforeRegisterServiceCollection(ServiceCollection); - RegisterServiceCollection(); - RegisterServiceCollectionFinished(ServiceCollection); - - BuildServiceProvider(); - - RegisterServiceStart(); - - var app = builder.Build(); - app.UseNcfDatabase(); - - app.UseXncfModules(registerService); - - #region 填充种子数据 - //执行前准备 - var dataList = seedDataBuilder?.ExecuteAsync(this._serviceProvider).GetAwaiter().GetResult(); - - dataList ??= new DataList(Guid.Empty.ToString("N")); - - if (!GlobalDataListCollection.ContainsKey(dataList.UUID)) - { - GlobalDataListCollection[dataList.UUID] = dataList; - GlobalDataList.AddRange(dataList); - - //自动填充 - AutoFillSeedData(seedDataBuilder, dataList); - //填充后 - seedDataBuilder?.OnExecutedAsync(this._serviceProvider, dataList).GetAwaiter().GetResult(); - } - - #endregion - } - - private void AutoFillSeedData(UnitTestSeedDataBuilder seedDataBuilder, DataList dataLists) - { - using (var scope = this._serviceProvider.CreateScope()) - { - var serviceProvider = scope.ServiceProvider; - var dbContext = serviceProvider.GetService(); - //填充所有数据 - foreach (var dataListKv in dataLists) - { - var type = dataListKv.Key; - var dataList = dataListKv.Value; - - //设置 DbSet - var binder = Type.DefaultBinder; - var method = dbContext.GetType().GetMethod(nameof(dbContext.Set), BindingFlags.Instance | BindingFlags.Public, binder, Type.EmptyTypes, null).MakeGenericMethod(type); - var dbSet = method.Invoke(dbContext, null); - - //转换 data 类型 - var listType = typeof(List<>).MakeGenericType(type); - var castMethod = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(type); - var toListMethod = typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(type); - - var castData = castMethod.Invoke(null, new object[] { dataList }); - var listData = toListMethod.Invoke(null, new object[] { castData }); - - //添加到 DbSet - var addRangeMethod = dbSet.GetType().GetMethod("AddRange", new[] { listType }); - addRangeMethod.Invoke(dbSet, new[] { listData }); - - dbContext.SaveChanges(); - } - } - } - - protected virtual void BeforeRegisterServiceCollection(IServiceCollection services) - { - Console.WriteLine("BaseNcfUnitTest.BeforeRegisterServiceCollection"); - } - - protected virtual void RegisterServiceCollectionFinished(IServiceCollection services) - { - - } - - /// - /// 尝试初始化指定类型的种子数据 - /// - /// 实体类型 - public void TryInitSeedData() where T : EntityBase - { - if (!GlobalDataList.ContainsKey(typeof(T))) - { - GlobalDataList[typeof(T)] = new List(); - } - } - - - - public void BuildServiceProvider(IServiceCollection services = null) - { - if (services != null) - { - this.ServiceCollection = services; - } - this._serviceProvider = this.ServiceCollection.BuildServiceProvider(); - - } - - protected virtual void ActionInServiceCollection() - { - - } - - /// - /// 注册 IServiceCollection 和 MemoryCache - /// - public void RegisterServiceCollection() - { - var configBuilder = new ConfigurationBuilder(); - configBuilder.AddJsonFile(UnitTestHelper.GetAppSettingsFile(Env), false, false); - var config = configBuilder.Build(); - Configuration = config; - - _senparcSetting = new SenparcSetting() { IsDebug = true }; - config.GetSection("SenparcSetting").Bind(_senparcSetting); - - ServiceCollection.AddSenparcGlobalServices(config); - ServiceCollection.AddMemoryCache();//使用内存缓存 - - ActionInServiceCollection(); - - //设置单元测试默认 DbContext(需要在 StartNcfEngine 之前) - MultipleDatabasePool.UnitTestPillarDbContext = typeof(NcfUnitTestEntities); - - var result = Senparc.Ncf.XncfBase.Register.StartNcfEngine(ServiceCollection, Configuration, Env, null); - - //覆盖 NCF 基础设置 - ServiceCollection.AddScoped(s => - { - var dbContext = s.GetService(); - return new NcfUnitTestDataDb(dbContext); - }); - - ServiceCollection.AddScoped(s => - { - var dbContext = s.GetService(); - return new NcfUnitTestDataDb(dbContext); - }); - - ServiceCollection.AddScoped(s => - { - //初始化对象 - var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase(databaseName: "UnitTestDb") - //InMemory 不支持事务,如果不希望抛错,则忽略警告 - .ConfigureWarnings(warnings => warnings.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - .Options; - var dbContext = new NcfUnitTestEntities(options, s, GlobalDataList); - - return dbContext; - - }); - } - - /// - /// 注册 RegisterService.Start() - /// - public void RegisterServiceStart(bool autoScanExtensionCacheStrategies = false) - { - //注册 - registerService = Senparc.CO2NET.AspNet.RegisterServices.RegisterService.Start(Env, _senparcSetting) - .UseSenparcGlobal(autoScanExtensionCacheStrategies); - - //配置全局使用Redis缓存(按需,独立) - var redisConfigurationStr = _senparcSetting.Cache_Redis_Configuration; - var useRedis = !string.IsNullOrEmpty(redisConfigurationStr) && redisConfigurationStr != "#{Cache_Redis_Configuration}#"/*默认值,不启用*/; - if (useRedis)//这里为了方便不同环境的开发者进行配置,做成了判断的方式,实际开发环境一般是确定的,这里的if条件可以忽略 - { - /* 说明: - * 1、Redis 的连接字符串信息会从 Config.SenparcSetting.Cache_Redis_Configuration 自动获取并注册,如不需要修改,下方方法可以忽略 - /* 2、如需手动修改,可以通过下方 SetConfigurationOption 方法手动设置 Redis 链接信息(仅修改配置,不立即启用) - */ - Senparc.CO2NET.Cache.CsRedis.Register.SetConfigurationOption(redisConfigurationStr); - Console.WriteLine("完成 Redis 设置"); - - //以下会立即将全局缓存设置为 Redis - Senparc.CO2NET.Cache.CsRedis.Register.UseKeyValueRedisNow();//键值对缓存策略(推荐) - Console.WriteLine("启用 Redis UseKeyValue 策略"); - - //Senparc.CO2NET.Cache.Redis.Register.UseHashRedisNow();//HashSet储存格式的缓存策略 - - //也可以通过以下方式自定义当前需要启用的缓存策略 - //CacheStrategyFactory.RegisterObjectCacheStrategy(() => RedisObjectCacheStrategy.Instance);//键值对 - //CacheStrategyFactory.RegisterObjectCacheStrategy(() => RedisHashSetObjectCacheStrategy.Instance);//HashSet - } - - } - } -} +using Microsoft.AspNetCore.Builder; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Moq; +using Senparc.CO2NET; +using Senparc.CO2NET.RegisterServices; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Database; +using Senparc.Ncf.Database.InMemory; +using Senparc.Ncf.UnitTestExtension.Database; +using Senparc.Ncf.UnitTestExtension.Entities; +using Senparc.Ncf.XncfBase; +using Senparc.Xncf.SystemCore.Domain.Database; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Senparc.Ncf.UnitTestExtension +{ + public class BaseNcfUnitTest + { + private IServiceCollection ServiceCollection { get; set; } + public IConfiguration Configuration { get; set; } + + public IHostEnvironment Env { get; set; } + + protected IRegisterService registerService; + protected SenparcSetting _senparcSetting; + + protected IServiceProvider _serviceProvider; + protected static DataList GlobalDataList = new DataList(Guid.NewGuid().ToString()); + protected static ConcurrentDictionary GlobalDataListCollection = new ConcurrentDictionary(); + + //public BaseNcfUnitTest() : this(null, null) + //{ + + //} + + /// + /// Constructor to initialize the service provider and seed data + /// + /// Register the ServiceCollection's delegate at startup + /// Initialize the delegate for seed data + public BaseNcfUnitTest(Action servicesRegister = null, UnitTestSeedDataBuilder seedDataBuilder = null) + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + + var builder = WebApplication.CreateBuilder(); + ServiceCollection = builder.Services; + + var mockEnv = new Mock(); + mockEnv.Setup(z => z.ContentRootPath).Returns(() => UnitTestHelper.RootPath); + mockEnv.Setup(z => z.EnvironmentName).Returns(() => "Test"); + + Env = mockEnv.Object; + + servicesRegister?.Invoke(ServiceCollection); + + //Console.WriteLine("Test Data:" + dataLists.ToJson(true, new Newtonsoft.Json.JsonSerializerSettings() + //{ + // ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore + //})); + + //var configuration = builder.Configuration; + //configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + // .AddJsonFile($"appsettings.{Env.EnvironmentName}.json", optional: true, reloadOnChange: true); + + + BeforeRegisterServiceCollection(ServiceCollection); + RegisterServiceCollection(); + RegisterServiceCollectionFinished(ServiceCollection); + + BuildServiceProvider(); + + RegisterServiceStart(); + + var app = builder.Build(); + app.UseNcfDatabase(); + + app.UseXncfModules(registerService); + + #region Populate seed data//Preparation before execution + var dataList = seedDataBuilder?.ExecuteAsync(this._serviceProvider).GetAwaiter().GetResult(); + + dataList ??= new DataList(Guid.Empty.ToString("N")); + + if (!GlobalDataListCollection.ContainsKey(dataList.UUID)) + { + GlobalDataListCollection[dataList.UUID] = dataList; + GlobalDataList.AddRange(dataList); + + //autofill + AutoFillSeedData(seedDataBuilder, dataList); + //After filling + seedDataBuilder?.OnExecutedAsync(this._serviceProvider, dataList).GetAwaiter().GetResult(); + } + + #endregion + } + + private void AutoFillSeedData(UnitTestSeedDataBuilder seedDataBuilder, DataList dataLists) + { + using (var scope = this._serviceProvider.CreateScope()) + { + var serviceProvider = scope.ServiceProvider; + var dbContext = serviceProvider.GetService(); + //Populate all data + foreach (var dataListKv in dataLists) + { + var type = dataListKv.Key; + var dataList = dataListKv.Value; + + //SetupDbSet + var binder = Type.DefaultBinder; + var method = dbContext.GetType().GetMethod(nameof(dbContext.Set), BindingFlags.Instance | BindingFlags.Public, binder, Type.EmptyTypes, null).MakeGenericMethod(type); + var dbSet = method.Invoke(dbContext, null); + + //Convert data type + var listType = typeof(List<>).MakeGenericType(type); + var castMethod = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(type); + var toListMethod = typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(type); + + var castData = castMethod.Invoke(null, new object[] { dataList }); + var listData = toListMethod.Invoke(null, new object[] { castData }); + + //Add to DbSet + var addRangeMethod = dbSet.GetType().GetMethod("AddRange", new[] { listType }); + addRangeMethod.Invoke(dbSet, new[] { listData }); + + dbContext.SaveChanges(); + } + } + } + + protected virtual void BeforeRegisterServiceCollection(IServiceCollection services) + { + Console.WriteLine("BaseNcfUnitTest.BeforeRegisterServiceCollection"); + } + + protected virtual void RegisterServiceCollectionFinished(IServiceCollection services) + { + + } + + /// + /// Attempts to initialize seed data of the specified type + /// + /// Entity type + public void TryInitSeedData() where T : EntityBase + { + if (!GlobalDataList.ContainsKey(typeof(T))) + { + GlobalDataList[typeof(T)] = new List(); + } + } + + + + public void BuildServiceProvider(IServiceCollection services = null) + { + if (services != null) + { + this.ServiceCollection = services; + } + this._serviceProvider = this.ServiceCollection.BuildServiceProvider(); + + } + + protected virtual void ActionInServiceCollection() + { + + } + + /// + /// Register IServiceCollection and MemoryCache + /// + public void RegisterServiceCollection() + { + var configBuilder = new ConfigurationBuilder(); + configBuilder.AddJsonFile(UnitTestHelper.GetAppSettingsFile(Env), false, false); + var config = configBuilder.Build(); + Configuration = config; + + _senparcSetting = new SenparcSetting() { IsDebug = true }; + config.GetSection("SenparcSetting").Bind(_senparcSetting); + + ServiceCollection.AddSenparcGlobalServices(config); + ServiceCollection.AddMemoryCache();//Use memory cache + + ActionInServiceCollection(); + + //Set the unit test default DbContext (needs before StartNcfEngine) + MultipleDatabasePool.UnitTestPillarDbContext = typeof(NcfUnitTestEntities); + + var result = Senparc.Ncf.XncfBase.Register.StartNcfEngine(ServiceCollection, Configuration, Env, null); + + //Override NCF basic settings + ServiceCollection.AddScoped(s => + { + var dbContext = s.GetService(); + return new NcfUnitTestDataDb(dbContext); + }); + + ServiceCollection.AddScoped(s => + { + var dbContext = s.GetService(); + return new NcfUnitTestDataDb(dbContext); + }); + + ServiceCollection.AddScoped(s => + { + //initialize object + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: "UnitTestDb") + //InMemory Transactions are not supported, ignore warnings if you do not want to throw errors + .ConfigureWarnings(warnings => warnings.Ignore(InMemoryEventId.TransactionIgnoredWarning)) + .Options; + var dbContext = new NcfUnitTestEntities(options, s, GlobalDataList); + + return dbContext; + + }); + } + + /// + /// Register RegisterService.Start() + /// + public void RegisterServiceStart(bool autoScanExtensionCacheStrategies = false) + { + //register + registerService = Senparc.CO2NET.AspNet.RegisterServices.RegisterService.Start(Env, _senparcSetting) + .UseSenparcGlobal(autoScanExtensionCacheStrategies); + + //Configure global use of Redis cache (on-demand, independent) + var redisConfigurationStr = _senparcSetting.Cache_Redis_Configuration; + var useRedis = !string.IsNullOrEmpty(redisConfigurationStr) && redisConfigurationStr != "#{Cache_Redis_Configuration}#"/*Default value, not enabled*/; + if (useRedis)//In order to facilitate the configuration of developers in different environments, a judgment method is made here. The actual development environment is generally determined, and the if condition here can be ignored. + { + /* illustrate:* 1、Redis The connection string information will be retrieved from Config.SenparcSetting.Cache_Redis_Configuration Automatically obtain and register. If no modification is required, the following method can be ignored./* 2、If you need to modify it manually, you can manually set the Redis link information through the SetConfigurationOption method below (only modify the configuration, not enable it immediately)*/ + Senparc.CO2NET.Cache.CsRedis.Register.SetConfigurationOption(redisConfigurationStr); + Console.WriteLine("Complete Redis setup"); + + //The following will immediately set the global cache to Redis + Senparc.CO2NET.Cache.CsRedis.Register.UseKeyValueRedisNow();//Key-value pair caching strategy (recommended) + Console.WriteLine("Enable Redis UseKeyValue policy"); + + //Senparc.CO2NET.Cache.Redis.Register.UseHashRedisNow();//HashSetStorage format caching strategy + + //You can also customize the caching strategy that currently needs to be enabled in the following ways + //CacheStrategyFactory.RegisterObjectCacheStrategy(() => RedisObjectCacheStrategy.Instance);//key value pair + //CacheStrategyFactory.RegisterObjectCacheStrategy(() => RedisHashSetObjectCacheStrategy.Instance);//HashSet + } + + } + } +} diff --git a/src/Basic/Senparc.Ncf.UnitTestExtension/Database/NcfUnitTestDataDb.cs b/src/Basic/Senparc.Ncf.UnitTestExtension/Database/NcfUnitTestDataDb.cs index ff0dcf757..3015819d3 100644 --- a/src/Basic/Senparc.Ncf.UnitTestExtension/Database/NcfUnitTestDataDb.cs +++ b/src/Basic/Senparc.Ncf.UnitTestExtension/Database/NcfUnitTestDataDb.cs @@ -1,67 +1,67 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Senparc.Ncf.Core.Models; -using Senparc.Xncf.SystemCore.Domain.Database; - -namespace Senparc.Ncf.UnitTestExtension.Database -{ - - public class NcfUnitTestDataDb : NcfDbData, INcfDbData, INcfClientDbData - { - private NcfUnitTestEntities dataContext; - - public NcfUnitTestDataDb() { } - - public NcfUnitTestDataDb(NcfUnitTestEntities dbContext) - { - dataContext = dbContext; - } - - //public NcfUnitTestDataDb(IServiceProvider serviceProvider) - //{ - // dataContext = new NcfUnitTestEntities(new DbContextOptionsBuilder().Options, serviceProvider); - //} - - - - public virtual BasePoolEntities DataContext - { - get - { - return BaseDataContext as BasePoolEntities; - } - } - - /// - /// 关闭连接(长时间保持一个连接操作会导致数据库操作时间逐渐变长) - /// - public override void CloseConnection() - { - //COCONET 升级到.net core的过程中注释掉 - //BaseDataContext.Database.Connection.Close(); - dataContext = null; - } - - public override DbContext BaseDataContext - { - get - { - if (dataContext == null) - { - //var connectionString = Ncf.Core.Config.SenparcDatabaseConfigs.ClientConnectionString; - - //dataContext = SenparcDI.GetService(); - //TODO:当前采用注入可以保证HttpContext单例,如果要全局单例,可采用单件模式(需要先解决释放的问题) - } - //var hashCode = dataContext.GetHashCode(); - //System.Web.HttpContext.Current.Response.Write(dataContext.GetHashCode() + "
");//测试同一Request只有一个SenparcEntities实例 - return dataContext; - } - - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Senparc.Ncf.Core.Models; +using Senparc.Xncf.SystemCore.Domain.Database; + +namespace Senparc.Ncf.UnitTestExtension.Database +{ + + public class NcfUnitTestDataDb : NcfDbData, INcfDbData, INcfClientDbData + { + private NcfUnitTestEntities dataContext; + + public NcfUnitTestDataDb() { } + + public NcfUnitTestDataDb(NcfUnitTestEntities dbContext) + { + dataContext = dbContext; + } + + //public NcfUnitTestDataDb(IServiceProvider serviceProvider) + //{ + // dataContext = new NcfUnitTestEntities(new DbContextOptionsBuilder().Options, serviceProvider); + //} + + + + public virtual BasePoolEntities DataContext + { + get + { + return BaseDataContext as BasePoolEntities; + } + } + + /// + /// Close the connection (maintaining a connection operation for a long time will cause the database operation time to gradually become longer) + /// + public override void CloseConnection() + { + //COCONET upgrade to.net coreComment out the process + //BaseDataContext.Database.Connection.Close(); + dataContext = null; + } + + public override DbContext BaseDataContext + { + get + { + if (dataContext == null) + { + //var connectionString = Ncf.Core.Config.SenparcDatabaseConfigs.ClientConnectionString; + + //dataContext = SenparcDI.GetService(); + //TODO:Currently, injection can be used to ensure the HttpContext singleton. If you want a global singleton, you can use the singleton mode (you need to solve the release problem first) + } + //var hashCode = dataContext.GetHashCode(); + //System.Web.HttpContext.Current.Response.Write(dataContext.GetHashCode() + "
");//Test that there is only one SenparcEntities instance for the same Request + return dataContext; + } + + } + } +} diff --git a/src/Basic/Senparc.Ncf.UnitTestExtension/Database/NcfUnitTestEntities.cs b/src/Basic/Senparc.Ncf.UnitTestExtension/Database/NcfUnitTestEntities.cs index 4feee5de9..016021494 100644 --- a/src/Basic/Senparc.Ncf.UnitTestExtension/Database/NcfUnitTestEntities.cs +++ b/src/Basic/Senparc.Ncf.UnitTestExtension/Database/NcfUnitTestEntities.cs @@ -1,51 +1,51 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using Microsoft.EntityFrameworkCore; -using Senparc.Ncf.UnitTestExtension.Entities; -using Senparc.Xncf.SystemCore.Domain.Database; - -namespace Senparc.Ncf.UnitTestExtension.Database -{ - /// - /// 模拟 SenparcEntities 或各个 XNCF 模块中的 xxxSenparcEntities。 - /// 在单元测试过程中,这个类将取代 NCF 运行时的 SenparcEntities,集成所有模块的 DbSet 对象 - /// - public class NcfUnitTestEntities : BasePoolEntities //DbContext - { - DataList _dataList; - private readonly Dictionary _dbSets = new(); - - public NcfUnitTestEntities(DbContextOptions dbContextOptions, IServiceProvider serviceProvider, DataList dataList) : base(dbContextOptions, serviceProvider) - { - _dataList = dataList; - } - - public static IList ConvertList(IList list, Type outType) - { - var genericListType = typeof(List<>).MakeGenericType(outType); - var convertedList = (IList)Activator.CreateInstance(genericListType); - - foreach (var item in list) - { - convertedList.Add(Convert.ChangeType(item, outType)); - } - return convertedList; - } - - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - Console.WriteLine("NcfUnitTestEntities OnConfiguring"); - - base.OnConfiguring(optionsBuilder); - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - Console.WriteLine("NcfUnitTestEntities OnModelCreating"); - - base.OnModelCreating(modelBuilder); - } - } -} +using System; +using System.Collections; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Senparc.Ncf.UnitTestExtension.Entities; +using Senparc.Xncf.SystemCore.Domain.Database; + +namespace Senparc.Ncf.UnitTestExtension.Database +{ + /// + /// Emulate SenparcEntities or xxxSenparcEntities in various XNCF modules. + /// During unit testing, this class will replace the NCF runtime's SenparcEntities, integrating all modules' DbSet objects + /// + public class NcfUnitTestEntities : BasePoolEntities //DbContext + { + DataList _dataList; + private readonly Dictionary _dbSets = new(); + + public NcfUnitTestEntities(DbContextOptions dbContextOptions, IServiceProvider serviceProvider, DataList dataList) : base(dbContextOptions, serviceProvider) + { + _dataList = dataList; + } + + public static IList ConvertList(IList list, Type outType) + { + var genericListType = typeof(List<>).MakeGenericType(outType); + var convertedList = (IList)Activator.CreateInstance(genericListType); + + foreach (var item in list) + { + convertedList.Add(Convert.ChangeType(item, outType)); + } + return convertedList; + } + + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + Console.WriteLine("NcfUnitTestEntities OnConfiguring"); + + base.OnConfiguring(optionsBuilder); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + Console.WriteLine("NcfUnitTestEntities OnModelCreating"); + + base.OnModelCreating(modelBuilder); + } + } +} diff --git a/src/Basic/Senparc.Ncf.UnitTestExtension/Entities/DataList.cs b/src/Basic/Senparc.Ncf.UnitTestExtension/Entities/DataList.cs index 726a2c033..8effda158 100644 --- a/src/Basic/Senparc.Ncf.UnitTestExtension/Entities/DataList.cs +++ b/src/Basic/Senparc.Ncf.UnitTestExtension/Entities/DataList.cs @@ -1,98 +1,98 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Senparc.Ncf.UnitTestExtension.Entities -{ - public class DataList : Dictionary> - { - /// - /// - /// - public string UUID { get; set; } - - /// - /// 获取强类型的列表 - /// - /// - /// - public List GetDataList() - { - if (!base.ContainsKey(typeof(T))) - { - return null; - } - - return base[typeof(T)].Cast().ToList(); - } - - public List GetList(Type type) - { - if (!base.ContainsKey(type)) - { - return null; - } - - return base[type]; - } - - - /// - /// 快速添加实体类型的列表数据 - /// - /// - /// - public void Add(List list) - { - if (base.ContainsKey(typeof(T))) - { - base[typeof(T)].AddRange(list.Cast().ToList()); - } - else - { - base[typeof(T)] = list.Cast().ToList(); - } - } - - /// - /// 快速添加 个对象,兼备自动根据类型分类 - /// - /// - /// - public void AddItem(T item) - { - Add(new List() { item }); - } - - /// - /// 快速添加实体类型的列表数据 - /// - /// - public void AddRange(DataList dataList) - { - foreach (var item in dataList) - { - if (base.ContainsKey(item.Key)) - { - base[item.Key].AddRange(item.Value); - } - else - { - base[item.Key] = item.Value; - } - } - } - - /// - /// 创建 DataList - /// - /// 唯一编号,相同编号的 DataList 数据在同一次单元门测试中不会被反复写入 - public DataList(string uUID) - { - UUID = uUID; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Senparc.Ncf.UnitTestExtension.Entities +{ + public class DataList : Dictionary> + { + /// + /// + /// + public string UUID { get; set; } + + /// + /// Get a strongly typed list + /// + /// + /// + public List GetDataList() + { + if (!base.ContainsKey(typeof(T))) + { + return null; + } + + return base[typeof(T)].Cast().ToList(); + } + + public List GetList(Type type) + { + if (!base.ContainsKey(type)) + { + return null; + } + + return base[type]; + } + + + /// + /// Quickly add list data of entity types + /// + /// + /// + public void Add(List list) + { + if (base.ContainsKey(typeof(T))) + { + base[typeof(T)].AddRange(list.Cast().ToList()); + } + else + { + base[typeof(T)] = list.Cast().ToList(); + } + } + + /// + /// Quickly add objects, with automatic classification by type + /// + /// + /// + public void AddItem(T item) + { + Add(new List() { item }); + } + + /// + /// Quickly add list data of entity types + /// + /// + public void AddRange(DataList dataList) + { + foreach (var item in dataList) + { + if (base.ContainsKey(item.Key)) + { + base[item.Key].AddRange(item.Value); + } + else + { + base[item.Key] = item.Value; + } + } + } + + /// + /// Create DataList + /// + /// Unique number. DataList data with the same number will not be written repeatedly in the same unit gate test. + public DataList(string uUID) + { + UUID = uUID; + } + } +} diff --git a/src/Basic/Senparc.Ncf.UnitTestExtension/UnitTestHelper.cs b/src/Basic/Senparc.Ncf.UnitTestExtension/UnitTestHelper.cs index 9665098f9..4380bf473 100644 --- a/src/Basic/Senparc.Ncf.UnitTestExtension/UnitTestHelper.cs +++ b/src/Basic/Senparc.Ncf.UnitTestExtension/UnitTestHelper.cs @@ -1,76 +1,76 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Hosting; -using Moq; - -namespace Senparc.Ncf.UnitTestExtension -{ - public static class UnitTestHelper - { - /// - /// 根目录地址 - /// - public static string RootPath => Path.GetFullPath("..\\..\\..\\"); - - - /// - /// 检查关键字存在 - /// - /// - /// - /// - public static bool CheckKeywordsExist(string filePath, params string[] keywords) - { - using (var fs = new FileStream(filePath, FileMode.Open)) - { - using (var sr = new StreamReader(fs)) - { - var content = sr.ReadToEnd(); - foreach (var item in keywords) - { - if (!content.Contains(item)) - { - return false; - } - } - return true; - } - } - } - - /// - /// Get AppSettings file name. - /// - /// - public static string GetAppSettingsFile(IHostEnvironment env=null) - { - var envStr = env?.EnvironmentName ?? "Test"; - var appsettingFile = $"appsettings.{envStr}.json"; - if (File.Exists(appsettingFile)) - { - Console.WriteLine($"use {appsettingFile}"); - return appsettingFile; - } - - Console.WriteLine("use appsettings.json"); - return "appsettings.json"; - } - - public static Mock> CreateMockDbSet(List dataList) where T : class - { - var queryable = dataList.AsQueryable(); - - var mockSet = new Mock>(); - mockSet.As>().Setup(m => m.Provider).Returns(queryable.Provider); - mockSet.As>().Setup(m => m.Expression).Returns(queryable.Expression); - mockSet.As>().Setup(m => m.ElementType).Returns(queryable.ElementType); - mockSet.As>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator()); - - return mockSet; - } - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Hosting; +using Moq; + +namespace Senparc.Ncf.UnitTestExtension +{ + public static class UnitTestHelper + { + /// + /// Root directory address + /// + public static string RootPath => Path.GetFullPath("..\\..\\..\\"); + + + /// + /// Check keyword exists + /// + /// + /// + /// + public static bool CheckKeywordsExist(string filePath, params string[] keywords) + { + using (var fs = new FileStream(filePath, FileMode.Open)) + { + using (var sr = new StreamReader(fs)) + { + var content = sr.ReadToEnd(); + foreach (var item in keywords) + { + if (!content.Contains(item)) + { + return false; + } + } + return true; + } + } + } + + /// + /// Get AppSettings file name. + /// + /// + public static string GetAppSettingsFile(IHostEnvironment env=null) + { + var envStr = env?.EnvironmentName ?? "Test"; + var appsettingFile = $"appsettings.{envStr}.json"; + if (File.Exists(appsettingFile)) + { + Console.WriteLine($"use {appsettingFile}"); + return appsettingFile; + } + + Console.WriteLine("use appsettings.json"); + return "appsettings.json"; + } + + public static Mock> CreateMockDbSet(List dataList) where T : class + { + var queryable = dataList.AsQueryable(); + + var mockSet = new Mock>(); + mockSet.As>().Setup(m => m.Provider).Returns(queryable.Provider); + mockSet.As>().Setup(m => m.Expression).Returns(queryable.Expression); + mockSet.As>().Setup(m => m.ElementType).Returns(queryable.ElementType); + mockSet.As>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator()); + + return mockSet; + } + } +} diff --git a/src/Basic/Senparc.Ncf.UnitTestExtension/UnitTestSeedDataBuilder.cs b/src/Basic/Senparc.Ncf.UnitTestExtension/UnitTestSeedDataBuilder.cs index 4739952c8..b21e9b129 100644 --- a/src/Basic/Senparc.Ncf.UnitTestExtension/UnitTestSeedDataBuilder.cs +++ b/src/Basic/Senparc.Ncf.UnitTestExtension/UnitTestSeedDataBuilder.cs @@ -1,33 +1,33 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Senparc.Ncf.UnitTestExtension.Entities; - -namespace Senparc.Ncf.UnitTestExtension -{ - /// - /// 创建种子数据 - /// - public abstract class UnitTestSeedDataBuilder - { - - - /// - /// 填充种子数据前的操作 - /// - /// - /// - public abstract Task ExecuteAsync(IServiceProvider serviceProvider); - - /// - /// 填充种子数据后的操作 - /// - /// - /// 从 ExecuteAsync 中返回的 dataList - /// - public abstract Task OnExecutedAsync(IServiceProvider serviceProvider, DataList dataList); - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Senparc.Ncf.UnitTestExtension.Entities; + +namespace Senparc.Ncf.UnitTestExtension +{ + /// + /// Create seed data + /// + public abstract class UnitTestSeedDataBuilder + { + + + /// + /// Operations before filling in seed data + /// + /// + /// + public abstract Task ExecuteAsync(IServiceProvider serviceProvider); + + /// + /// What to do after filling in seed data + /// + /// + /// dataList returned from ExecuteAsync + /// + public abstract Task OnExecutedAsync(IServiceProvider serviceProvider, DataList dataList); + } +} diff --git a/src/Basic/Senparc.Ncf.Utility/DIExtension/Extensions.cs b/src/Basic/Senparc.Ncf.Utility/DIExtension/Extensions.cs index 6ce8ced94..8bc56149e 100644 --- a/src/Basic/Senparc.Ncf.Utility/DIExtension/Extensions.cs +++ b/src/Basic/Senparc.Ncf.Utility/DIExtension/Extensions.cs @@ -1,25 +1,25 @@ -using Microsoft.AspNetCore.Builder; -using System; - -namespace Microsoft.Extensions.DependencyInjection -{ - //参考:https://www.cnblogs.com/yuangang/archive/2016/08/08/5743660.html - - public static class Extensions - { - - public static IApplicationBuilder UseSenparcMvcDI(this IApplicationBuilder builder) - { - DI.ServiceProvider = builder.ApplicationServices; - return builder; - } - } - - /// - /// TODO:和SenparcDI合并 - /// - public static class DI - { - public static IServiceProvider ServiceProvider { get; set; } - } -} +using Microsoft.AspNetCore.Builder; +using System; + +namespace Microsoft.Extensions.DependencyInjection +{ + //Reference: https://www.cnblogs.com/yuangang/archive/2016/08/08/5743660.html + + public static class Extensions + { + + public static IApplicationBuilder UseSenparcMvcDI(this IApplicationBuilder builder) + { + DI.ServiceProvider = builder.ApplicationServices; + return builder; + } + } + + /// + ///TODO: Merge with SenparcDI + /// + public static class DI + { + public static IServiceProvider ServiceProvider { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.Utility/ExpressionExtension/SenparcExpressionHelper.cs b/src/Basic/Senparc.Ncf.Utility/ExpressionExtension/SenparcExpressionHelper.cs index 53cbd89e1..385704f91 100644 --- a/src/Basic/Senparc.Ncf.Utility/ExpressionExtension/SenparcExpressionHelper.cs +++ b/src/Basic/Senparc.Ncf.Utility/ExpressionExtension/SenparcExpressionHelper.cs @@ -1,167 +1,167 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; - -namespace Senparc.Ncf.Utility -{ - /// - /// 支持关联表查询 - /// - /// 类型如: - /// var seh = new SenparcExpressionHelper(); - /// seh.ValueCompare - /// .AndAlso(true, i => !i.IsDeleted && i.MPAccountId == MPAccount.Id) - /// .AndAlso(!string.IsNullOrEmpty(SearchText), i => i.MPKeywordList.Count(c => c.Content.Contains(SearchText)) > 0); - /// var where = seh.BuildWhereExpression(); - /// - /// 其中关联表的Count统计,在SenparcExpressionHelper下,会吧变量c当作主表属性而报错 - /// - /// - public class SenparcExpressionHelper where TEntity : class - { - public ValueCompare ValueCompare { get; set; } - public SenparcExpressionHelper() - { - ValueCompare = new ValueCompare(); - } - - /// - /// 生成where表达式 - /// - /// - public Expression> BuildWhereExpression() - { - if (ValueCompare.ExpressionBody == null) - { - //不需要查询 - Expression> returnTrue = z => true; - return returnTrue; - } - var where = ValueCompare.ExpressionBody; - return where; - } - } - public class ValueCompare - { - public Expression> ExpressionBody { get; set; } - public ValueCompare() - { - } - /// - /// Combines the first predicate with the second using the logical "and". - /// - public ValueCompare AndAlso(bool combineWhenTrue, Expression> filter) - { - return Combine(combineWhenTrue, filter, Expression.AndAlso); - } - /// - /// Combines the first predicate with the second using the logical "or". - /// - public ValueCompare OrElse(bool combineWhenTrue, Expression> filter) - { - return Combine(combineWhenTrue, filter, Expression.OrElse); - } - - /// - /// Negates the predicate. - /// - public ValueCompare Not() - { - var negated = Expression.Not(ExpressionBody.Body); - ExpressionBody = Expression.Lambda>(negated, ExpressionBody.Parameters); - return this; - } - public ValueCompare Combine(Expression> filter, Func merge) - { - if (ExpressionBody == null) - { - ExpressionBody = PredicateBuilder.Create(filter); - return this; - } - ExpressionBody = ExpressionBody.Compose(filter, merge); - - return this; - } - public ValueCompare Combine(bool combineWhenTrue, Expression> filter, Func merge) - { - if (!combineWhenTrue) - { - return this; - } - return Combine(filter, merge); - } - } - public static class PredicateBuilder - { - /// - /// Creates a predicate expression from the specified lambda expression. - /// - public static Expression> Create(Expression> predicate) { return predicate; } - - /// - /// Combines the first expression with the second using the specified merge function. - /// - public static Expression Compose(this Expression first, Expression second, Func merge) - { - // zip parameters (map from parameters of second to parameters of first) - var map = first.Parameters - .Select((f, i) => new { f, s = second.Parameters[i] }) - .ToDictionary(p => p.s, p => p.f); - - // replace parameters in the second lambda expression with the parameters in the first - var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); - - // create a merged lambda expression with parameters from the first expression - return Expression.Lambda(merge(first.Body, secondBody), first.Parameters); - } - - /// - /// ParameterRebinder - /// - class ParameterRebinder : ExpressionVisitor - { - /// - /// The ParameterExpression map - /// - readonly Dictionary map; - - /// - /// Initializes a new instance of the class. - /// - /// The map. - ParameterRebinder(Dictionary map) - { - this.map = map ?? new Dictionary(); - } - - /// - /// Replaces the parameters. - /// - /// The map. - /// The exp. - /// Expression - public static Expression ReplaceParameters(Dictionary map, Expression exp) - { - return new ParameterRebinder(map).Visit(exp); - } - - /// - /// Visits the parameter. - /// - /// The p. - /// Expression - protected override Expression VisitParameter(ParameterExpression p) - { - ParameterExpression replacement; - - if (map.TryGetValue(p, out replacement)) - { - p = replacement; - } - - return base.VisitParameter(p); - } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace Senparc.Ncf.Utility +{ + /// + ///Support related table query + /// + /// Type such as: + /// var seh = new SenparcExpressionHelper(); + /// seh.ValueCompare + /// .AndAlso(true, i => !i.IsDeleted && i.MPAccountId == MPAccount.Id) + /// .AndAlso(!string.IsNullOrEmpty(SearchText), i => i.MPKeywordList.Count(c => c.Content.Contains(SearchText)) > 0); + /// var where = seh.BuildWhereExpression(); + /// + /// Among them, the Count statistics of the related table, under SenparcExpressionHelper, the variable c will be treated as the main table attribute and an error will be reported. + /// + /// + public class SenparcExpressionHelper where TEntity : class + { + public ValueCompare ValueCompare { get; set; } + public SenparcExpressionHelper() + { + ValueCompare = new ValueCompare(); + } + + /// + /// Generate where expression + /// + /// + public Expression> BuildWhereExpression() + { + if (ValueCompare.ExpressionBody == null) + { + //No query required + Expression> returnTrue = z => true; + return returnTrue; + } + var where = ValueCompare.ExpressionBody; + return where; + } + } + public class ValueCompare + { + public Expression> ExpressionBody { get; set; } + public ValueCompare() + { + } + /// + /// Combines the first predicate with the second using the logical "and". + /// + public ValueCompare AndAlso(bool combineWhenTrue, Expression> filter) + { + return Combine(combineWhenTrue, filter, Expression.AndAlso); + } + /// + /// Combines the first predicate with the second using the logical "or". + /// + public ValueCompare OrElse(bool combineWhenTrue, Expression> filter) + { + return Combine(combineWhenTrue, filter, Expression.OrElse); + } + + /// + /// Negates the predicate. + /// + public ValueCompare Not() + { + var negated = Expression.Not(ExpressionBody.Body); + ExpressionBody = Expression.Lambda>(negated, ExpressionBody.Parameters); + return this; + } + public ValueCompare Combine(Expression> filter, Func merge) + { + if (ExpressionBody == null) + { + ExpressionBody = PredicateBuilder.Create(filter); + return this; + } + ExpressionBody = ExpressionBody.Compose(filter, merge); + + return this; + } + public ValueCompare Combine(bool combineWhenTrue, Expression> filter, Func merge) + { + if (!combineWhenTrue) + { + return this; + } + return Combine(filter, merge); + } + } + public static class PredicateBuilder + { + /// + /// Creates a predicate expression from the specified lambda expression. + /// + public static Expression> Create(Expression> predicate) { return predicate; } + + /// + /// Combines the first expression with the second using the specified merge function. + /// + public static Expression Compose(this Expression first, Expression second, Func merge) + { + // zip parameters (map from parameters of second to parameters of first) + var map = first.Parameters + .Select((f, i) => new { f, s = second.Parameters[i] }) + .ToDictionary(p => p.s, p => p.f); + + // replace parameters in the second lambda expression with the parameters in the first + var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); + + // create a merged lambda expression with parameters from the first expression + return Expression.Lambda(merge(first.Body, secondBody), first.Parameters); + } + + /// + /// ParameterRebinder + /// + class ParameterRebinder : ExpressionVisitor + { + /// + /// The ParameterExpression map + /// + readonly Dictionary map; + + /// + /// Initializes a new instance of the class. + /// + /// The map. + ParameterRebinder(Dictionary map) + { + this.map = map ?? new Dictionary(); + } + + /// + /// Replaces the parameters. + /// + /// The map. + /// The exp. + /// Expression + public static Expression ReplaceParameters(Dictionary map, Expression exp) + { + return new ParameterRebinder(map).Visit(exp); + } + + /// + /// Visits the parameter. + /// + /// The p. + /// Expression + protected override Expression VisitParameter(ParameterExpression p) + { + ParameterExpression replacement; + + if (map.TryGetValue(p, out replacement)) + { + p = replacement; + } + + return base.VisitParameter(p); + } + } + } +} diff --git a/src/Basic/Senparc.Ncf.Utility/ExpressionExtension/SenparcExpressionModifier.cs b/src/Basic/Senparc.Ncf.Utility/ExpressionExtension/SenparcExpressionModifier.cs index 111ae62f3..dfb76f46a 100644 --- a/src/Basic/Senparc.Ncf.Utility/ExpressionExtension/SenparcExpressionModifier.cs +++ b/src/Basic/Senparc.Ncf.Utility/ExpressionExtension/SenparcExpressionModifier.cs @@ -1,58 +1,58 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq.Expressions; - -namespace Senparc.Ncf.Utility -{ - public class SenparcExpressionModifier : ExpressionVisitor - { - public BinaryExpression BE { get; set; } - - public SenparcExpressionModifier() - { } - - public ReadOnlyCollection AndAlso(IList expressions) - { - ReadOnlyCollection col = new ReadOnlyCollection(expressions); - var result = Visit(col); - return result; - } - - public Expression> BuildWhereExpression() - { - ParameterExpression pe = Expression.Parameter(typeof(string), "z"); - - Expression> where = Expression.Lambda>(BE, pe); - return where; - } - - protected override Expression VisitBinary(BinaryExpression b) - { - //if (b.NodeType == ExpressionType.AndAlso) - //{ - // Expression left = this.Visit(b.Left); - // Expression right = this.Visit(b.Right); - - // // Make this binary expression an OrElse operation instead of an AndAlso operation. - // return Expression.MakeBinary(ExpressionType.OrElse, left, right, b.IsLiftedToNull, b.Method); - //} - - if (b.IsLifted) - { - return base.VisitBinary(b); - } - if (BE == null) - { - BE = b; - } - else - { - //默认为添加And - BE = Expression.MakeBinary(ExpressionType.AndAlso, BE, b); - } - - return base.VisitBinary(b); - } - } +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq.Expressions; + +namespace Senparc.Ncf.Utility +{ + public class SenparcExpressionModifier : ExpressionVisitor + { + public BinaryExpression BE { get; set; } + + public SenparcExpressionModifier() + { } + + public ReadOnlyCollection AndAlso(IList expressions) + { + ReadOnlyCollection col = new ReadOnlyCollection(expressions); + var result = Visit(col); + return result; + } + + public Expression> BuildWhereExpression() + { + ParameterExpression pe = Expression.Parameter(typeof(string), "z"); + + Expression> where = Expression.Lambda>(BE, pe); + return where; + } + + protected override Expression VisitBinary(BinaryExpression b) + { + //if (b.NodeType == ExpressionType.AndAlso) + //{ + // Expression left = this.Visit(b.Left); + // Expression right = this.Visit(b.Right); + + // // Make this binary expression an OrElse operation instead of an AndAlso operation. + // return Expression.MakeBinary(ExpressionType.OrElse, left, right, b.IsLiftedToNull, b.Method); + //} + + if (b.IsLifted) + { + return base.VisitBinary(b); + } + if (BE == null) + { + BE = b; + } + else + { + //The default is to add And + BE = Expression.MakeBinary(ExpressionType.AndAlso, BE, b); + } + + return base.VisitBinary(b); + } + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Utility/ExpressionExtension/SenparcIQueryableExtension.cs b/src/Basic/Senparc.Ncf.Utility/ExpressionExtension/SenparcIQueryableExtension.cs index 98fcbd832..30c882e8a 100644 --- a/src/Basic/Senparc.Ncf.Utility/ExpressionExtension/SenparcIQueryableExtension.cs +++ b/src/Basic/Senparc.Ncf.Utility/ExpressionExtension/SenparcIQueryableExtension.cs @@ -1,87 +1,87 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Text; - -namespace Senparc.Ncf.Utility.ExpressionExtension -{ - /// - /// linq拓展方法 - /// - public static class SenparcIQueryableExtension - { - const string OrderByDescendingFuncName = "OrderByDescending"; - - const string OrderByFuncName = "OrderBy"; - - const string ThenByFuncName = "ThenBy"; - - const string ThenByDescendingFuncName = "ThenByDescending"; - - const string Asc = "asc"; - - const string Desc = "desc"; - - /// - /// 动态字段排序方法 - /// - /// - /// - /// xxxx desc, aaa asc, ddd desc - /// - public static IQueryable OrderByExtension(this IQueryable iQueryable, string orderBy) - { - if (string.IsNullOrEmpty(orderBy)) - { - return iQueryable; - } - - Type type = typeof(T); - MethodCallExpression OrderExpression = null; - string[] orderField = orderBy.Trim().Split(","); - foreach (var item in orderField) - { - string[] singleField = item.Trim().Split(" "); - string fieldName = singleField[0]; - string orderby = OrderByFuncName; - - ParameterExpression param = Expression.Parameter(type, "_"); - PropertyInfo property = type.GetProperty(fieldName); - if (property == null) - { - continue; - } - - if (singleField.Length == 2) - { - orderby = singleField[1].ToLower(); - } - Expression propertyAccessExpression = Expression.Property(param, property); - LambdaExpression le = Expression.Lambda(propertyAccessExpression, param); - - if (OrderExpression == null) - { - string funcName = OrderByFuncName; - if (!string.IsNullOrEmpty(orderby) && orderby == Desc) - { - funcName = OrderByDescendingFuncName; - } - OrderExpression = Expression.Call(typeof(Queryable), funcName, new Type[] { type, property.PropertyType }, iQueryable.Expression, le); - } - else - { - string funcName = ThenByFuncName; - if (!string.IsNullOrEmpty(orderby) && orderby == Desc) - { - funcName = ThenByDescendingFuncName; - } - OrderExpression = Expression.Call(typeof(Queryable), funcName, new Type[] { type, property.PropertyType }, OrderExpression, le); - } - - } - return iQueryable.Provider.CreateQuery(OrderExpression); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; + +namespace Senparc.Ncf.Utility.ExpressionExtension +{ + /// + ///linq expansion method + /// + public static class SenparcIQueryableExtension + { + const string OrderByDescendingFuncName = "OrderByDescending"; + + const string OrderByFuncName = "OrderBy"; + + const string ThenByFuncName = "ThenBy"; + + const string ThenByDescendingFuncName = "ThenByDescending"; + + const string Asc = "asc"; + + const string Desc = "desc"; + + /// + ///Dynamic field sorting method + /// + /// + /// + /// xxxx desc, aaa asc, ddd desc + /// + public static IQueryable OrderByExtension(this IQueryable iQueryable, string orderBy) + { + if (string.IsNullOrEmpty(orderBy)) + { + return iQueryable; + } + + Type type = typeof(T); + MethodCallExpression OrderExpression = null; + string[] orderField = orderBy.Trim().Split(","); + foreach (var item in orderField) + { + string[] singleField = item.Trim().Split(" "); + string fieldName = singleField[0]; + string orderby = OrderByFuncName; + + ParameterExpression param = Expression.Parameter(type, "_"); + PropertyInfo property = type.GetProperty(fieldName); + if (property == null) + { + continue; + } + + if (singleField.Length == 2) + { + orderby = singleField[1].ToLower(); + } + Expression propertyAccessExpression = Expression.Property(param, property); + LambdaExpression le = Expression.Lambda(propertyAccessExpression, param); + + if (OrderExpression == null) + { + string funcName = OrderByFuncName; + if (!string.IsNullOrEmpty(orderby) && orderby == Desc) + { + funcName = OrderByDescendingFuncName; + } + OrderExpression = Expression.Call(typeof(Queryable), funcName, new Type[] { type, property.PropertyType }, iQueryable.Expression, le); + } + else + { + string funcName = ThenByFuncName; + if (!string.IsNullOrEmpty(orderby) && orderby == Desc) + { + funcName = ThenByDescendingFuncName; + } + OrderExpression = Expression.Call(typeof(Queryable), funcName, new Type[] { type, property.PropertyType }, OrderExpression, le); + } + + } + return iQueryable.Provider.CreateQuery(OrderExpression); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Utility/Extensions/DateTimeExtensions.cs b/src/Basic/Senparc.Ncf.Utility/Extensions/DateTimeExtensions.cs index 75822103b..b95a68948 100644 --- a/src/Basic/Senparc.Ncf.Utility/Extensions/DateTimeExtensions.cs +++ b/src/Basic/Senparc.Ncf.Utility/Extensions/DateTimeExtensions.cs @@ -1,31 +1,31 @@ -//using System; - -//namespace Senparc.Ncf.Core.Extensions -//{ -// public static class DateTimeExtensions -// { -// public static string ToShortTime(this DateTime dt) -// { -// string result = null; -// TimeSpan timeSpan = DateTime.Now - dt; -// if (timeSpan.TotalSeconds < 59) -// { -// result = "刚刚"; -// } -// else if (timeSpan.TotalMinutes < 59) -// { -// result = "{0}分钟前".With((int)timeSpan.TotalMinutes); -// } -// else if (timeSpan.TotalHours < 24) -// { -// result = "{0}小时前".With((int)timeSpan.TotalHours); -// } -// else -// { -// result = "{0}天前".With((int)timeSpan.TotalDays); -// } -// return result; -// } - -// } -//} +//using System; + +//namespace Senparc.Ncf.Core.Extensions +//{ +// public static class DateTimeExtensions +// { +// public static string ToShortTime(this DateTime dt) +// { +// string result = null; +// TimeSpan timeSpan = DateTime.Now - dt; +// if (timeSpan.TotalSeconds < 59) +// { +// result = "just"; +// } +// else if (timeSpan.TotalMinutes < 59) +// { +// result = "{0} minutes ago".With((int)timeSpan.TotalMinutes); +// } +// else if (timeSpan.TotalHours < 24) +// { +// result = "{0} hours ago".With((int)timeSpan.TotalHours); +// } +// else +// { +// result = "{0} days ago".With((int)timeSpan.TotalDays); +// } +// return result; +// } + +// } +//} diff --git a/src/Basic/Senparc.Ncf.Utility/Extensions/StringExtensions.cs b/src/Basic/Senparc.Ncf.Utility/Extensions/StringExtensions.cs index d02cbffbb..a4dd7316e 100644 --- a/src/Basic/Senparc.Ncf.Utility/Extensions/StringExtensions.cs +++ b/src/Basic/Senparc.Ncf.Utility/Extensions/StringExtensions.cs @@ -1,300 +1,300 @@ -using System; -using System.Text; -using System.Text.RegularExpressions; -using System.Web; -using Microsoft.AspNetCore.Html; -using Senparc.CO2NET.Extensions; - -namespace Senparc.Ncf.Core.Extensions -{ - public static class StringExtensions - { - //public static HtmlString RenderMeta(this HtmlHelper helper) - //{ - // if (!(helper.ViewData.Model is IBaseVD)) - // { - // return new HtmlString(""); - // } - - // IBaseVD model = helper.ViewData.Model as IBaseVD; - // MetaCollection metaCollection = model.MetaCollection as MetaCollection; - // string result = null; - // foreach (var item in metaCollection) - // { - // if (!string.IsNullOrEmpty(item.Value)) - // { - // result += string.Format("\r\n", item.Key.ToString(), - // //helper.AttributeEncode(item.Value) //COCONET 此方法已失效 - // item.Value - // ); - // } - // } - // return new HtmlString(result); - //} - - //public static string HtmlDnc(this string InputString) - //{ - // string tString = String.Empty; - // StringBuilder str = null; - // if (!string.IsNullOrEmpty(InputString)) - // { - // tString = InputString; - // str = new StringBuilder(tString); - // str = str.Replace("&", "&"); - // str = str.Replace("<", "<"); - // str = str.Replace(">", ">"); - // str = str.Replace(" ", " "); - // str = str.Replace(" ", " "); - // str = str.Replace(""", "\"\""); - // str = str.Replace("
", "\n"); - // } - // return str.ToString(); - //} - - //public static string HtmlEnc(this string InputString) - //{ - // string tString = String.Empty; - // StringBuilder str = null; - // if (!string.IsNullOrEmpty(InputString)) - // { - // tString = InputString; - // str = new StringBuilder(tString); - // str = str.Replace(">", ">"); - // str = str.Replace("<", "<"); - // str = str.Replace(" ", "  "); - // str = str.Replace(" ", "  "); - // str = str.Replace("\"", """); - // str = str.Replace("\'", "'"); - // str = str.Replace("\n", "
"); - // } - // return str.ToString(); - //} - - - //public static T ShowWhenNullOrEmpty(this T obj, T defaultConent) - //{ - // if (obj == null) - // { - // return defaultConent; - // } - // else if (obj is String && obj.ToString() == "") - // { - // return defaultConent; - // } - // else - // { - // return obj; - // } - //} - - ///// - ///// 把数据转换为Json格式 - ///// - ///// - ///// - //public static string ToJson(this object data) - //{ - // using (MemoryStream ms = new MemoryStream()) - // { - // DataContractJsonSerializer s = new DataContractJsonSerializer(data.GetType()); - // s.WriteObject(ms, data); - // ms.Seek(0, SeekOrigin.Begin); - - // return Encoding.UTF8.GetString(ms.ToArray()); - // } - //} - - ///// - ///// 把数据转换为Json格式(使用Newtonsoft.Json.dll) - ///// - ///// - ///// - //public static string ToJson(this object data) - //{ - // return Newtonsoft.Json.JsonConvert.SerializeObject(data); - //} - - ///// - ///// 格式化成Json字符串 - ///// - ///// 需要格式化的对象 - ///// 指定序列化的深度 - ///// Json字符串 - //public static string ToJson(this object obj, int recursionDepth) - //{ - // //JavaScriptSerializer serializer = new JavaScriptSerializer(); - // //serializer.RecursionLimit = recursionDepth; - // //return serializer.Serialize(obj); - - // return Newtonsoft.Json.JsonConvert.SerializeObject(obj, new Newtonsoft.Json.JsonSerializerSettings() - // { - // //TODO:设置recursionDepth COCONET - // }); - //} - - #region 转换HTML代码 public static string exHTML(string ntext) - ///// - ///// 转换HTML代码——TNT2 - ///// 已实现:回车,空格 - ///// - //public static string exHTML(string ntext) - //{ - // ntext = ntext.ToString().Replace(" ", " ").Replace(Convert.ToString((char)13), "
"); - // return ntext; - //} - - /// - /// 转换HTML代码——TNT2 - /// 已实现:回车,空格 - /// - public static HtmlString ExHTML(this string ntext) - { - if (!string.IsNullOrEmpty(ntext)) - { - ntext = ntext.ToString().Replace(" ", "  ").Replace("\n", "
");//.Replace(Convert.ToString((char)13), "
"); - ntext = Regex.Replace(ntext, @"(?http[s]?://([\w-]+\.)+[\w-]+([/\w-.?=%&;\(\):]*))", "${url}", RegexOptions.IgnoreCase); - } - return new HtmlString(ntext ?? ""); - } - - /// - /// 删除所有HTML标记 - /// - /// - /// - public static string DelHtml(this string str) - { - if (!string.IsNullOrEmpty(str)) - { - str = Regex.Replace(str, "<[^>]*>", "").Replace("\r\n", ""); - } - return str; - } - - //public static string HtmlEncode(this string str) - //{ - // return System.Web.HttpUtility.HtmlEncode(str); - //} - - //public static string HtmlDecode(this string str) - //{ - // return System.Web.HttpUtility.HtmlDecode(str); - //} - - /// - /// 根据布尔值,返回√或× - /// - /// true:√,false:× - /// - public static HtmlString YesOrNo(this bool yesOrNo) - { - #region 根据布尔值,返回√或× - if (yesOrNo) - { - return new HtmlString(""); - } - else - { - return new HtmlString("×"); - } - #endregion - } - - /// - /// 区字符串固定长度,其余的省略 - /// - /// 规则: - /// 1.如果startIndex大于字符串长度,则自动调整到取最后maxLangth长度。如果此时maxLangth长度比字符串长度还要大,那么startIndex回到0 - /// 2.如果在startIndex基础上,取maxLangth长度大于比字符串长度,那么maxLangth自动取到可能的最大值,即从startIndex一直取到字符串末尾 - /// 3.结果中,字符串只要有削减的地方,都以".."替代 - /// - /// 原字符串 - /// 最长字符个数 - /// - public static string SubString(this string str, int startIndex, int maxLangth) - { - if (str == string.Empty || str == null) - { - return ""; - } - else - { - string substring = ""; - - //调整startIndex - if (startIndex > str.Length - 1)//如果startIndex大于字符串长度 - { - startIndex = (str.Length - maxLangth > 0) ? str.Length - maxLangth : 0;//则自动调整到取最后maxLangth长度。如果此时maxLangth长度比字符串长度还要大,那么startIndex回到0 - } - - //调整maxLangth - if (startIndex + maxLangth > str.Length)//如果在startIndex基础上,取maxLangth长度大于比字符串长度 - { - maxLangth = str.Length - startIndex;//那么maxLangth自动取到可能的最大值,即从startIndex一直取到字符串末尾 - } - //调整完成 - - //加缩略符号 - substring += (startIndex > 0) ? ".." : "";//如果开头削减,以".."替代 - - //进行取定长字符串 - substring += str.Substring(startIndex, maxLangth); - - //加缩略符号 - substring += (str.Length - startIndex - maxLangth > 0) ? "..." : "";//如果结尾削减,以".."替代 - - return substring; - } - } - - - /// - /// 高亮关键字(红色) - /// - /// 原始字符串 - /// 关键字 - /// - public static string HighlightKeyword(this string str, string keyword) - { - if (str.IsNullOrEmpty()) - { - return str; - } - - if (!keyword.IsNullOrEmpty()) - { - string replaceFormat = "{0}";//替换格式 - str = Regex.Replace(str, string.Format(@"({0})", keyword), string.Format(replaceFormat, "$1"), RegexOptions.IgnoreCase); - } - return str; - } - - /// - /// 隐藏IP段 - /// - /// - /// 从后计隐藏几个区段 - /// - public static string HideIP(this string ip, int hideNum) - { - string[] ipItems = ip.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries); - for (int i = 0; i < hideNum; i++) - { - ipItems[3 - i] = "*"; - } - return string.Join(".", ipItems); - } - - /// - /// 隐藏IP段(只隐藏最后一个IP段) - /// - /// - /// - public static string HideIP(this string ip) - { - return ip.Substring(0, ip.LastIndexOf(".")) + ".*"; - } - - #endregion - } +using System; +using System.Text; +using System.Text.RegularExpressions; +using System.Web; +using Microsoft.AspNetCore.Html; +using Senparc.CO2NET.Extensions; + +namespace Senparc.Ncf.Core.Extensions +{ + public static class StringExtensions + { + //public static HtmlString RenderMeta(this HtmlHelper helper) + //{ + // if (!(helper.ViewData.Model is IBaseVD)) + // { + // return new HtmlString(""); + // } + + // IBaseVD model = helper.ViewData.Model as IBaseVD; + // MetaCollection metaCollection = model.MetaCollection as MetaCollection; + // string result = null; + // foreach (var item in metaCollection) + // { + // if (!string.IsNullOrEmpty(item.Value)) + // { + // result += string.Format("\r\n", item.Key.ToString(), + // //helper.AttributeEncode(item.Value) //COCONET This method is invalid + // item.Value + // ); + // } + // } + // return new HtmlString(result); + //} + + //public static string HtmlDnc(this string InputString) + //{ + // string tString = String.Empty; + // StringBuilder str = null; + // if (!string.IsNullOrEmpty(InputString)) + // { + // tString = InputString; + // str = new StringBuilder(tString); + // str = str.Replace("&", "&"); + // str = str.Replace("<", "<"); + // str = str.Replace(">", ">"); + // str = str.Replace(" ", " "); + // str = str.Replace(" ", " "); + // str = str.Replace(""", "\"\""); + // str = str.Replace("
", "\n"); + // } + // return str.ToString(); + //} + + //public static string HtmlEnc(this string InputString) + //{ + // string tString = String.Empty; + // StringBuilder str = null; + // if (!string.IsNullOrEmpty(InputString)) + // { + // tString = InputString; + // str = new StringBuilder(tString); + // str = str.Replace(">", ">"); + // str = str.Replace("<", "<"); + // str = str.Replace(" ", "  "); + // str = str.Replace(" ", "  "); + // str = str.Replace("\"", """); + // str = str.Replace("\'", "'"); + // str = str.Replace("\n", "
"); + // } + // return str.ToString(); + //} + + + //public static T ShowWhenNullOrEmpty(this T obj, T defaultConent) + //{ + // if (obj == null) + // { + // return defaultConent; + // } + // else if (obj is String && obj.ToString() == "") + // { + // return defaultConent; + // } + // else + // { + // return obj; + // } + //} + + ///// + ///// Convert data to Json format + ///// + ///// + ///// + //public static string ToJson(this object data) + //{ + // using (MemoryStream ms = new MemoryStream()) + // { + // DataContractJsonSerializer s = new DataContractJsonSerializer(data.GetType()); + // s.WriteObject(ms, data); + // ms.Seek(0, SeekOrigin.Begin); + + // return Encoding.UTF8.GetString(ms.ToArray()); + // } + //} + + ///// + ///// Convert data to Json format (using Newtonsoft.Json.dll) + ///// + ///// + ///// + //public static string ToJson(this object data) + //{ + // return Newtonsoft.Json.JsonConvert.SerializeObject(data); + //} + + ///// + ///// Format into Json string + ///// + ///// Object that needs to be formatted + ///// Specifies the serialization depth + ///// Json string + //public static string ToJson(this object obj, int recursionDepth) + //{ + // //JavaScriptSerializer serializer = new JavaScriptSerializer(); + // //serializer.RecursionLimit = recursionDepth; + // //return serializer.Serialize(obj); + + // return Newtonsoft.Json.JsonConvert.SerializeObject(obj, new Newtonsoft.Json.JsonSerializerSettings() + // { + // //TODO: Set recursionDepth COCONET + // }); + //} + + #region 转换HTML代码 public static string exHTML(string ntext) + ///// + /////Convert HTML code——TNT2 + ///// Implemented: carriage return, space + ///// + //public static string exHTML(string ntext) + //{ + // ntext = ntext.ToString().Replace(" ", " ").Replace(Convert.ToString((char)13), "
"); + // return ntext; + //} + + /// + /// Convert HTML code - TNT2 + /// Implemented: carriage return, space + /// + public static HtmlString ExHTML(this string ntext) + { + if (!string.IsNullOrEmpty(ntext)) + { + ntext = ntext.ToString().Replace(" ", "  ").Replace("\n", "
");//.Replace(Convert.ToString((char)13), "
"); + ntext = Regex.Replace(ntext, @"(?http[s]?://([\w-]+\.)+[\w-]+([/\w-.?=%&;\(\):]*))", "${url}", RegexOptions.IgnoreCase); + } + return new HtmlString(ntext ?? ""); + } + + /// + /// Remove all HTML tags + /// + /// + /// + public static string DelHtml(this string str) + { + if (!string.IsNullOrEmpty(str)) + { + str = Regex.Replace(str, "<[^>]*>", "").Replace("\r\n", ""); + } + return str; + } + + //public static string HtmlEncode(this string str) + //{ + // return System.Web.HttpUtility.HtmlEncode(str); + //} + + //public static string HtmlDecode(this string str) + //{ + // return System.Web.HttpUtility.HtmlDecode(str); + //} + + /// + /// Returns √ or × based on Boolean value + /// + /// true:√,false:× + /// + public static HtmlString YesOrNo(this bool yesOrNo) + { + #region 根据布尔值,返回√或× + if (yesOrNo) + { + return new HtmlString(""); + } + else + { + return new HtmlString("×"); + } + #endregion + } + + /// + /// area string is of fixed length, the rest is omitted + /// + /// rule: + /// 1. If startIndex is greater than the string length, it will automatically adjust to the last maxLangth length. If the maxLangth length is greater than the string length at this time, then startIndex returns to 0 + /// 2. If the length of maxLangth is greater than the length of the string based on startIndex, then maxLangth will automatically take the maximum possible value, that is, from startIndex to the end of the string. + /// 3. In the result, wherever there is a cut in the string, it will be replaced with ".." + /// + /// Original string + /// The longest number of characters + /// + public static string SubString(this string str, int startIndex, int maxLangth) + { + if (str == string.Empty || str == null) + { + return ""; + } + else + { + string substring = ""; + + //Adjust startIndex + if (startIndex > str.Length - 1)//If startIndex is greater than the string length + { + startIndex = (str.Length - maxLangth > 0) ? str.Length - maxLangth : 0;//It will automatically adjust to the last maxLangth length. If the maxLangth length is greater than the string length at this time, then startIndex returns to 0 + } + + //Adjust maxLangth + if (startIndex + maxLangth > str.Length)//If based on startIndex, the maxLangth length is greater than the string length + { + maxLangth = str.Length - startIndex;//Then maxLangth automatically takes the maximum possible value, that is, from startIndex to the end of the string + } + //Adjustment completed + + //Add abbreviation + substring += (startIndex > 0) ? ".." : "";//If the beginning is cut, replace it with ".." + + //Get a fixed-length string + substring += str.Substring(startIndex, maxLangth); + + //Add abbreviation + substring += (str.Length - startIndex - maxLangth > 0) ? "..." : "";//If the ending is cut, replace it with ".." + + return substring; + } + } + + + /// + /// Highlight keywords (red) + /// + /// Original string + /// Keyword + /// + public static string HighlightKeyword(this string str, string keyword) + { + if (str.IsNullOrEmpty()) + { + return str; + } + + if (!keyword.IsNullOrEmpty()) + { + string replaceFormat = "{0}";//Replacement format + str = Regex.Replace(str, string.Format(@"({0})", keyword), string.Format(replaceFormat, "$1"), RegexOptions.IgnoreCase); + } + return str; + } + + /// + /// Hide IP segment + /// + /// + /// Hide several sections from the back + /// + public static string HideIP(this string ip, int hideNum) + { + string[] ipItems = ip.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < hideNum; i++) + { + ipItems[3 - i] = "*"; + } + return string.Join(".", ipItems); + } + + /// + /// Hide IP segment (only hide the last IP segment) + /// + /// + /// + public static string HideIP(this string ip) + { + return ip.Substring(0, ip.LastIndexOf(".")) + ".*"; + } + + #endregion + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Utility/FileUtility.cs b/src/Basic/Senparc.Ncf.Utility/FileUtility.cs index b5d20a647..910136208 100644 --- a/src/Basic/Senparc.Ncf.Utility/FileUtility.cs +++ b/src/Basic/Senparc.Ncf.Utility/FileUtility.cs @@ -1,16 +1,16 @@ -using System; - -namespace Senparc.Ncf.Utility -{ - public class FileUtility - { - /// - /// 获取随机文件名 - /// - /// - public static string GetRandomFileName() - { - return $"{DateTime.Now.Ticks}{Guid.NewGuid().ToString("n").Substring(0, 8)}"; - } - } +using System; + +namespace Senparc.Ncf.Utility +{ + public class FileUtility + { + /// + /// Get a random filename + /// + /// + public static string GetRandomFileName() + { + return $"{DateTime.Now.Ticks}{Guid.NewGuid().ToString("n").Substring(0, 8)}"; + } + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Utility/Helpers/GlobalCulture.cs b/src/Basic/Senparc.Ncf.Utility/Helpers/GlobalCulture.cs index 211f9405c..08bd09d21 100644 --- a/src/Basic/Senparc.Ncf.Utility/Helpers/GlobalCulture.cs +++ b/src/Basic/Senparc.Ncf.Utility/Helpers/GlobalCulture.cs @@ -1,137 +1,137 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Ncf.Utility.Helpers -{ - public enum SystemLanguage - { - Chinese = 0, - English = 1, - } - - /// - /// 文化帮助类 - /// - public class GlobalCulture - { - private static SystemLanguage? _currentSystemLanguage = null; - - private SystemLanguage _defaultLanguage; - private Dictionary _languageActionCollection = new Dictionary(); - - /// - /// 当前系统使用的语言 - /// - public static SystemLanguage CurrentLanguage - { - get - { - if (_currentSystemLanguage == null) - { - CultureInfo currentCulture = CultureInfo.CurrentCulture; - CultureInfo currentUICulture = CultureInfo.CurrentUICulture; - if (currentCulture.TwoLetterISOLanguageName.Equals("zh", StringComparison.OrdinalIgnoreCase)) - { - _currentSystemLanguage = SystemLanguage.Chinese; - } - else //if (currentCulture.TwoLetterISOLanguageName.Equals("en", StringComparison.OrdinalIgnoreCase)) - { - _currentSystemLanguage = SystemLanguage.English; - } - } - return _currentSystemLanguage.Value; - } - set { _currentSystemLanguage = value; } - } - - private GlobalCulture(SystemLanguage defaultLanguage = SystemLanguage.English) - { - _defaultLanguage = defaultLanguage; - } - - private bool _invoked = false; - - private void CheckAndRun(SystemLanguage language, Action action) - { - if (_invoked) - { - return; - } - - if (language == CurrentLanguage) - { - action.Invoke(); - _invoked = true; - } - else - { - _languageActionCollection[language] = action; - } - } - - public static GlobalCulture Create(SystemLanguage defaultLanguage = SystemLanguage.English) - { - return new GlobalCulture(defaultLanguage); - } - - public GlobalCulture SetChinese(Action action) - { - CheckAndRun(SystemLanguage.Chinese, action); - return this; - } - - public GlobalCulture SetEnglish(Action action) - { - CheckAndRun(SystemLanguage.English, action); - return this; - } - - /// - /// 如果之前的语言都不匹配,则执行默认语言设置 - /// - /// 如果未设置任何语言,则抛出异常 - /// 如何未设置全所有语言,则抛出异常 - /// - public void InvokeDefault(bool throwIfNothingIsSet = false, bool throwIfNotAllIsSet = false) - { - if (_invoked) - { - return; - } - - if (_languageActionCollection.Count == 0) - { - if (throwIfNothingIsSet) - { - throw new Exception("Please set at least one language!"); - } - else - { - return; - } - } - - if (throwIfNotAllIsSet && _languageActionCollection.Count != Enum.GetNames().Length) - { - throw new Exception("Please set all languages!"); - } - - - if (_languageActionCollection.ContainsKey(_defaultLanguage)) - { - //使用默认语言 - _languageActionCollection[_defaultLanguage].Invoke(); - } - else - { - //默认语言也未指定,取当前设定的第一个 - _languageActionCollection.Values.First().Invoke(); - } - } - } - +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Ncf.Utility.Helpers +{ + public enum SystemLanguage + { + Chinese = 0, + English = 1, + } + + /// + /// Cultural help category + /// + public class GlobalCulture + { + private static SystemLanguage? _currentSystemLanguage = null; + + private SystemLanguage _defaultLanguage; + private Dictionary _languageActionCollection = new Dictionary(); + + /// + /// The language currently used by the system + /// + public static SystemLanguage CurrentLanguage + { + get + { + if (_currentSystemLanguage == null) + { + CultureInfo currentCulture = CultureInfo.CurrentCulture; + CultureInfo currentUICulture = CultureInfo.CurrentUICulture; + if (currentCulture.TwoLetterISOLanguageName.Equals("zh", StringComparison.OrdinalIgnoreCase)) + { + _currentSystemLanguage = SystemLanguage.Chinese; + } + else //if (currentCulture.TwoLetterISOLanguageName.Equals("en", StringComparison.OrdinalIgnoreCase)) + { + _currentSystemLanguage = SystemLanguage.English; + } + } + return _currentSystemLanguage.Value; + } + set { _currentSystemLanguage = value; } + } + + private GlobalCulture(SystemLanguage defaultLanguage = SystemLanguage.English) + { + _defaultLanguage = defaultLanguage; + } + + private bool _invoked = false; + + private void CheckAndRun(SystemLanguage language, Action action) + { + if (_invoked) + { + return; + } + + if (language == CurrentLanguage) + { + action.Invoke(); + _invoked = true; + } + else + { + _languageActionCollection[language] = action; + } + } + + public static GlobalCulture Create(SystemLanguage defaultLanguage = SystemLanguage.English) + { + return new GlobalCulture(defaultLanguage); + } + + public GlobalCulture SetChinese(Action action) + { + CheckAndRun(SystemLanguage.Chinese, action); + return this; + } + + public GlobalCulture SetEnglish(Action action) + { + CheckAndRun(SystemLanguage.English, action); + return this; + } + + /// + /// If none of the previous languages ​​match, perform the default language setting + /// + /// Throws an exception if no language is set + /// How to throw an exception if all languages ​​are not set + /// + public void InvokeDefault(bool throwIfNothingIsSet = false, bool throwIfNotAllIsSet = false) + { + if (_invoked) + { + return; + } + + if (_languageActionCollection.Count == 0) + { + if (throwIfNothingIsSet) + { + throw new Exception("Please set at least one language!"); + } + else + { + return; + } + } + + if (throwIfNotAllIsSet && _languageActionCollection.Count != Enum.GetNames().Length) + { + throw new Exception("Please set all languages!"); + } + + + if (_languageActionCollection.ContainsKey(_defaultLanguage)) + { + //Use default language + _languageActionCollection[_defaultLanguage].Invoke(); + } + else + { + //The default language is also not specified, and the first one currently set is taken. + _languageActionCollection.Values.First().Invoke(); + } + } + } + } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Utility/Helpers/ReflectionHelper.cs b/src/Basic/Senparc.Ncf.Utility/Helpers/ReflectionHelper.cs index 57f54fa2e..621620101 100644 --- a/src/Basic/Senparc.Ncf.Utility/Helpers/ReflectionHelper.cs +++ b/src/Basic/Senparc.Ncf.Utility/Helpers/ReflectionHelper.cs @@ -1,76 +1,76 @@ -using System; -using System.Reflection; - -namespace Senparc.Ncf.Utility.Helpers -{ - /// - /// 反射帮助类 - /// - public static class ReflectionHelper - { - /// - /// 创建对象实例 - /// - /// 要创建对象的类型 - /// 类型所在程序集名称 - /// 类型所在命名空间 - /// 类型名 - /// - public static T CreateInstance(string assemblyName, string nameSpace, string className) - { - try - { - var ect = CreateInstance(assemblyName, nameSpace, className); - return (T)ect;//强制转换类型 - } - catch - { - //发生异常,返回类型的默认值 - return default(T); - } - } - - /// - /// 创建对象实例 - /// - /// 类型所在程序集名称 - /// 类型所在命名空间 - /// 类型名 - /// - public static object CreateInstance(string assemblyName, string nameSpace, string className) - { - try - { - string fullName = nameSpace + "." + className;//命名空间.类型名 - //此为第一种写法 - object ect = Assembly.Load(assemblyName).CreateInstance(fullName);//加载程序集,创建程序集里面的 命名空间.类型名 实例 - return ect;//返回 - //下面是第二种写法 - //string path = fullName + "," + assemblyName;//命名空间.类型名,程序集 - //Type o = Type.GetType(path);//加载类型 - //object obj = Activator.CreateInstance(o, true);//根据类型创建实例 - //return (T)obj;//类型转换并返回 - } - catch - { - //发生异常,返回null - return null; - } - } - - /// - /// 根据程序集、命名空间、类名得到类型 - /// - /// - /// - /// - /// - public static Type GetTypeFromName(string assemblyName, string nameSpace, string className) - { - string fullName = nameSpace + "." + className;//命名空间.类型名 - string path = fullName + "," + assemblyName;//命名空间.类型名,程序集 - Type o = Type.GetType(path);//加载类型 - return o; - } - } +using System; +using System.Reflection; + +namespace Senparc.Ncf.Utility.Helpers +{ + /// + ///Reflection helper class + /// + public static class ReflectionHelper + { + /// + ///Create object instance + /// + /// The type of object to be created + /// The name of the assembly where the type is located + /// The namespace where the type is located + /// Type name + /// + public static T CreateInstance(string assemblyName, string nameSpace, string className) + { + try + { + var ect = CreateInstance(assemblyName, nameSpace, className); + return (T)ect;//cast type + } + catch + { + //An exception occurs, returning the default value of the type + return default(T); + } + } + + /// + ///Create object instance + /// + /// The name of the assembly where the type is located + /// The namespace where the type is located + /// Type name + /// + public static object CreateInstance(string assemblyName, string nameSpace, string className) + { + try + { + string fullName = nameSpace + "." + className;//namespace.typename + //This is the first way of writing + object ect = Assembly.Load(assemblyName).CreateInstance(fullName);//Load the assembly and create the namespace.typename instance in the assembly + return ect;//return + //The following is the second way of writing + //string path = fullName + "," + assemblyName;//namespace.typename,assembly + //Type o = Type.GetType(path); //Load type + //object obj = Activator.CreateInstance(o, true);//Create an instance based on the type + //return (T)obj;//Type conversion and return + } + catch + { + //An exception occurs and null is returned. + return null; + } + } + + /// + /// Get the type based on assembly, namespace, and class name + /// + /// + /// + /// + /// + public static Type GetTypeFromName(string assemblyName, string nameSpace, string className) + { + string fullName = nameSpace + "." + className;//namespace.typename + string path = fullName + "," + assemblyName;//Namespace.Type name,assembly + Type o = Type.GetType(path);//Load type + return o; + } + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Utility/IDCardValid.cs b/src/Basic/Senparc.Ncf.Utility/IDCardValid.cs index 0555f45b2..ccc261570 100644 --- a/src/Basic/Senparc.Ncf.Utility/IDCardValid.cs +++ b/src/Basic/Senparc.Ncf.Utility/IDCardValid.cs @@ -1,109 +1,109 @@ -using System; - -namespace Senparc.Ncf.Utility -{ - /// - /// Summary description for IDCardValid - /// - public class IDCardValid - { - public IDCardValid() - { - // - // TODO: Add constructor logic here - // - } - - /// - /// 验证身份证号码 - /// - /// 身份证号码 - /// 验证成功为True,否则为False - public static bool CheckIDCard(string Id) - { - if (Id.Length == 18) - { - bool check = CheckIDCard18(Id); - return check; - } - else if (Id.Length == 15) - { - bool check = CheckIDCard15(Id); - return check; - } - else - { - return false; - } - } - - #region 身份证号码验证 - - /// - /// 验证15位身份证号 - /// - /// 身份证号 - /// 验证成功为True,否则为False - private static bool CheckIDCard18(string Id) - { - long n = 0; - if (long.TryParse(Id.Remove(17), out n) == false || n < Math.Pow(10, 16) || long.TryParse(Id.Replace('x', '0').Replace('X', '0'), out n) == false) - { - return false;//数字验证 - } - string address = "11x22x35x44x53x12x23x36x45x54x13x31x37x46x61x14x32x41x50x62x15x33x42x51x63x21x34x43x52x64x65x71x81x82x91"; - if (address.IndexOf(Id.Remove(2)) == -1) - { - return false;//省份验证 - } - string birth = Id.Substring(6, 8).Insert(6, "-").Insert(4, "-"); - DateTime time = new DateTime(); - if (DateTime.TryParse(birth, out time) == false) - { - return false;//生日验证 - } - string[] arrVarifyCode = ("1,0,x,9,8,7,6,5,4,3,2").Split(','); - string[] Wi = ("7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2").Split(','); - char[] Ai = Id.Remove(17).ToCharArray(); - int sum = 0; - for (int i = 0; i < 17; i++) - { - sum += int.Parse(Wi[i]) * int.Parse(Ai[i].ToString()); - } - int y = -1; - Math.DivRem(sum, 11, out y); - if (arrVarifyCode[y] != Id.Substring(17, 1).ToLower()) - { - return false;//校验码验证 - } - return true;//符合GB11643-1999标准 - } - - /// - /// 验证18位身份证号 - /// - /// 身份证号 - /// 验证成功为True,否则为False - private static bool CheckIDCard15(string Id) - { - long n = 0; - if (long.TryParse(Id, out n) == false || n < Math.Pow(10, 14)) - { - return false;//数字验证 - } - string address = "11x22x35x44x53x12x23x36x45x54x13x31x37x46x61x14x32x41x50x62x15x33x42x51x63x21x34x43x52x64x65x71x81x82x91"; - if (address.IndexOf(Id.Remove(2)) == -1) - { - return false;//省份验证 - } - string birth = Id.Substring(6, 6).Insert(4, "-").Insert(2, "-"); - DateTime time = new DateTime(); - if (DateTime.TryParse(birth, out time) == false) - { - return false;//生日验证 - } - return true;//符合15位身份证标准 - } - #endregion - } +using System; + +namespace Senparc.Ncf.Utility +{ + /// + /// Summary description for IDCardValid + /// + public class IDCardValid + { + public IDCardValid() + { + // + // TODO: Add constructor logic here + // + } + + /// + ///Verify ID number + /// + /// ID card number + /// True if verification is successful, False otherwise + public static bool CheckIDCard(string Id) + { + if (Id.Length == 18) + { + bool check = CheckIDCard18(Id); + return check; + } + else if (Id.Length == 15) + { + bool check = CheckIDCard15(Id); + return check; + } + else + { + return false; + } + } + + #region 身份证号码验证 + + /// + /// Verify 15-digit ID number + /// + /// ID card number + /// True if verification is successful, False otherwise + private static bool CheckIDCard18(string Id) + { + long n = 0; + if (long.TryParse(Id.Remove(17), out n) == false || n < Math.Pow(10, 16) || long.TryParse(Id.Replace('x', '0').Replace('X', '0'), out n) == false) + { + return false;//digital verification + } + string address = "11x22x35x44x53x12x23x36x45x54x13x31x37x46x61x14x32x41x50x62x15x33x42x51x63x21x34x43x52x64x65x71x81x82x91"; + if (address.IndexOf(Id.Remove(2)) == -1) + { + return false;//Province verification + } + string birth = Id.Substring(6, 8).Insert(6, "-").Insert(4, "-"); + DateTime time = new DateTime(); + if (DateTime.TryParse(birth, out time) == false) + { + return false;//Birthday verification + } + string[] arrVarifyCode = ("1,0,x,9,8,7,6,5,4,3,2").Split(','); + string[] Wi = ("7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2").Split(','); + char[] Ai = Id.Remove(17).ToCharArray(); + int sum = 0; + for (int i = 0; i < 17; i++) + { + sum += int.Parse(Wi[i]) * int.Parse(Ai[i].ToString()); + } + int y = -1; + Math.DivRem(sum, 11, out y); + if (arrVarifyCode[y] != Id.Substring(17, 1).ToLower()) + { + return false;//Check code verification + } + return true;//Comply with GB11643-1999 standard + } + + /// + /// Verify 18-digit ID number + /// + /// ID card number + /// True if verification is successful, False otherwise + private static bool CheckIDCard15(string Id) + { + long n = 0; + if (long.TryParse(Id, out n) == false || n < Math.Pow(10, 14)) + { + return false;//digital verification + } + string address = "11x22x35x44x53x12x23x36x45x54x13x31x37x46x61x14x32x41x50x62x15x33x42x51x63x21x34x43x52x64x65x71x81x82x91"; + if (address.IndexOf(Id.Remove(2)) == -1) + { + return false;//Province verification + } + string birth = Id.Substring(6, 6).Insert(4, "-").Insert(2, "-"); + DateTime time = new DateTime(); + if (DateTime.TryParse(birth, out time) == false) + { + return false;//Birthday verification + } + return true;//Meet the 15-digit ID card standard + } + #endregion + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Utility/IPData.cs b/src/Basic/Senparc.Ncf.Utility/IPData.cs index 6e05e0fa4..047b96b14 100644 --- a/src/Basic/Senparc.Ncf.Utility/IPData.cs +++ b/src/Basic/Senparc.Ncf.Utility/IPData.cs @@ -1,558 +1,558 @@ -using System; -using System.Text; -using System.IO; -using System.Text.RegularExpressions; -using Senparc.Ncf.Core.Utility; -using Microsoft.AspNetCore.Http; - -#pragma warning disable CS0675 // 对进行了带符号扩展的操作数使用了按位或运算符 - -namespace Senparc.Ncf.Utility -{ - /// - /// QQWry 的摘要说明。 - /// - public class QQWry : IDisposable - { - //第一种模式 - #region 第一种模式 - /**/ - /// - /// 第一种模式 - /// - #endregion - private const byte REDIRECT_MODE_1 = 0x01; - - //第二种模式 - #region 第二种模式 - /**/ - /// - /// 第二种模式 - /// - #endregion - private const byte REDIRECT_MODE_2 = 0x02; - - //每条记录长度 - #region 每条记录长度 - /**/ - /// - /// 每条记录长度 - /// - #endregion - private const int IP_RECORD_LENGTH = 7; - - //数据库文件 - #region 数据库文件 - /**/ - /// - /// 文件对象 - /// - #endregion - private FileStream ipFile; - - private const string unCountry = "未知国家"; - private const string unArea = "未知地区"; - - //索引开始位置 - #region 索引开始位置 - /**/ - /// - /// 索引开始位置 - /// - #endregion - private long ipBegin; - - //索引结束位置 - #region 索引结束位置 - /**/ - /// - /// 索引结束位置 - /// - #endregion - private long ipEnd; - - //IP地址对象 - #region IP地址对象 - /**/ - /// - /// IP对象 - /// - #endregion - private IPLocation loc; - - //存储文本内容 - #region 存储文本内容 - /**/ - /// - /// 存储文本内容 - /// - #endregion - private byte[] buf; - - //存储3字节 - #region 存储3字节 - /**/ - /// - /// 存储3字节 - /// - #endregion - private byte[] b3; - - //存储4字节 - #region 存储4字节 - /**/ - /// - /// 存储4字节IP地址 - /// - #endregion - private byte[] b4; - - //#region 单件模式——TNT2 - - //private static GLJK.Common.QQWry qq = new QQWry(); - - ///// - ///// 用于锁定单进程访问 - ///// - //private static readonly object objLock = new object(); - - //public static QQWry GetInstance() - //{ - // if (qq == null) - // { - // lock (objLock) - // { - // if (qq == null) - // { - // qq = new QQWry(); - // } - // } - // } - // else - // { - // qq.Dispose(); - // } - // return qq; - //} - //#endregion - - - //构造函数 - #region 构造函数 - /**/ - /// - /// 构造函数 - /// - /// IP数据库文件绝对路径 - #endregion - public QQWry(string ipfile) - { - ipfile = ipfile ?? Server.GetMapPath("~/App_Data/QQWry.Dat");//数据库地址 - buf = new byte[100]; - b3 = new byte[3]; - b4 = new byte[4]; - try - { - ipFile = new FileStream(ipfile, FileMode.Open, FileAccess.Read, FileShare.Read);//FileAccess.Read,,FileShare.Read为后添加 - } - catch (Exception ex) - { - throw new Exception(ex.Message); - } - ipBegin = readLong4(0); - ipEnd = readLong4(4); - loc = new IPLocation(); - } - - public QQWry() - : this(null) - { } - - /// - /// 根据客户端IP地址搜索 - /// - /// - public IPLocation SearchIPLocation() - { - return SearchIPLocation(GetCurrentIP()); - } - - public string GetCurrentIP() - { - //return HttpContext.Current.Request.UserHostAddress; - //COCONET 另外可参考:https://blog.csdn.net/yzj_xiaoyue/article/details/79200714 - return SenparcHttpContext.Current.Connection.RemoteIpAddress.ToString(); - } - - //根据IP地址搜索 - #region 根据IP地址搜索 - /**/ - /// - /// 搜索IP地址搜索 - /// - /// - /// - #endregion - public IPLocation SearchIPLocation(string ip) - { - ip = ip ?? ""; - //验证IP合法性 - string pattrn = @"(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])"; - if (!Regex.IsMatch(ip, pattrn)) - { - return new IPLocation() { country = "未知", area = "" }; - } - - //将字符IP转换为字节 - string[] ipSp = ip.Split('.'); - if (ipSp.Length != 4) - { - throw new ArgumentOutOfRangeException("不是合法的IP地址!"); - } - byte[] IP = new byte[4]; - for (int i = 0; i < IP.Length; i++) - { - IP[i] = (byte)(Int32.Parse(ipSp[i]) & 0xFF); - } - - IPLocation local = null; - long offset = locateIP(IP); - - if (offset != -1) - { - local = getIPLocation(offset); - } - - if (local == null) - { - local = new IPLocation(); - local.area = unArea; - local.country = unCountry; - } - return local; - } - - //取得具体信息 - #region 取得具体信息 - /**/ - /// - /// 取得具体信息 - /// - /// - /// - #endregion - private IPLocation getIPLocation(long offset) - { - ipFile.Position = offset + 4; - //读取第一个字节判断是否是标志字节 - byte one = (byte)ipFile.ReadByte(); - if (one == REDIRECT_MODE_1) - { - //第一种模式 - //读取国家偏移 - long countryOffset = readLong3(); - //转至偏移处 - ipFile.Position = countryOffset; - //再次检查标志字节 - byte b = (byte)ipFile.ReadByte(); - if (b == REDIRECT_MODE_2) - { - loc.country = readString(readLong3()); - ipFile.Position = countryOffset + 4; - } - else - loc.country = readString(countryOffset); - - //读取地区标志 - loc.area = readArea(ipFile.Position); - - } - else if (one == REDIRECT_MODE_2) - { - //第二种模式 - loc.country = readString(readLong3()); - loc.area = readArea(offset + 8); - } - else - { - //普通模式 - loc.country = readString(--ipFile.Position); - loc.area = readString(ipFile.Position); - } - - loc.country = loc.country.Replace("CZ88.NET", "");//替换 By TNT2 - loc.area = loc.area.Replace("CZ88.NET", "");//替换 By TNT2 - - return loc; - } - - //取得地区信息 - #region 取得地区信息 - /**/ - /// - /// 读取地区名称 - /// - /// - /// - #endregion - private string readArea(long offset) - { - ipFile.Position = offset; - byte one = (byte)ipFile.ReadByte(); - if (one == REDIRECT_MODE_1 || one == REDIRECT_MODE_2) - { - long areaOffset = readLong3(offset + 1); - if (areaOffset == 0) - return unArea; - else - { - return readString(areaOffset); - } - } - else - { - return readString(offset); - } - } - - //读取字符串 - #region 读取字符串 - /**/ - /// - /// 读取字符串 - /// - /// - /// - #endregion - private string readString(long offset) - { - ipFile.Position = offset; - int i = 0; - for (i = 0, buf[i] = (byte)ipFile.ReadByte(); buf[i] != (byte)(0); buf[++i] = (byte)ipFile.ReadByte()) ; - - if (i > 0) - return Encoding.Default.GetString(buf, 0, i); - else - return ""; - } - - //查找IP地址所在的绝对偏移量 - #region 查找IP地址所在的绝对偏移量 - /**/ - /// - /// 查找IP地址所在的绝对偏移量 - /// - /// - /// - #endregion - private long locateIP(byte[] ip) - { - long m = 0; - int r; - - //比较第一个IP项 - readIP(ipBegin, b4); - r = compareIP(ip, b4); - if (r == 0) - return ipBegin; - else if (r < 0) - return -1; - //开始二分搜索 - for (long i = ipBegin, j = ipEnd; i < j; ) - { - m = this.getMiddleOffset(i, j); - readIP(m, b4); - r = compareIP(ip, b4); - if (r > 0) - i = m; - else if (r < 0) - { - if (m == j) - { - j -= IP_RECORD_LENGTH; - m = j; - } - else - { - j = m; - } - } - else - return readLong3(m + 4); - } - m = readLong3(m + 4); - readIP(m, b4); - r = compareIP(ip, b4); - if (r <= 0) - return m; - else - return -1; - } - - //读出4字节的IP地址 - #region 读出4字节的IP地址 - /**/ - /// - /// 从当前位置读取四字节,此四字节是IP地址 - /// - /// - /// - #endregion - private void readIP(long offset, byte[] ip) - { - ipFile.Position = offset; - ipFile.Read(ip, 0, ip.Length); - byte tmp = ip[0]; - ip[0] = ip[3]; - ip[3] = tmp; - tmp = ip[1]; - ip[1] = ip[2]; - ip[2] = tmp; - } - - //比较IP地址是否相同 - #region 比较IP地址是否相同 - /**/ - /// - /// 比较IP地址是否相同 - /// - /// - /// - /// 0:相等,1:ip大于beginIP,-1:小于 - #endregion - private int compareIP(byte[] ip, byte[] beginIP) - { - for (int i = 0; i < 4; i++) - { - int r = compareByte(ip[i], beginIP[i]); - if (r != 0) - return r; - } - return 0; - } - - //比较两个字节是否相等 - #region 比较两个字节是否相等 - /**/ - /// - /// 比较两个字节是否相等 - /// - /// - /// - /// - #endregion - private int compareByte(byte bsrc, byte bdst) - { - if ((bsrc & 0xFF) > (bdst & 0xFF)) - return 1; - else if ((bsrc ^ bdst) == 0) - return 0; - else - return -1; - } - - //根据当前位置读取4字节 - #region 根据当前位置读取4字节 - /**/ - /// - /// 从当前位置读取4字节,转换为长整型 - /// - /// - /// - #endregion - private long readLong4(long offset) - { - long ret = 0; - ipFile.Position = offset; - ret |= (ipFile.ReadByte() & 0xFF); - ret |= ((ipFile.ReadByte() << 8) & 0xFF00); - ret |= ((ipFile.ReadByte() << 16) & 0xFF0000); - ret |= ((ipFile.ReadByte() << 24) & 0xFF000000); - return ret; - } - - //根据当前位置,读取3字节 - #region 根据当前位置,读取3字节 - /**/ - /// - /// 根据当前位置,读取3字节 - /// - /// - /// - #endregion - private long readLong3(long offset) - { - long ret = 0; - ipFile.Position = offset; - ret |= (ipFile.ReadByte() & 0xFF); - ret |= ((ipFile.ReadByte() << 8) & 0xFF00); - ret |= ((ipFile.ReadByte() << 16) & 0xFF0000); - return ret; - } - - //从当前位置读取3字节 - #region 从当前位置读取3字节 - /**/ - /// - /// 从当前位置读取3字节 - /// - /// - #endregion - private long readLong3() - { - long ret = 0; - ret |= (ipFile.ReadByte() & 0xFF); - ret |= ((ipFile.ReadByte() << 8) & 0xFF00); - ret |= ((ipFile.ReadByte() << 16) & 0xFF0000); - return ret; - } - - //取得begin和end之间的偏移量 - #region 取得begin和end之间的偏移量 - /**/ - /// - /// 取得begin和end中间的偏移 - /// - /// - /// - /// - #endregion - private long getMiddleOffset(long begin, long end) - { - long records = (end - begin) / IP_RECORD_LENGTH; - records >>= 1; - if (records == 0) - records = 1; - return begin + records * IP_RECORD_LENGTH; - } - - #region IDisposable 成员 - - public void Dispose() - { - ipFile.Close(); - } - - #endregion - } - - public class IPLocation - { - public String country; - public String area; - - public IPLocation() - { - country = area = ""; - } - - public IPLocation getCopy() - { - IPLocation ret = new IPLocation(); - ret.country = country; - ret.area = area; - return ret; - } - } -} -#pragma warning restore CS0675 // 对进行了带符号扩展的操作数使用了按位或运算符 +using System; +using System.Text; +using System.IO; +using System.Text.RegularExpressions; +using Senparc.Ncf.Core.Utility; +using Microsoft.AspNetCore.Http; + +#pragma warning disable CS0675 // Bitwise OR operator used on sign-extended operands + +namespace Senparc.Ncf.Utility +{ + /// + /// Summary description of QQWry. + /// + public class QQWry : IDisposable + { + //first mode + #region 第一种模式 + /**/ + /// + /// first mode + /// + #endregion + private const byte REDIRECT_MODE_1 = 0x01; + + //Second mode + #region 第二种模式 + /**/ + /// + /// Second mode + /// + #endregion + private const byte REDIRECT_MODE_2 = 0x02; + + //length of each record + #region 每条记录长度 + /**/ + /// + /// Length of each record + /// + #endregion + private const int IP_RECORD_LENGTH = 7; + + //database file + #region 数据库文件 + /**/ + /// + ///file object + /// + #endregion + private FileStream ipFile; + + private const string unCountry = "未知国家"; + private const string unArea = "未知地区"; + + //index start position + #region 索引开始位置 + /**/ + /// + /// Index starting position + /// + #endregion + private long ipBegin; + + //index end position + #region 索引结束位置 + /**/ + /// + /// index end position + /// + #endregion + private long ipEnd; + + //IP address object + #region IP地址对象 + /**/ + /// + ///IP object + /// + #endregion + private IPLocation loc; + + //Store text content + #region 存储文本内容 + /**/ + /// + /// store text content + /// + #endregion + private byte[] buf; + + //Store 3 bytes + #region 存储3字节 + /**/ + /// + /// store 3 bytes + /// + #endregion + private byte[] b3; + + //Store 4 bytes + #region 存储4字节 + /**/ + /// + /// stores 4-byte IP address + /// + #endregion + private byte[] b4; + + //#region Singleton Mode - TNT2 + + //private static GLJK.Common.QQWry qq = new QQWry(); + + ///// + ///// Used to lock single-process access + ///// + //private static readonly object objLock = new object(); + + //public static QQWry GetInstance() + //{ + // if (qq == null) + // { + // lock (objLock) + // { + // if (qq == null) + // { + // qq = new QQWry(); + // } + // } + // } + // else + // { + // qq.Dispose(); + // } + // return qq; + //} + //#endregion + + + //Constructor + #region 构造函数 + /**/ + /// + ///Constructor + /// + /// IP database file absolute path + #endregion + public QQWry(string ipfile) + { + ipfile = ipfile ?? Server.GetMapPath("~/App_Data/QQWry.Dat");//Database address + buf = new byte[100]; + b3 = new byte[3]; + b4 = new byte[4]; + try + { + ipFile = new FileStream(ipfile, FileMode.Open, FileAccess.Read, FileShare.Read);//FileAccess.Read,,FileShare.Read is added after + } + catch (Exception ex) + { + throw new Exception(ex.Message); + } + ipBegin = readLong4(0); + ipEnd = readLong4(4); + loc = new IPLocation(); + } + + public QQWry() + : this(null) + { } + + /// + ///Search based on client IP address + /// + /// + public IPLocation SearchIPLocation() + { + return SearchIPLocation(GetCurrentIP()); + } + + public string GetCurrentIP() + { + //return HttpContext.Current.Request.UserHostAddress; + //COCONET can also refer to: https://blog.csdn.net/yzj_xiaoyue/article/details/79200714 + return SenparcHttpContext.Current.Connection.RemoteIpAddress.ToString(); + } + + //Search by IP address + #region 根据IP地址搜索 + /**/ + /// + ///Search IP address search + /// + /// + /// + #endregion + public IPLocation SearchIPLocation(string ip) + { + ip = ip ?? ""; + //Verify IP legitimacy + string pattrn = @"(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])"; + if (!Regex.IsMatch(ip, pattrn)) + { + return new IPLocation() { country = "未知", area = "" }; + } + + //Convert character IP to bytes + string[] ipSp = ip.Split('.'); + if (ipSp.Length != 4) + { + throw new ArgumentOutOfRangeException("不是合法的IP地址!"); + } + byte[] IP = new byte[4]; + for (int i = 0; i < IP.Length; i++) + { + IP[i] = (byte)(Int32.Parse(ipSp[i]) & 0xFF); + } + + IPLocation local = null; + long offset = locateIP(IP); + + if (offset != -1) + { + local = getIPLocation(offset); + } + + if (local == null) + { + local = new IPLocation(); + local.area = unArea; + local.country = unCountry; + } + return local; + } + + //Get specific information + #region 取得具体信息 + /**/ + /// + ///Get specific information + /// + /// + /// + #endregion + private IPLocation getIPLocation(long offset) + { + ipFile.Position = offset + 4; + //Read the first byte to determine whether it is a flag byte + byte one = (byte)ipFile.ReadByte(); + if (one == REDIRECT_MODE_1) + { + //first mode + //Read country offset + long countryOffset = readLong3(); + //Go to offset + ipFile.Position = countryOffset; + //Check the flag byte again + byte b = (byte)ipFile.ReadByte(); + if (b == REDIRECT_MODE_2) + { + loc.country = readString(readLong3()); + ipFile.Position = countryOffset + 4; + } + else + loc.country = readString(countryOffset); + + //Read area flag + loc.area = readArea(ipFile.Position); + + } + else if (one == REDIRECT_MODE_2) + { + //Second mode + loc.country = readString(readLong3()); + loc.area = readArea(offset + 8); + } + else + { + //Normal mode + loc.country = readString(--ipFile.Position); + loc.area = readString(ipFile.Position); + } + + loc.country = loc.country.Replace("CZ88.NET", "");//Replace By TNT2 + loc.area = loc.area.Replace("CZ88.NET", "");//Replace By TNT2 + + return loc; + } + + //Get area information + #region 取得地区信息 + /**/ + /// + ///Read region name + /// + /// + /// + #endregion + private string readArea(long offset) + { + ipFile.Position = offset; + byte one = (byte)ipFile.ReadByte(); + if (one == REDIRECT_MODE_1 || one == REDIRECT_MODE_2) + { + long areaOffset = readLong3(offset + 1); + if (areaOffset == 0) + return unArea; + else + { + return readString(areaOffset); + } + } + else + { + return readString(offset); + } + } + + //Read string + #region 读取字符串 + /**/ + /// + /// read string + /// + /// + /// + #endregion + private string readString(long offset) + { + ipFile.Position = offset; + int i = 0; + for (i = 0, buf[i] = (byte)ipFile.ReadByte(); buf[i] != (byte)(0); buf[++i] = (byte)ipFile.ReadByte()) ; + + if (i > 0) + return Encoding.Default.GetString(buf, 0, i); + else + return ""; + } + + //Find the absolute offset at which an IP address is located + #region 查找IP地址所在的绝对偏移量 + /**/ + /// + /// Find the absolute offset at which the IP address is located + /// + /// + /// + #endregion + private long locateIP(byte[] ip) + { + long m = 0; + int r; + + //Compare the first IP entry + readIP(ipBegin, b4); + r = compareIP(ip, b4); + if (r == 0) + return ipBegin; + else if (r < 0) + return -1; + //Start binary search + for (long i = ipBegin, j = ipEnd; i < j; ) + { + m = this.getMiddleOffset(i, j); + readIP(m, b4); + r = compareIP(ip, b4); + if (r > 0) + i = m; + else if (r < 0) + { + if (m == j) + { + j -= IP_RECORD_LENGTH; + m = j; + } + else + { + j = m; + } + } + else + return readLong3(m + 4); + } + m = readLong3(m + 4); + readIP(m, b4); + r = compareIP(ip, b4); + if (r <= 0) + return m; + else + return -1; + } + + //Read out the 4-byte IP address + #region 读出4字节的IP地址 + /**/ + /// + /// Read four bytes from the current location, these four bytes are the IP address + /// + /// + /// + #endregion + private void readIP(long offset, byte[] ip) + { + ipFile.Position = offset; + ipFile.Read(ip, 0, ip.Length); + byte tmp = ip[0]; + ip[0] = ip[3]; + ip[3] = tmp; + tmp = ip[1]; + ip[1] = ip[2]; + ip[2] = tmp; + } + + //Compare IP addresses to see if they are the same + #region 比较IP地址是否相同 + /**/ + /// + /// Compare IP addresses to see if they are the same + /// + /// + /// + /// 0: equal, 1: ip is greater than beginIP, -1: less than + #endregion + private int compareIP(byte[] ip, byte[] beginIP) + { + for (int i = 0; i < 4; i++) + { + int r = compareByte(ip[i], beginIP[i]); + if (r != 0) + return r; + } + return 0; + } + + //Compare two bytes for equality + #region 比较两个字节是否相等 + /**/ + /// + /// Compare two bytes to see if they are equal + /// + /// + /// + /// + #endregion + private int compareByte(byte bsrc, byte bdst) + { + if ((bsrc & 0xFF) > (bdst & 0xFF)) + return 1; + else if ((bsrc ^ bdst) == 0) + return 0; + else + return -1; + } + + //Read 4 bytes based on the current position + #region 根据当前位置读取4字节 + /**/ + /// + /// Read 4 bytes from the current position and convert to long integer + /// + /// + /// + #endregion + private long readLong4(long offset) + { + long ret = 0; + ipFile.Position = offset; + ret |= (ipFile.ReadByte() & 0xFF); + ret |= ((ipFile.ReadByte() << 8) & 0xFF00); + ret |= ((ipFile.ReadByte() << 16) & 0xFF0000); + ret |= ((ipFile.ReadByte() << 24) & 0xFF000000); + return ret; + } + + //According to the current position, read 3 bytes + #region 根据当前位置,读取3字节 + /**/ + /// + /// According to the current position, read 3 bytes + /// + /// + /// + #endregion + private long readLong3(long offset) + { + long ret = 0; + ipFile.Position = offset; + ret |= (ipFile.ReadByte() & 0xFF); + ret |= ((ipFile.ReadByte() << 8) & 0xFF00); + ret |= ((ipFile.ReadByte() << 16) & 0xFF0000); + return ret; + } + + //Read 3 bytes from current position + #region 从当前位置读取3字节 + /**/ + /// + /// Read 3 bytes from the current position + /// + /// + #endregion + private long readLong3() + { + long ret = 0; + ret |= (ipFile.ReadByte() & 0xFF); + ret |= ((ipFile.ReadByte() << 8) & 0xFF00); + ret |= ((ipFile.ReadByte() << 16) & 0xFF0000); + return ret; + } + + //Get the offset between begin and end + #region 取得begin和end之间的偏移量 + /**/ + /// + /// Get the offset between begin and end + /// + /// + /// + /// + #endregion + private long getMiddleOffset(long begin, long end) + { + long records = (end - begin) / IP_RECORD_LENGTH; + records >>= 1; + if (records == 0) + records = 1; + return begin + records * IP_RECORD_LENGTH; + } + + #region IDisposable 成员 + + public void Dispose() + { + ipFile.Close(); + } + + #endregion + } + + public class IPLocation + { + public String country; + public String area; + + public IPLocation() + { + country = area = ""; + } + + public IPLocation getCopy() + { + IPLocation ret = new IPLocation(); + ret.country = country; + ret.area = area; + return ret; + } + } +} +#pragma warning restore CS0675 // Bitwise OR operator used on sign-extended operands diff --git a/src/Basic/Senparc.Ncf.Utility/ModelStateDictionaryExtension.cs b/src/Basic/Senparc.Ncf.Utility/ModelStateDictionaryExtension.cs index b863f2b3f..a11235ba3 100644 --- a/src/Basic/Senparc.Ncf.Utility/ModelStateDictionaryExtension.cs +++ b/src/Basic/Senparc.Ncf.Utility/ModelStateDictionaryExtension.cs @@ -1,22 +1,22 @@ -using System.Linq; - -namespace Senparc.Ncf.Utility -{ - //public static class ModelStateDictionaryExtension - //{ - // /// - // /// 获取第一个错误提示 - // /// - // /// - // /// - // public static string FirstErrorMessage (this ModelStateDictionary modelStateDictionary) - // { - // if (modelStateDictionary.IsValid) - // { - // return ""; - // } - // return modelStateDictionary.Values.FirstOrDefault (z => z.Errors.Count > 0).Errors [0].ErrorMessage; - // } - //} - -} +using System.Linq; + +namespace Senparc.Ncf.Utility +{ + //public static class ModelStateDictionaryExtension + //{ + // /// + // /// Get the first error message + // /// + // /// + // /// + // public static string FirstErrorMessage (this ModelStateDictionary modelStateDictionary) + // { + // if (modelStateDictionary.IsValid) + // { + // return ""; + // } + // return modelStateDictionary.Values.FirstOrDefault (z => z.Errors.Count > 0).Errors [0].ErrorMessage; + // } + //} + +} diff --git a/src/Basic/Senparc.Ncf.Utility/RequestExtension.cs b/src/Basic/Senparc.Ncf.Utility/RequestExtension.cs index ce9dde084..3f9cee5c7 100644 --- a/src/Basic/Senparc.Ncf.Utility/RequestExtension.cs +++ b/src/Basic/Senparc.Ncf.Utility/RequestExtension.cs @@ -1,155 +1,155 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using System.Net; - -namespace Senparc.Ncf.Utility -{ - public static class RequestExtension - { - private const string NullIpAddress = "::1"; - - //public static bool IsLocal(this HttpRequest req) - //{ - // var connection = req.HttpContext.Connection; - // if (connection.RemoteIpAddress.IsSet()) - // { - // //We have a remote address set up - // return connection.LocalIpAddress.IsSet() - // //Is local is same as remote, then we are local - // ? connection.RemoteIpAddress.Equals(connection.LocalIpAddress) - // //else we are remote if the remote IP address is not a loopback address - // : IPAddress.IsLoopback(connection.RemoteIpAddress); - // } - - // return true; - //} - - private static bool IsSet(this IPAddress address) - { - return address != null && address.ToString() != NullIpAddress; - } - - - ///// - ///// Determines whether the specified HTTP request is an AJAX request. - ///// - ///// - ///// - ///// true if the specified HTTP request is an AJAX request; otherwise, false. - ///// - ///// The HTTP request.The parameter is null (Nothing in Visual Basic). - //public static bool IsAjaxRequest(this HttpRequest request) - //{ - // if (request == null) - // throw new ArgumentNullException("request"); - - // if (request.Headers != null) - // return request.Headers["X-Requested-With"] == "XMLHttpRequest"; - // return false; - //} - - ///// - ///// 通常是以/开头的完整路径 - ///// - ///// - ///// - //public static string PathAndQuery(this HttpRequest request) - //{ - // if (request == null) - // throw new ArgumentNullException("request"); - - // return request.Path + request.QueryString; - //} - - ///// - ///// 获取来源页面 - ///// - ///// - ///// - //public static string UrlReferrer(this HttpRequest request) - //{ - // return request.Headers["Referer"].ToString(); - //} - - /// - /// 返回绝对地址 - /// - /// - /// - public static string AbsoluteUri(this HttpRequest request) - { - var absoluteUri = string.Concat( - request.Scheme, - "://", - request.Host.ToUriComponent(), - request.PathBase.ToUriComponent(), - request.Path.ToUriComponent(), - request.QueryString.ToUriComponent()); - - return absoluteUri; - } - - /// - /// 获取客户端信息 - /// - /// - /// - public static string UserAgent(this HttpRequest request) - { - return request.Headers["User-Agent"].ToString(); - } - - - /// - /// 获取客户端信息 - /// - /// - /// - public static AgentType UserAgentType(this HttpRequest request) - { - var userAgent = request.Headers["User-Agent"].ToString(); - switch (userAgent) - { - case string android when android.Contains("MicroMessenger"): - return AgentType.Wechat; - case string android when android.Contains("Android"): - return AgentType.Android; - case string android when android.Contains("iPhone"): - return AgentType.IPhone; - case string android when android.Contains("iPad"): - return AgentType.IPad; - case string android when android.Contains("Windows Phone"): - return AgentType.WindowsPhone; - case string android when android.Contains("Windows NT"): - return AgentType.Windows; - case string android when android.Contains("Mac OS"): - return AgentType.MacOS; - } - return AgentType.Android; - } - - public enum AgentType - { - Android = 0, - IPhone = 1, - IPad = 2, - WindowsPhone = 3, - Windows = 4, - Wechat = 6, - MacOS = 7 - - } - - - /// - /// 获取客户端地址(IP) - /// - /// - /// - public static IPAddress UserHostAddress(this HttpContext httpContext) - { - return httpContext.Features.Get()?.RemoteIpAddress; - } - } - -} +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using System.Net; + +namespace Senparc.Ncf.Utility +{ + public static class RequestExtension + { + private const string NullIpAddress = "::1"; + + //public static bool IsLocal(this HttpRequest req) + //{ + // var connection = req.HttpContext.Connection; + // if (connection.RemoteIpAddress.IsSet()) + // { + // //We have a remote address set up + // return connection.LocalIpAddress.IsSet() + // //Is local is same as remote, then we are local + // ? connection.RemoteIpAddress.Equals(connection.LocalIpAddress) + // //else we are remote if the remote IP address is not a loopback address + // : IPAddress.IsLoopback(connection.RemoteIpAddress); + // } + + // return true; + //} + + private static bool IsSet(this IPAddress address) + { + return address != null && address.ToString() != NullIpAddress; + } + + + ///// + ///// Determines whether the specified HTTP request is an AJAX request. + ///// + ///// + ///// + ///// true if the specified HTTP request is an AJAX request; otherwise, false. + ///// + ///// The HTTP request.The parameter is null (Nothing in Visual Basic). + //public static bool IsAjaxRequest(this HttpRequest request) + //{ + // if (request == null) + // throw new ArgumentNullException("request"); + + // if (request.Headers != null) + // return request.Headers["X-Requested-With"] == "XMLHttpRequest"; + // return false; + //} + + ///// + ///// Usually a full path starting with / + ///// + ///// + ///// + //public static string PathAndQuery(this HttpRequest request) + //{ + // if (request == null) + // throw new ArgumentNullException("request"); + + // return request.Path + request.QueryString; + //} + + ///// + ///// Get the source page + ///// + ///// + ///// + //public static string UrlReferrer(this HttpRequest request) + //{ + // return request.Headers["Referer"].ToString(); + //} + + /// + /// Returns the absolute address + /// + /// + /// + public static string AbsoluteUri(this HttpRequest request) + { + var absoluteUri = string.Concat( + request.Scheme, + "://", + request.Host.ToUriComponent(), + request.PathBase.ToUriComponent(), + request.Path.ToUriComponent(), + request.QueryString.ToUriComponent()); + + return absoluteUri; + } + + /// + /// Get client information + /// + /// + /// + public static string UserAgent(this HttpRequest request) + { + return request.Headers["User-Agent"].ToString(); + } + + + /// + /// Get client information + /// + /// + /// + public static AgentType UserAgentType(this HttpRequest request) + { + var userAgent = request.Headers["User-Agent"].ToString(); + switch (userAgent) + { + case string android when android.Contains("MicroMessenger"): + return AgentType.Wechat; + case string android when android.Contains("Android"): + return AgentType.Android; + case string android when android.Contains("iPhone"): + return AgentType.IPhone; + case string android when android.Contains("iPad"): + return AgentType.IPad; + case string android when android.Contains("Windows Phone"): + return AgentType.WindowsPhone; + case string android when android.Contains("Windows NT"): + return AgentType.Windows; + case string android when android.Contains("Mac OS"): + return AgentType.MacOS; + } + return AgentType.Android; + } + + public enum AgentType + { + Android = 0, + IPhone = 1, + IPad = 2, + WindowsPhone = 3, + Windows = 4, + Wechat = 6, + MacOS = 7 + + } + + + /// + /// Get client address (IP) + /// + /// + /// + public static IPAddress UserHostAddress(this HttpContext httpContext) + { + return httpContext.Features.Get()?.RemoteIpAddress; + } + } + +} diff --git a/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/DateTimeUtility.cs b/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/DateTimeUtility.cs index 16f4a5437..35a822501 100644 --- a/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/DateTimeUtility.cs +++ b/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/DateTimeUtility.cs @@ -1,91 +1,91 @@ -using System; - -namespace Senparc.Ncf.Core.Utility -{ - public static class DateTimeUtility - { - public static long GetJavascriptTimestamp(System.DateTime input) - { - return input.AddTicks((-1) * DateTime.Parse("1970-1-1").Ticks).Ticks / 10000; - //System.TimeSpan span = new System.TimeSpan(System.DateTime.Parse("1970-1-1").Ticks); - //System.DateTime time = input.Subtract(span); - //return (int)((int)time.Ticks / 10000); - } - - /// - /// 获取正确可用的生日 - /// - /// - public static DateTime GetUsableBirthday(int year, int month, int day) - { - year = Math.Min(DateTime.Now.Year, Math.Max(1921, year));//确保年在1921到今年之间 - month = Math.Min(12, Math.Max(1, month));//确保月份在1-12月之间 - day = Math.Min(31, Math.Max(1, day));//确保天在1-31日之间 - DateTime birthday; - while (true) - { - if (DateTime.TryParse($"{year}-{month}-{day}", out birthday)) - { - break; - } - else - { - day--;//如果日期不正确,则减一 - } - } - return birthday; - } - - /// - /// 获取本周六日期(周六为一周最后一天) - /// - /// - public static DateTime GetStaturdayInWeek(DateTime date) - { - DateTime staturday = date.Date; - while (staturday.DayOfWeek != DayOfWeek.Saturday) - { - staturday = staturday.AddDays(1); - } - return staturday; - } - - /// - /// 获取本周日日期(周日为一周的第一天) - /// - /// - public static DateTime GetSundayInWeek(DateTime date) - { - DateTime sunday = date.Date; - while (sunday.DayOfWeek != DayOfWeek.Sunday) - { - sunday = sunday.AddDays(-1); - } - return sunday; - } - - /// - /// 获取当天是本年度第几周 - /// - /// - public static int GetWeekOfYear(DateTime date) - { - var firstDay = new DateTime(date.Year, 1, 1); - int firstWeekendDayOfYear = 7 - (int)firstDay.DayOfWeek;//第一个周末(周六)是几号 - int lastWeek = (date.DayOfYear - firstWeekendDayOfYear + 6) / 7; - return lastWeek + 1; - } - - /// - /// 获取指定周的第一天(周日) - /// - /// - public static DateTime GetSundayOfWeek(int year, int weeek) - { - var firstDay = new DateTime(year, 1, 1); - int firstWeekend = 7 - (int)firstDay.DayOfWeek;//第一个周末(周六)是几号 - int dayOfYear = firstWeekend + (weeek - 2) * 7; - return firstDay.AddDays(dayOfYear); - } - } +using System; + +namespace Senparc.Ncf.Core.Utility +{ + public static class DateTimeUtility + { + public static long GetJavascriptTimestamp(System.DateTime input) + { + return input.AddTicks((-1) * DateTime.Parse("1970-1-1").Ticks).Ticks / 10000; + //System.TimeSpan span = new System.TimeSpan(System.DateTime.Parse("1970-1-1").Ticks); + //System.DateTime time = input.Subtract(span); + //return (int)((int)time.Ticks / 10000); + } + + /// + /// Get the correct available birthday + /// + /// + public static DateTime GetUsableBirthday(int year, int month, int day) + { + year = Math.Min(DateTime.Now.Year, Math.Max(1921, year));//Make sure the year is between 1921 and this year + month = Math.Min(12, Math.Max(1, month));//Make sure the month is between January and December + day = Math.Min(31, Math.Max(1, day));//Make sure the day is between 1-31 + DateTime birthday; + while (true) + { + if (DateTime.TryParse($"{year}-{month}-{day}", out birthday)) + { + break; + } + else + { + day--;//If the date is incorrect, subtract one + } + } + return birthday; + } + + /// + /// Get the date of this Saturday (Saturday is the last day of the week) + /// + /// + public static DateTime GetStaturdayInWeek(DateTime date) + { + DateTime staturday = date.Date; + while (staturday.DayOfWeek != DayOfWeek.Saturday) + { + staturday = staturday.AddDays(1); + } + return staturday; + } + + /// + /// Get the date of this Sunday (Sunday is the first day of the week) + /// + /// + public static DateTime GetSundayInWeek(DateTime date) + { + DateTime sunday = date.Date; + while (sunday.DayOfWeek != DayOfWeek.Sunday) + { + sunday = sunday.AddDays(-1); + } + return sunday; + } + + /// + /// Get the current week of the year + /// + /// + public static int GetWeekOfYear(DateTime date) + { + var firstDay = new DateTime(date.Year, 1, 1); + int firstWeekendDayOfYear = 7 - (int)firstDay.DayOfWeek;//When is the first weekend (Saturday)? + int lastWeek = (date.DayOfYear - firstWeekendDayOfYear + 6) / 7; + return lastWeek + 1; + } + + /// + /// Get the first day of the specified week (Sunday) + /// + /// + public static DateTime GetSundayOfWeek(int year, int weeek) + { + var firstDay = new DateTime(year, 1, 1); + int firstWeekend = 7 - (int)firstDay.DayOfWeek;//When is the first weekend (Saturday)? + int dayOfYear = firstWeekend + (weeek - 2) * 7; + return firstDay.AddDays(dayOfYear); + } + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/DesUtility.cs b/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/DesUtility.cs index a6d9bbded..d05c244df 100644 --- a/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/DesUtility.cs +++ b/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/DesUtility.cs @@ -1,65 +1,65 @@ -using System; -using System.IO; -using System.Security.Cryptography; -using System.Text; - -namespace Senparc.Ncf.Core.Utility -{ - public class DesUtility - { - //默认密钥向量 - private static byte[] Keys = { 0xA2, 0x24, 0x26, 0x77, 0x99, 0xAB, 0xEF, 0x88 }; - - /// - /// DES加密字符串 - /// - /// 待加密的字符串 - /// 加密密钥,要求为8位 - /// 加密成功返回加密后的字符串,失败返回源串 - public static string EncryptDES(string encryptString, string encryptKey) - { - try - { - byte[] rgbKey = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 8)); - byte[] rgbIV = Keys; - byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString); - DESCryptoServiceProvider dCSP = new DESCryptoServiceProvider(); - MemoryStream mStream = new MemoryStream(); - CryptoStream cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write); - cStream.Write(inputByteArray, 0, inputByteArray.Length); - cStream.FlushFinalBlock(); - return Convert.ToBase64String(mStream.ToArray()); - } - catch - { - return encryptString; - } - } - - /// - /// DES解密字符串 - /// - /// 待解密的字符串 - /// 解密密钥,要求为8位,和加密密钥相同 - /// 解密成功返回解密后的字符串,失败返源串 - public static string DecryptDES(string decryptString, string decryptKey) - { - try - { - byte[] rgbKey = Encoding.UTF8.GetBytes(decryptKey.Substring(0, 8)); - byte[] rgbIV = Keys; - byte[] inputByteArray = Convert.FromBase64String(decryptString); - DESCryptoServiceProvider DCSP = new DESCryptoServiceProvider(); - MemoryStream mStream = new MemoryStream(); - CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write); - cStream.Write(inputByteArray, 0, inputByteArray.Length); - cStream.FlushFinalBlock(); - return Encoding.UTF8.GetString(mStream.ToArray()); - } - catch - { - return decryptString; - } - } - } +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace Senparc.Ncf.Core.Utility +{ + public class DesUtility + { + //Default key vector + private static byte[] Keys = { 0xA2, 0x24, 0x26, 0x77, 0x99, 0xAB, 0xEF, 0x88 }; + + /// + ///DES encrypted string + /// + /// String to be encrypted + /// Encryption key, required to be 8 bits + /// Encryption successfully returns the encrypted string, failure returns the source string + public static string EncryptDES(string encryptString, string encryptKey) + { + try + { + byte[] rgbKey = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 8)); + byte[] rgbIV = Keys; + byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString); + DESCryptoServiceProvider dCSP = new DESCryptoServiceProvider(); + MemoryStream mStream = new MemoryStream(); + CryptoStream cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write); + cStream.Write(inputByteArray, 0, inputByteArray.Length); + cStream.FlushFinalBlock(); + return Convert.ToBase64String(mStream.ToArray()); + } + catch + { + return encryptString; + } + } + + /// + ///DES decrypt string + /// + /// String to be decrypted + /// The decryption key, which is required to be 8 bits, is the same as the encryption key + /// Returns the decrypted string if decryption is successful, otherwise returns the source string + public static string DecryptDES(string decryptString, string decryptKey) + { + try + { + byte[] rgbKey = Encoding.UTF8.GetBytes(decryptKey.Substring(0, 8)); + byte[] rgbIV = Keys; + byte[] inputByteArray = Convert.FromBase64String(decryptString); + DESCryptoServiceProvider DCSP = new DESCryptoServiceProvider(); + MemoryStream mStream = new MemoryStream(); + CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write); + cStream.Write(inputByteArray, 0, inputByteArray.Length); + cStream.FlushFinalBlock(); + return Encoding.UTF8.GetString(mStream.ToArray()); + } + catch + { + return decryptString; + } + } + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/Extensions.cs b/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/Extensions.cs index 478254fda..c3b69c5fe 100644 --- a/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/Extensions.cs +++ b/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/Extensions.cs @@ -1,95 +1,95 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Reflection; - -namespace Senparc.Ncf.Core.Utility -{ - public static class Extensions - { - /// - /// 获取翻页时跳过的记录数 - /// - /// 当前页码 - /// 每页记录数 - /// - public static int GetSkipRecord(int pageIndex, int pageCount) - { - return (pageIndex - 1) * pageCount; - } - - /// - /// 获取数组的字典类型(key为index,value为数组内容)。通常用于配合枚举类型 - /// - /// - /// - public static Dictionary GetDictionaryFromStringArray(this object[] strArr) - { - Dictionary dic = new Dictionary(); - int i = 0; - foreach (var item in strArr) - { - dic.Add(i, item.ToString()); - i++; - } - return dic; - } - - /// - /// 获取枚举成员,转为Dictionary类型 - /// - /// - /// 是否使用枚举类型的描述 - /// - public static Dictionary GetDictionaryForEnums(this Type enumType, bool useDescription = false, bool addBlankOption = false, string blankOptionText = null) - { - if (!enumType.IsEnum) - { - throw new Exception("此对象不是Enum类型!"); - } - Dictionary dic = new Dictionary(); - if (addBlankOption) - { - dic.Add("", blankOptionText ?? "");//添加空白项 - } - foreach (int item in Enum.GetValues(enumType)) - { - string name = Enum.GetName(enumType, item); - if (useDescription) - { - FieldInfo fi = enumType.GetField(Enum.GetName(enumType, item)); - var dna = (DescriptionAttribute)Attribute.GetCustomAttribute(fi, typeof(DescriptionAttribute)); - if (dna != null) - { - name = dna.Description; - } - } - - name = name ?? Enum.GetName(enumType, item); - dic.Add(item.ToString(), name); - } - return dic; - } - - public static string GetDescriptionForEnum(this Type enumType, int item) - { - if (!enumType.IsEnum) - { - throw new Exception("此对象不是Enum类型!"); - } - string name = null; - FieldInfo fi = enumType.GetField(Enum.GetName(enumType, item)); - var dna = (DescriptionAttribute)Attribute.GetCustomAttribute(fi, typeof(DescriptionAttribute)); - if (dna != null) - { - name = dna.Description; - } - return name; - } - - public static string GetTaskPrice(this decimal price) - { - return price == 0 ? "开发者竞价" : price.ToString("C"); - } - } +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Reflection; + +namespace Senparc.Ncf.Core.Utility +{ + public static class Extensions + { + /// + /// Get the number of records skipped when turning pages + /// + /// Current page number + /// Number of records per page + /// + public static int GetSkipRecord(int pageIndex, int pageCount) + { + return (pageIndex - 1) * pageCount; + } + + /// + /// Get the dictionary type of the array (key is index, value is array content). Usually used with enumeration types + /// + /// + /// + public static Dictionary GetDictionaryFromStringArray(this object[] strArr) + { + Dictionary dic = new Dictionary(); + int i = 0; + foreach (var item in strArr) + { + dic.Add(i, item.ToString()); + i++; + } + return dic; + } + + /// + /// Get the enumeration members and convert them to Dictionary type + /// + /// + /// Whether to use the description of the enumeration type + /// + public static Dictionary GetDictionaryForEnums(this Type enumType, bool useDescription = false, bool addBlankOption = false, string blankOptionText = null) + { + if (!enumType.IsEnum) + { + throw new Exception("此对象不是Enum类型!"); + } + Dictionary dic = new Dictionary(); + if (addBlankOption) + { + dic.Add("", blankOptionText ?? "");//Add blank items + } + foreach (int item in Enum.GetValues(enumType)) + { + string name = Enum.GetName(enumType, item); + if (useDescription) + { + FieldInfo fi = enumType.GetField(Enum.GetName(enumType, item)); + var dna = (DescriptionAttribute)Attribute.GetCustomAttribute(fi, typeof(DescriptionAttribute)); + if (dna != null) + { + name = dna.Description; + } + } + + name = name ?? Enum.GetName(enumType, item); + dic.Add(item.ToString(), name); + } + return dic; + } + + public static string GetDescriptionForEnum(this Type enumType, int item) + { + if (!enumType.IsEnum) + { + throw new Exception("此对象不是Enum类型!"); + } + string name = null; + FieldInfo fi = enumType.GetField(Enum.GetName(enumType, item)); + var dna = (DescriptionAttribute)Attribute.GetCustomAttribute(fi, typeof(DescriptionAttribute)); + if (dna != null) + { + name = dna.Description; + } + return name; + } + + public static string GetTaskPrice(this decimal price) + { + return price == 0 ? "开发者竞价" : price.ToString("C"); + } + } } \ No newline at end of file diff --git a/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/IoUtility.cs b/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/IoUtility.cs index 884debdea..0add5ce79 100644 --- a/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/IoUtility.cs +++ b/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/IoUtility.cs @@ -1,48 +1,48 @@ -namespace Senparc.Ncf.Core.Utility -{ - public class IoUtility - { - //COCONET .net core不支持FileSystemRights,需要继续改进 - - //// Adds an ACL entry on the specified directory for the specified account. - //public static void AddDirectorySecurity(string FileName, string User, FileSystemRights Rights, AccessControlType ControlType) - //{ - // // Create a new DirectoryInfo object. - // DirectoryInfo dInfo = new DirectoryInfo(FileName); - - // // Get a DirectorySecurity object that represents the - // // current security settings. - // DirectorySecurity dSecurity = dInfo.GetAccessControl(); - - // // Add the FileSystemAccessRule to the security settings. - // dSecurity.AddAccessRule(new FileSystemAccessRule(User, - // Rights, - // ControlType)); - - // // Set the new access settings. - // dInfo.SetAccessControl(dSecurity); - - //} - - //// Removes an ACL entry on the specified directory for the specified account. - //public static void RemoveDirectorySecurity(string FileName, string User, FileSystemRights Rights, AccessControlType ControlType) - //{ - // // Create a new DirectoryInfo object. - // DirectoryInfo dInfo = new DirectoryInfo(FileName); - - // // Get a DirectorySecurity object that represents the - // // current security settings. - // DirectorySecurity dSecurity = dInfo.GetAccessControl(); - - // // Add the FileSystemAccessRule to the security settings. - // dSecurity.RemoveAccessRule(new FileSystemAccessRule(User, - // Rights, - // ControlType)); - - // // Set the new access settings. - // dInfo.SetAccessControl(dSecurity); - - //} - - } -} +namespace Senparc.Ncf.Core.Utility +{ + public class IoUtility + { + //COCONET .net core does not support FileSystemRights and needs to continue to be improved. + + //// Adds an ACL entry on the specified directory for the specified account. + //public static void AddDirectorySecurity(string FileName, string User, FileSystemRights Rights, AccessControlType ControlType) + //{ + // // Create a new DirectoryInfo object. + // DirectoryInfo dInfo = new DirectoryInfo(FileName); + + // // Get a DirectorySecurity object that represents the + // // current security settings. + // DirectorySecurity dSecurity = dInfo.GetAccessControl(); + + // // Add the FileSystemAccessRule to the security settings. + // dSecurity.AddAccessRule(new FileSystemAccessRule(User, + // Rights, + // ControlType)); + + // // Set the new access settings. + // dInfo.SetAccessControl(dSecurity); + + //} + + //// Removes an ACL entry on the specified directory for the specified account. + //public static void RemoveDirectorySecurity(string FileName, string User, FileSystemRights Rights, AccessControlType ControlType) + //{ + // // Create a new DirectoryInfo object. + // DirectoryInfo dInfo = new DirectoryInfo(FileName); + + // // Get a DirectorySecurity object that represents the + // // current security settings. + // DirectorySecurity dSecurity = dInfo.GetAccessControl(); + + // // Add the FileSystemAccessRule to the security settings. + // dSecurity.RemoveAccessRule(new FileSystemAccessRule(User, + // Rights, + // ControlType)); + + // // Set the new access settings. + // dInfo.SetAccessControl(dSecurity); + + //} + + } +} diff --git a/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/MD5.cs b/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/MD5.cs index 06e3d80bf..3c8a94273 100644 --- a/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/MD5.cs +++ b/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/MD5.cs @@ -1,24 +1,24 @@ -using System; -using System.Security.Cryptography; -using System.Text; - -namespace Senparc.Ncf.Core.Utility -{ - /// - /// MD5 加密 - /// - public static class MD5 - { - /// - /// 获得 NCF 系统内全局一致的加盐的 MD5 加密结果 - /// - /// 原始密码 - /// 盐 - /// 默认为 UTF8 - /// - public static string GetMD5Code(string str, string salt, Encoding encoding = null) - { - return Senparc.CO2NET.Helpers.EncryptHelper.GetMD5(str + salt, encoding ?? Encoding.UTF8); - } - } -} +using System; +using System.Security.Cryptography; +using System.Text; + +namespace Senparc.Ncf.Core.Utility +{ + /// + ///MD5 encryption + /// + public static class MD5 + { + /// + /// Obtain globally consistent salted MD5 encryption results within the NCF system + /// + /// Original password + /// Salt + /// Default is UTF8 + /// + public static string GetMD5Code(string str, string salt, Encoding encoding = null) + { + return Senparc.CO2NET.Helpers.EncryptHelper.GetMD5(str + salt, encoding ?? Encoding.UTF8); + } + } +} diff --git a/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/W3wp.cs b/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/W3wp.cs index b0ba03d1f..0c83f72a6 100644 --- a/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/W3wp.cs +++ b/src/Basic/Senparc.Ncf.Utility/Senparc.Core.Utility/W3wp.cs @@ -1,56 +1,56 @@ -//using System.Management; - -namespace Senparc.Ncf.Core.Utility -{ - public class W3wp - { - private W3wp() { } - - //COCONET .net core不支持System.Management,需要继续改进 - - //public static string GetAllW3wp(string input) - //{ - // System.Management.ObjectQuery oQuery = new System.Management.ObjectQuery("select * from Win32_Process where Name='w3wp.exe'"); - // ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oQuery); - // ManagementObjectCollection oReturnCollection = oSearcher.Get(); - - // string pid; - // string cmdLine; - // StringBuilder sb = new StringBuilder(); - // foreach (ManagementObject oReturn in oReturnCollection) - // { - // pid = oReturn.GetPropertyValue("ProcessId").ToString(); - // cmdLine = (string)oReturn.GetPropertyValue("CommandLine"); - // string pattern = "-ap \"(.*)\""; - // Regex regex = new Regex(pattern, RegexOptions.IgnoreCase); - // Match match = regex.Match(cmdLine); - // string appPoolName = match.Groups[1].ToString(); - // sb.AppendFormat("W3WP.exe PID: {0} AppPoolId:{1}\r\n", pid, appPoolName); - // } - // return sb.ToString(); - //} - - //public static string GetAppPollName(string input) - //{ - // System.Management.ObjectQuery oQuery = new System.Management.ObjectQuery("select * from Win32_Process where Name='w3wp.exe'"); - // ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oQuery); - // ManagementObjectCollection oReturnCollection = oSearcher.Get(); - - // string pid; - // string cmdLine; - // string appPollNameResult = null; - // foreach (ManagementObject oReturn in oReturnCollection) - // { - // pid = oReturn.GetPropertyValue("ProcessId").ToString(); - // cmdLine = (string)oReturn.GetPropertyValue("CommandLine"); - // string pattern = "-ap \"(.*)\""; - // Regex regex = new Regex(pattern, RegexOptions.IgnoreCase); - // Match match = regex.Match(cmdLine); - // string appPoolName = match.Groups[1].ToString(); - // appPollNameResult = appPoolName.Substring(0, appPoolName.IndexOf(@"""")); - // break; - // } - // return appPollNameResult; - //} - } -} +//using System.Management; + +namespace Senparc.Ncf.Core.Utility +{ + public class W3wp + { + private W3wp() { } + + //COCONET .net core does not support System.Management and needs to continue to be improved. + + //public static string GetAllW3wp(string input) + //{ + // System.Management.ObjectQuery oQuery = new System.Management.ObjectQuery("select * from Win32_Process where Name='w3wp.exe'"); + // ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oQuery); + // ManagementObjectCollection oReturnCollection = oSearcher.Get(); + + // string pid; + // string cmdLine; + // StringBuilder sb = new StringBuilder(); + // foreach (ManagementObject oReturn in oReturnCollection) + // { + // pid = oReturn.GetPropertyValue("ProcessId").ToString(); + // cmdLine = (string)oReturn.GetPropertyValue("CommandLine"); + // string pattern = "-ap \"(.*)\""; + // Regex regex = new Regex(pattern, RegexOptions.IgnoreCase); + // Match match = regex.Match(cmdLine); + // string appPoolName = match.Groups[1].ToString(); + // sb.AppendFormat("W3WP.exe PID: {0} AppPoolId:{1}\r\n", pid, appPoolName); + // } + // return sb.ToString(); + //} + + //public static string GetAppPollName(string input) + //{ + // System.Management.ObjectQuery oQuery = new System.Management.ObjectQuery("select * from Win32_Process where Name='w3wp.exe'"); + // ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oQuery); + // ManagementObjectCollection oReturnCollection = oSearcher.Get(); + + // string pid; + // string cmdLine; + // string appPollNameResult = null; + // foreach (ManagementObject oReturn in oReturnCollection) + // { + // pid = oReturn.GetPropertyValue("ProcessId").ToString(); + // cmdLine = (string)oReturn.GetPropertyValue("CommandLine"); + // string pattern = "-ap \"(.*)\""; + // Regex regex = new Regex(pattern, RegexOptions.IgnoreCase); + // Match match = regex.Match(cmdLine); + // string appPoolName = match.Groups[1].ToString(); + // appPollNameResult = appPoolName.Substring(0, appPoolName.IndexOf(@"""")); + // break; + // } + // return appPollNameResult; + //} + } +} diff --git a/src/Basic/Senparc.Ncf.Utility/SenparcHttpContext.cs b/src/Basic/Senparc.Ncf.Utility/SenparcHttpContext.cs index db84dd0f2..ec8e4b993 100644 --- a/src/Basic/Senparc.Ncf.Utility/SenparcHttpContext.cs +++ b/src/Basic/Senparc.Ncf.Utility/SenparcHttpContext.cs @@ -1,80 +1,80 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using System.IO; - -namespace Microsoft.AspNetCore.Http -{ - //参考:https://www.cnblogs.com/yuangang/archive/2016/08/08/5743660.html - - public static class SenparcHttpContext - { - /// - /// 目录分隔符:Windows下“\”,Mac OS和Linux下“\” - /// - public static string DirectorySeparatorChar { get; } = Path.DirectorySeparatorChar.ToString(); - - /// - /// 包含引用程序的目录绝对路径 - /// - public static string ContentRootPath { get; } = DI.ServiceProvider.GetRequiredService().ContentRootPath; - - /// - /// 包含引用程序的目录绝对路径 - /// - public static string ContentWebRootPath { get; } = DI.ServiceProvider.GetRequiredService().WebRootPath; - - - public static HttpContext Current - { - get - { - try - { - object factory = DI.ServiceProvider.GetService(typeof(IHttpContextAccessor)); - HttpContext context = ((HttpContextAccessor)factory).HttpContext; - return context; - } - catch - { - return null; - } - } - } - - /// - /// 获取文件绝对路径 - /// - /// 文件路径 - /// - public static string MapPath(string path) - { - return IsAbsolute(path) - ? path - : Path.Combine(ContentRootPath, path.TrimStart('~', '/').Replace("/", DirectorySeparatorChar)); - } - - /// - /// 获取文件绝对路径 - /// - /// 文件路径 - /// - public static string MapWebPath(string path) - { - return IsAbsolute(path) - ? path - : Path.Combine(ContentWebRootPath, path.TrimStart('~', '/').Replace("/", DirectorySeparatorChar)); - } - - /// - /// 是否是绝对路径 - /// windows下判断 路径是否包含 ":" - /// Mac OS、Linux下判断 路径是否包含 "\" - /// - /// 路径 - /// - public static bool IsAbsolute(string path) - { - return Path.VolumeSeparatorChar == ':' ? path.IndexOf(Path.VolumeSeparatorChar) > 0 : path.IndexOf('\\') > 0; - } - } -} +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using System.IO; + +namespace Microsoft.AspNetCore.Http +{ + //Reference: https://www.cnblogs.com/yuangang/archive/2016/08/08/5743660.html + + public static class SenparcHttpContext + { + /// + /// Directory separator: "\" under Windows, "\" under Mac OS and Linux + /// + public static string DirectorySeparatorChar { get; } = Path.DirectorySeparatorChar.ToString(); + + /// + /// Absolute path to the directory containing the referencing program + /// + public static string ContentRootPath { get; } = DI.ServiceProvider.GetRequiredService().ContentRootPath; + + /// + /// Absolute path to the directory containing the referencing program + /// + public static string ContentWebRootPath { get; } = DI.ServiceProvider.GetRequiredService().WebRootPath; + + + public static HttpContext Current + { + get + { + try + { + object factory = DI.ServiceProvider.GetService(typeof(IHttpContextAccessor)); + HttpContext context = ((HttpContextAccessor)factory).HttpContext; + return context; + } + catch + { + return null; + } + } + } + + /// + /// Get the absolute path of the file + /// + /// File path + /// + public static string MapPath(string path) + { + return IsAbsolute(path) + ? path + : Path.Combine(ContentRootPath, path.TrimStart('~', '/').Replace("/", DirectorySeparatorChar)); + } + + /// + /// Get the absolute path of the file + /// + /// File path + /// + public static string MapWebPath(string path) + { + return IsAbsolute(path) + ? path + : Path.Combine(ContentWebRootPath, path.TrimStart('~', '/').Replace("/", DirectorySeparatorChar)); + } + + /// + /// Is it an absolute path? + /// Determine whether the path contains ":" under windows + /// Determine whether the path contains "\" under Mac OS and Linux + /// + /// Path + /// + public static bool IsAbsolute(string path) + { + return Path.VolumeSeparatorChar == ':' ? path.IndexOf(Path.VolumeSeparatorChar) > 0 : path.IndexOf('\\') > 0; + } + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfAutoConfigurationMappingAttribute.cs b/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfAutoConfigurationMappingAttribute.cs index e4c649843..76a743a73 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfAutoConfigurationMappingAttribute.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfAutoConfigurationMappingAttribute.cs @@ -1,16 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.XncfBase.Attributes -{ - /// - /// 自动配置 ConfigurationMapping 特性 - /// 注意:添加此属性后,ConfigurationMapping 中的配置会被优先注入到 SenparcEntities 系统对象,

- /// 否则,当某实体没有创建 ConfigurationMapping 时,会将其默认属性注入到 SenparcEntities 系统对象。
- ///
- [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class XncfAutoConfigurationMappingAttribute : Attribute - { - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.XncfBase.Attributes +{ + /// + /// Automatically configure the ConfigurationMapping attribute + /// Note: After adding this attribute, the configuration in ConfigurationMapping will be injected into the SenparcEntities system object first,

+ /// Otherwise, when no ConfigurationMapping is created for an entity, its default properties will be injected into the SenparcEntities system object.
+ ///
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class XncfAutoConfigurationMappingAttribute : Attribute + { + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfMethodAttribute.cs b/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfMethodAttribute.cs index afdaa5e8e..d8c89871f 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfMethodAttribute.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfMethodAttribute.cs @@ -1,17 +1,17 @@ -using System; - -namespace Senparc.Ncf.XncfBase -{ - /// - /// Xncf 模块特性 - 扩展方法 - /// - public class XncfMethodAttribute : Attribute - { - public string Name { get; set; } - - public XncfMethodAttribute(string name) - { - Name = name; - } - } -} +using System; + +namespace Senparc.Ncf.XncfBase +{ + /// + ///Xncf module attributes - extension methods + /// + public class XncfMethodAttribute : Attribute + { + public string Name { get; set; } + + public XncfMethodAttribute(string name) + { + Name = name; + } + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfOrderAttribute.cs b/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfOrderAttribute.cs index c13d2854a..bc78b7d59 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfOrderAttribute.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfOrderAttribute.cs @@ -1,26 +1,26 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.XncfBase -{ - /// - /// Xncf 模块执行顺序,Order 数字越大,执行越靠前,如果非系统关键模块,尽量靠后 - /// - public class XncfOrderAttribute : Attribute - { - /// - /// - /// - /// 加载顺序,数字越大加载顺序越靠前。请严格按照参考数值:0:普通(默认),1-5000:需要预加载的重要模块,>5000:系统及基础模块 - public XncfOrderAttribute(int order) - { - Order = order; - } - - /// - /// 加载顺序,数字越大加载顺序越靠前。请严格按照参考数值:0:普通(默认),1-5000:需要预加载的重要模块,>5000:系统及基础模块 - /// - public int Order { get; set; } - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.XncfBase +{ + /// + /// Xncf module execution order. The larger the Order number, the earlier the execution. If it is not a system-critical module, try to go as far back as possible. + /// + public class XncfOrderAttribute : Attribute + { + /// + /// + /// + /// Loading order, the larger the number, the higher the loading order. Please strictly follow the reference values: 0: normal (default), 1-5000: important modules that need to be preloaded, >5000: system and basic modules + public XncfOrderAttribute(int order) + { + Order = order; + } + + /// + /// Loading order, the larger the number, the higher the loading order. Please strictly follow the reference values: 0: normal (default), 1-5000: important modules that need to be preloaded, >5000: system and basic modules + /// + public int Order { get; set; } + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfRegisterAttribute.cs b/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfRegisterAttribute.cs index 74bd6e09a..1b2bcd300 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfRegisterAttribute.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Attributes/XncfRegisterAttribute.cs @@ -1,12 +1,12 @@ -using System; - -namespace Senparc.Ncf.XncfBase -{ - /// - /// Xncf 模块特性 - 注册 - /// - public class XncfRegisterAttribute : Attribute - { - - } -} +using System; + +namespace Senparc.Ncf.XncfBase +{ + /// + ///Xncf Module Properties - Register + /// + public class XncfRegisterAttribute : Attribute + { + + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/AutoMapper/XncfModuleProfile.cs b/src/Basic/Senparc.Ncf.XncfBase/AutoMapper/XncfModuleProfile.cs index d1a9138e6..517f6f5fa 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/AutoMapper/XncfModuleProfile.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/AutoMapper/XncfModuleProfile.cs @@ -1,32 +1,32 @@ -using AutoMapper; -using Senparc.CO2NET.Trace; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.XncfBase.AutoMapper -{ - /// - /// Xncf 模块使用的 AutoMap 配置 - /// - public class XncfModuleProfile : Profile - { - - public XncfModuleProfile() - { - //Console.WriteLine("XncfModuleProfile, XncfRegisterManager.RegisterList Count:" + XncfRegisterManager.RegisterList.Count); - foreach (var register in XncfRegisterManager.RegisterList) - { - //Console.WriteLine("register:" + register.Name); - if (register.AutoMapMappingConfigs != null) - { - //Console.WriteLine("register.AutoMapMappingConfigs:" + register.Name + "/" + register.AutoMapMappingConfigs.Count); - foreach (var config in register.AutoMapMappingConfigs) - { - config(this); - } - } - } - } - } -} +using AutoMapper; +using Senparc.CO2NET.Trace; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.XncfBase.AutoMapper +{ + /// + ///AutoMap configuration used by the Xncf module + /// + public class XncfModuleProfile : Profile + { + + public XncfModuleProfile() + { + //Console.WriteLine("XncfModuleProfile, XncfRegisterManager.RegisterList Count:" + XncfRegisterManager.RegisterList.Count); + foreach (var register in XncfRegisterManager.RegisterList) + { + //Console.WriteLine("register:" + register.Name); + if (register.AutoMapMappingConfigs != null) + { + //Console.WriteLine("register.AutoMapMappingConfigs:" + register.Name + "/" + register.AutoMapMappingConfigs.Count); + foreach (var config in register.AutoMapMappingConfigs) + { + config(this); + } + } + } + } + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Database/SenparcDesignTimeDbContextFactoryBase.cs b/src/Basic/Senparc.Ncf.XncfBase/Database/SenparcDesignTimeDbContextFactoryBase.cs index 1cce4e0f8..21c79d1ef 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Database/SenparcDesignTimeDbContextFactoryBase.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Database/SenparcDesignTimeDbContextFactoryBase.cs @@ -1,254 +1,254 @@ -using log4net; -using Microsoft.AspNetCore.Builder; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Design; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Senparc.CO2NET; -using Senparc.CO2NET.Extensions; -using Senparc.CO2NET.RegisterServices; -using Senparc.Ncf.Core.Config; -using Senparc.Ncf.Core.Exceptions; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Database; -using Senparc.Ncf.XncfBase.Helpers; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Senparc.Ncf.XncfBase.Database -{ - /// - /// 提供给数据库 Migration 使用的 DesignTimeDbContextFactory - /// - /// - public abstract class SenparcDesignTimeDbContextFactoryBase - : SenparcDesignTimeDbContextFactoryBase - where TSenparcEntities : DbContext, ISenparcEntitiesDbContext - where TXncfDatabaseRegister : class, IXncfDatabase, new() - { - public override TSenparcEntities GetDbContextInstance(DbContextOptions dbContextOptions) - { - //获取 XncfDatabase 对象 - //var databaseRegister = Activator.CreateInstance(typeof(TXncfDatabaseRegister)) as TXncfDatabaseRegister; - - //获取当前适用的 DbContext 类型 - var dbContextType = MultipleDatabasePool.Instance.GetXncfDbContextType(typeof(TXncfDatabaseRegister)); - - var constructorParams = new List() { - dbContextOptions - }; - - //判断构造函数参数个数 - if (dbContextType.GetConstructors().First().GetParameters().Length == 2) - { - constructorParams.Add(SenparcDI.GetServiceProvider());//添加第二个参数(目前只为系统基础对象,如 SystemServiceEntities 使用) - } - - //获取 XncfSenparcEntities 实例 - var xncfSenparcEntities = Activator.CreateInstance(dbContextType, constructorParams.ToArray()) as TSenparcEntities; - return xncfSenparcEntities; - } - - private readonly TXncfDatabaseRegister _register; - - - protected SenparcDesignTimeDbContextFactoryBase(string rootDirectoryPath, string databaseName = "Local", string note = null, string dbMigrationAssemblyName = null) - : base(StartupHelper.GetXncfVersion(), rootDirectoryPath, databaseName, note) - { - _register = System.Activator.CreateInstance(); - //var databaseRegister = _register as IXncfRegister; - base.XncfDatabaseData = new XncfDatabaseData(_register, dbMigrationAssemblyName); - } - - public override void CreateDbContextAction() - { - var currentDatabaseConfiguration = DatabaseConfigurationFactory.Instance.Current; - Console.WriteLine($"======= XNCF Database ======="); - Console.WriteLine($"Current System Database Type: {currentDatabaseConfiguration.MultipleDatabaseType}"); - Console.WriteLine(); - - if (_register is IXncfRegister xncfRegister) - { - Console.WriteLine($"Name: {xncfRegister.Name}"); - Console.WriteLine($"Menu Name: {xncfRegister.MenuName}"); - Console.WriteLine($"Uid: {xncfRegister.Uid}"); - Console.WriteLine($"Version: {xncfRegister.Version}"); - } - - //获取当前适用的 DbContext 类型 - var multipleDatabasePool = MultipleDatabasePool.Instance; - var dbContextType = multipleDatabasePool.GetXncfDbContextType(typeof(TXncfDatabaseRegister)); - var dbContextName = dbContextType.Name; - Console.WriteLine($"DbContextName: {dbContextName}"); - Console.WriteLine($"==============================="); - } - } - - /// - /// 提供给数据库 Migration 使用的 DesignTimeDbContextFactory - /// (针对非 XNCF 模块的普通 DbContext,在Senparc.Web 项目下进行 Add-Migration 等操作) - /// - /// - public abstract class SenparcDesignTimeDbContextFactoryBase - : IDesignTimeDbContextFactory - where TDbContext : DbContext - { - protected virtual Action ServicesAction { get; } - protected virtual Action AppAction { get; } - - public IDatabaseConfiguration DatabaseConfiguration { get; set; } - - /// - /// 数据库 连接字符串 - /// - public virtual string DatabaseConnectionStr => SenparcDatabaseConnectionConfigs.GetClientConnectionString(); //?? "Server=.\\;Database=NCF;Trusted_Connection=True;integrated security=True;"; - - /// - /// 返回 DbContext 实例 - /// - /// - /// - public abstract TDbContext GetDbContextInstance(DbContextOptions dbContextOptions); - - /// - /// 指定程序集等配置,如: - /// b => systemServiceRegister.DbContextOptionsAction(b, "Senparc.Service") - /// 注意:如果重写,最后一定要执行 base.DbContextOptionsAction() - /// - public virtual Action DbContextOptionsAction => (builder, xncfDatabaseData) => - { - DatabaseConfiguration.DbContextOptionsActionBase(builder, xncfDatabaseData); - }; - - private readonly string _ncfVersion; - private readonly string _note; - - /// - /// XncfDatabaseData - /// - protected XncfDatabaseData XncfDatabaseData { get; set; } - - ///// - ///// 特殊类型,例如 SystemServiceEntities - ///// - //protected Type SpecificSenparcEntites { get; } = null; - - - /// - /// SenparcDesignTimeDbContextFactoryBase 构造函数 - /// - /// NCF 版本号 - /// 将要设置的CO2NET.Config.RootDirectoryPath,一般为 Senparc.Web 或具有 App_Data/Database/SenparcConfig.config 配置文件的目录 - /// 数据库名称,默认为 Local,即 Senparc.Web/appsettings.json 中的 DatabaseName - /// 在日志中输出额外信息 - public SenparcDesignTimeDbContextFactoryBase(string ncfVersion, string rootDirectoryPath, /*XncfDatabaseData xncfDatabaseData = null,*/ - string databaseName = "Local", string note = null) - { - //注释可能出现中文,对中文环境可以配置使用 GB2312 - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - Console.InputEncoding = Encoding.UTF8; - Console.OutputEncoding = Encoding.UTF8; - - SiteConfig.SenparcCoreSetting.DatabaseName = databaseName; - CO2NET.Config.RootDirectoryPath = rootDirectoryPath; - //XncfDatabaseData = xncfDatabaseData; - this._ncfVersion = ncfVersion; - this._note = note; - - Senparc.Ncf.Core.Register.TryRegisterMiniCore(ServicesAction, AppAction); - } - - - /// - /// 创建过程的其他代码 - /// - public abstract void CreateDbContextAction(); - public virtual TDbContext CreateDbContext(string[] args) - { - //获取数据库配置 - DatabaseConfiguration = DatabaseConfigurationFactory.Instance.Current; - - try - { - var repository = LogManager.CreateRepository("NETCoreRepository"); - } - catch - { - } - var serviceCollection = new ServiceCollection(); - var configBuilder = new ConfigurationBuilder(); - var config = configBuilder.Build(); - var senparcSetting = new SenparcSetting() { IsDebug = true }; - var services = serviceCollection.AddSenparcGlobalServices(config); - - //TODO:env 参数从 v0.11 开始使用,需要进一步测试 —— Jeffrey 2021.12.15 - var env = services.BuildServiceProvider().GetService(); - - serviceCollection.AddMemoryCache();//使用内存缓存 - //修复 https://github.com/NeuCharFramework/NCF/issues/13 发现的问题(在非Web环境下无法得到网站根目录路径) - IRegisterService register = RegisterService.Start(senparcSetting); - - //如果运行 Add-Migration 命令,并且获取不到正确的网站根目录,此处可能无法自动获取到连接字符串(上述#13问题), - //也可通过下面已经注释的的提供默认值方式解决(不推荐) - - if (DatabaseConnectionStr.IsNullOrEmpty()) - { - throw new NcfDatabaseException("DatabaseConnectionStr cannot be empty!", DatabaseConfiguration.GetType()); - } - - var sqlConnection = DatabaseConnectionStr; - - Console.WriteLine(Senparc.Ncf.Core.VersionManager.GetVersionNote(_ncfVersion, _note)); - - Console.WriteLine("======= Connection String ======="); - Console.WriteLine(sqlConnection); - Console.WriteLine("==================================="); - Console.WriteLine(""); - Console.WriteLine("======= Start XNCF Engine ======="); - var dt1 = DateTime.Now; - var startEngineRresult = Senparc.Ncf.XncfBase.Register.StartNcfEngine(serviceCollection, config, env, null);//TODO:env 参数从 v0.11 开始使用,需要进一步测试 —— Jeffrey 2021.12.15 - if (!startEngineRresult.IsNullOrEmpty()) - { - Console.Write(startEngineRresult); - } - Console.WriteLine("======= XNCF Engine Started ======="); - Console.WriteLine($"======= Cost: {SystemTime.DiffTotalMS(dt1)} ms ======="); - Console.WriteLine(); - - Console.WriteLine("======= DatabaseConfiguration ======="); - Console.WriteLine($"DatabaseConfiguration: {DatabaseConfiguration.GetType().Name}"); - - if (XncfDatabaseData != null) - { - if (MultipleDatabasePool.Instance.TryGetValue(DatabaseConfiguration.MultipleDatabaseType, out var xncfDbContextTypes)) - { - Console.WriteLine($"Supported XncfDbContextTypes: {string.Join(",", xncfDbContextTypes)}"); - } - else - { - Console.WriteLine($"Supported XncfDbContextTypes: NONE !!!"); - } - } - - Console.WriteLine($"DbContextOptionsAction Extension Action: {(DatabaseConfiguration.DbContextOptionsActionExtension == null ? "n.s." : "Specified")}"); - Console.WriteLine($"DatabaseUniquePrefix: {(XncfDatabaseData?.XncfDatabaseRegister?.DatabaseUniquePrefix ?? "n.s.")}"); - Console.WriteLine("======================================="); - Console.WriteLine(); - - CreateDbContextAction(); - - //创建 DbContextOptionsBuilder 对象 - var builder = new DbContextOptionsBuilder(); - DatabaseConfiguration.UseDatabase(builder, sqlConnection, XncfDatabaseData, - /* 注意:这里不能用 this.DbContextOptionsAction,否则子类重写将无效!*/ - DbContextOptionsAction); - //单一使用 SQL Server 的方法:builder.UseSqlServer(sqlConnection, DbContextOptionsAction);//beta6 - - return GetDbContextInstance(builder.Options); - } - } -} +using log4net; +using Microsoft.AspNetCore.Builder; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Senparc.CO2NET; +using Senparc.CO2NET.Extensions; +using Senparc.CO2NET.RegisterServices; +using Senparc.Ncf.Core.Config; +using Senparc.Ncf.Core.Exceptions; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Database; +using Senparc.Ncf.XncfBase.Helpers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Senparc.Ncf.XncfBase.Database +{ + /// + /// DesignTimeDbContextFactory provided to database Migration + /// + /// + public abstract class SenparcDesignTimeDbContextFactoryBase + : SenparcDesignTimeDbContextFactoryBase + where TSenparcEntities : DbContext, ISenparcEntitiesDbContext + where TXncfDatabaseRegister : class, IXncfDatabase, new() + { + public override TSenparcEntities GetDbContextInstance(DbContextOptions dbContextOptions) + { + //Get the XncfDatabase object + //var databaseRegister = Activator.CreateInstance(typeof(TXncfDatabaseRegister)) as TXncfDatabaseRegister; + + //Get the currently applicable DbContext type + var dbContextType = MultipleDatabasePool.Instance.GetXncfDbContextType(typeof(TXncfDatabaseRegister)); + + var constructorParams = new List() { + dbContextOptions + }; + + //Determine the number of constructor parameters + if (dbContextType.GetConstructors().First().GetParameters().Length == 2) + { + constructorParams.Add(SenparcDI.GetServiceProvider());//Add a second parameter (currently only used by system base objects such as SystemServiceEntities) + } + + //Get XncfSenparcEntities instance + var xncfSenparcEntities = Activator.CreateInstance(dbContextType, constructorParams.ToArray()) as TSenparcEntities; + return xncfSenparcEntities; + } + + private readonly TXncfDatabaseRegister _register; + + + protected SenparcDesignTimeDbContextFactoryBase(string rootDirectoryPath, string databaseName = "Local", string note = null, string dbMigrationAssemblyName = null) + : base(StartupHelper.GetXncfVersion(), rootDirectoryPath, databaseName, note) + { + _register = System.Activator.CreateInstance(); + //var databaseRegister = _register as IXncfRegister; + base.XncfDatabaseData = new XncfDatabaseData(_register, dbMigrationAssemblyName); + } + + public override void CreateDbContextAction() + { + var currentDatabaseConfiguration = DatabaseConfigurationFactory.Instance.Current; + Console.WriteLine($"======= XNCF Database ======="); + Console.WriteLine($"Current System Database Type: {currentDatabaseConfiguration.MultipleDatabaseType}"); + Console.WriteLine(); + + if (_register is IXncfRegister xncfRegister) + { + Console.WriteLine($"Name: {xncfRegister.Name}"); + Console.WriteLine($"Menu Name: {xncfRegister.MenuName}"); + Console.WriteLine($"Uid: {xncfRegister.Uid}"); + Console.WriteLine($"Version: {xncfRegister.Version}"); + } + + //Get the currently applicable DbContext type + var multipleDatabasePool = MultipleDatabasePool.Instance; + var dbContextType = multipleDatabasePool.GetXncfDbContextType(typeof(TXncfDatabaseRegister)); + var dbContextName = dbContextType.Name; + Console.WriteLine($"DbContextName: {dbContextName}"); + Console.WriteLine($"==============================="); + } + } + + /// + /// DesignTimeDbContextFactory provided to database Migration + /// (For ordinary DbContext of non-XNCF modules, perform Add-Migration and other operations under the Senparc.Web project) + /// + /// + public abstract class SenparcDesignTimeDbContextFactoryBase + : IDesignTimeDbContextFactory + where TDbContext : DbContext + { + protected virtual Action ServicesAction { get; } + protected virtual Action AppAction { get; } + + public IDatabaseConfiguration DatabaseConfiguration { get; set; } + + /// + ///database connection string + /// + public virtual string DatabaseConnectionStr => SenparcDatabaseConnectionConfigs.GetClientConnectionString(); //?? "Server=.\\;Database=NCF;Trusted_Connection=True;integrated security=True;"; + + /// + /// Returns the DbContext instance + /// + /// + /// + public abstract TDbContext GetDbContextInstance(DbContextOptions dbContextOptions); + + /// + /// Specify assembly and other configurations, such as: + /// b => systemServiceRegister.DbContextOptionsAction(b, "Senparc.Service") + /// Note: If you rewrite, you must execute base.DbContextOptionsAction() at the end + /// + public virtual Action DbContextOptionsAction => (builder, xncfDatabaseData) => + { + DatabaseConfiguration.DbContextOptionsActionBase(builder, xncfDatabaseData); + }; + + private readonly string _ncfVersion; + private readonly string _note; + + /// + /// XncfDatabaseData + /// + protected XncfDatabaseData XncfDatabaseData { get; set; } + + ///// + ///// Special types, such as SystemServiceEntities + ///// + //protected Type SpecificSenparcEntites { get; } = null; + + + /// + /// SenparcDesignTimeDbContextFactoryBase constructor + /// + /// NCF version number + /// CO2NET.Config.RootDirectoryPath to be set, usually Senparc.Web or the directory with the App_Data/Database/SenparcConfig.config configuration file + /// Database name, default is Local, that is, DatabaseName in Senparc.Web/appsettings.json + /// Output additional information in the log + public SenparcDesignTimeDbContextFactoryBase(string ncfVersion, string rootDirectoryPath, /*XncfDatabaseData xncfDatabaseData = null,*/ + string databaseName = "Local", string note = null) + { + //Comments may appear in Chinese, and the Chinese environment can be configured to use GB2312 + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + Console.InputEncoding = Encoding.UTF8; + Console.OutputEncoding = Encoding.UTF8; + + SiteConfig.SenparcCoreSetting.DatabaseName = databaseName; + CO2NET.Config.RootDirectoryPath = rootDirectoryPath; + //XncfDatabaseData = xncfDatabaseData; + this._ncfVersion = ncfVersion; + this._note = note; + + Senparc.Ncf.Core.Register.TryRegisterMiniCore(ServicesAction, AppAction); + } + + + /// + /// Other code for the creation process + /// + public abstract void CreateDbContextAction(); + public virtual TDbContext CreateDbContext(string[] args) + { + //Get database configuration + DatabaseConfiguration = DatabaseConfigurationFactory.Instance.Current; + + try + { + var repository = LogManager.CreateRepository("NETCoreRepository"); + } + catch + { + } + var serviceCollection = new ServiceCollection(); + var configBuilder = new ConfigurationBuilder(); + var config = configBuilder.Build(); + var senparcSetting = new SenparcSetting() { IsDebug = true }; + var services = serviceCollection.AddSenparcGlobalServices(config); + + //TODO:env parameter is used starting from v0.11 and needs further testing - Jeffrey 2021.12.15 + var env = services.BuildServiceProvider().GetService(); + + serviceCollection.AddMemoryCache();//Use memory cache + //Fix the problem found in https://github.com/NeuCharFramework/NCF/issues/13 (the website root directory path cannot be obtained in non-Web environment) + IRegisterService register = RegisterService.Start(senparcSetting); + + //If you run the Add-Migration command and cannot obtain the correct website root directory, the connection string may not be automatically obtained here (issue #13 above), + //It can also be solved by providing default values ​​as commented below (not recommended) + + if (DatabaseConnectionStr.IsNullOrEmpty()) + { + throw new NcfDatabaseException("DatabaseConnectionStr cannot be empty!", DatabaseConfiguration.GetType()); + } + + var sqlConnection = DatabaseConnectionStr; + + Console.WriteLine(Senparc.Ncf.Core.VersionManager.GetVersionNote(_ncfVersion, _note)); + + Console.WriteLine("======= Connection String ======="); + Console.WriteLine(sqlConnection); + Console.WriteLine("==================================="); + Console.WriteLine(""); + Console.WriteLine("======= Start XNCF Engine ======="); + var dt1 = DateTime.Now; + var startEngineRresult = Senparc.Ncf.XncfBase.Register.StartNcfEngine(serviceCollection, config, env, null);//TODO:env parameter is used starting from v0.11 and needs further testing - Jeffrey 2021.12.15 + if (!startEngineRresult.IsNullOrEmpty()) + { + Console.Write(startEngineRresult); + } + Console.WriteLine("======= XNCF Engine Started ======="); + Console.WriteLine($"======= Cost: {SystemTime.DiffTotalMS(dt1)} ms ======="); + Console.WriteLine(); + + Console.WriteLine("======= DatabaseConfiguration ======="); + Console.WriteLine($"DatabaseConfiguration: {DatabaseConfiguration.GetType().Name}"); + + if (XncfDatabaseData != null) + { + if (MultipleDatabasePool.Instance.TryGetValue(DatabaseConfiguration.MultipleDatabaseType, out var xncfDbContextTypes)) + { + Console.WriteLine($"Supported XncfDbContextTypes: {string.Join(",", xncfDbContextTypes)}"); + } + else + { + Console.WriteLine($"Supported XncfDbContextTypes: NONE !!!"); + } + } + + Console.WriteLine($"DbContextOptionsAction Extension Action: {(DatabaseConfiguration.DbContextOptionsActionExtension == null ? "n.s." : "Specified")}"); + Console.WriteLine($"DatabaseUniquePrefix: {(XncfDatabaseData?.XncfDatabaseRegister?.DatabaseUniquePrefix ?? "n.s.")}"); + Console.WriteLine("======================================="); + Console.WriteLine(); + + CreateDbContextAction(); + + //Create a DbContextOptionsBuilder object + var builder = new DbContextOptionsBuilder(); + DatabaseConfiguration.UseDatabase(builder, sqlConnection, XncfDatabaseData, + /* Note: this.DbContextOptionsAction cannot be used here, otherwise subclass rewriting will be invalid!*/ + DbContextOptionsAction); + //Single method of using SQL Server: builder.UseSqlServer(sqlConnection, DbContextOptionsAction);//beta6 + + return GetDbContextInstance(builder.Options); + } + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Database/XncfDatabaseDbContext.cs b/src/Basic/Senparc.Ncf.XncfBase/Database/XncfDatabaseDbContext.cs index 595132e21..ee7be5f1a 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Database/XncfDatabaseDbContext.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Database/XncfDatabaseDbContext.cs @@ -1,221 +1,221 @@ -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.Extensions.DependencyInjection; -using Senparc.CO2NET.Trace; -using Senparc.Ncf.Core.Config; -using Senparc.Ncf.Core.Exceptions; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Core.MultiTenant; -using Senparc.Ncf.Database; -using Senparc.Ncf.Database.MultipleMigrationDbContext; -using System; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; - -namespace Senparc.Ncf.XncfBase.Database -{ - /// - /// IXncfDatabase 使用的 DbContext 基类 - /// - public abstract class XncfDatabaseDbContext : DbContext, ISenparcEntitiesDbContext, IMultipleMigrationDbContext - { - MultipleMigrationDbContextAttribute _multipleMigrationDbContext; - /// - /// MultipleMigrationDbContext - /// - public MultipleMigrationDbContextAttribute MultipleMigrationDbContext - { - get - { - if (_multipleMigrationDbContext == null) - { - _multipleMigrationDbContext = this.GetType().GetCustomAttribute(typeof(MultipleMigrationDbContextAttribute)) as MultipleMigrationDbContextAttribute; - - if (_multipleMigrationDbContext == null) - { - throw new NcfDatabaseException($"{this.GetType().Name} 未标记 {nameof(MultipleMigrationDbContextAttribute)} 特性!", null, this.GetType()); - } - } - return _multipleMigrationDbContext; - } - } - - /// - /// 当前 IXncfDatabase 注册类实例 - /// - public IXncfDatabase XncfDatabaseRegister => MultipleMigrationDbContext.XncfDatabaseRegister; - - - protected XncfDatabaseDbContext(DbContextOptions dbContextOptions) - : base(dbContextOptions) - { } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - if (XncfDatabaseRegister == null) - { - throw new ArgumentNullException(nameof(XncfDatabaseRegister)); - } - - XncfDatabaseRegister.OnModelCreating(modelBuilder); - - var types = modelBuilder.Model.GetEntityTypes().Where(e => typeof(EntityBase).IsAssignableFrom(e.ClrType)); - foreach (var entityType in types) - { - SetGlobalQueryMethodInfo(entityType, modelBuilder); - } - - base.OnModelCreating(modelBuilder); - } - - #region ISenparcEntities 接口 - - - public bool? _enableMultiTenant; - - /// - /// 是否启用多租户,默认读取 SiteConfig.SenparcCoreSetting.EnableMultiTenant - /// - public bool EnableMultiTenant - { - get - { - if (!_enableMultiTenant.HasValue) - { - _enableMultiTenant = SiteConfig.SenparcCoreSetting.EnableMultiTenant; - } - return _enableMultiTenant.Value; - } - private set - { - _enableMultiTenant = value; - } - } - - private RequestTenantInfo _tenantInfo; - - /// - /// 当前上下文中租户信息 - /// - public RequestTenantInfo TenantInfo - { - get - { - if (this.EnableMultiTenant && _tenantInfo == null) - { - var serviceProvicer = Senparc.CO2NET.SenparcDI.GetServiceProvider(); - _tenantInfo = MultiTenantHelper.TryGetAndCheckRequestTenantInfo(serviceProvicer, $"SenparcEntitiesDbContextBase.TenantInfo", this); - } - return _tenantInfo; - } - set - { - _tenantInfo = value; - } - } - - private static readonly bool[] _migrated = { true }; - - /// - /// - /// - private void SetGlobalQueryMethodInfo(IMutableEntityType entityType, ModelBuilder modelBuilder) - { - var dbContextType = this.GetType(); - dbContextType.GetMethods(BindingFlags.Public | BindingFlags.Instance) - .Single(t => t.IsGenericMethod && t.Name == "SetGlobalQuery") - .MakeGenericMethod(entityType.ClrType) - .Invoke(this, new object[] { modelBuilder }); - } - - - public void SetGlobalQuery(ModelBuilder builder) where T : EntityBase - { - builder.Entity().HasQueryFilter(z => !z.Flag); - } - - public void ResetMigrate() - { - _migrated[0] = false; - } - - public void Migrate() - { - if (!_migrated[0]) - { - lock (_migrated) - { - if (!_migrated[0]) - { - Database.Migrate(); // apply all migrations - _migrated[0] = true; - } - } - } - } - - - #endregion - - /// - /// 安装过程中使用的 Migrate 系列操作 - /// - /// - /// 实现了数据库接口的 Register,databaseRegister.TryGetXncfDatabaseDbContextType 必须返回 XncfDatabaseDbContext 的子类 - /// 是否需要对 dbContextType 类型进行检查 - /// - public static async Task MigrateOnInstallAsync(IServiceProvider serviceProvider, IXncfDatabase databaseRegister, bool checkdbContextType = true) - { - var dbContextType = databaseRegister.TryGetXncfDatabaseDbContextType; - if (checkdbContextType && !dbContextType.IsSubclassOf(typeof(XncfDatabaseDbContext))) - { - throw new NcfDatabaseException($"dbContextType 参数必须继承自 XncfDatabaseDbContext,当前类型:{dbContextType.FullName}", null, dbContextType); - } - - var xncfDatabaseDbContext = serviceProvider.GetService(dbContextType) as XncfDatabaseDbContext; - await MigrateOnInstallAsync(xncfDatabaseDbContext); - } - - - /// - /// 安装过程中使用的 Migrate 系列操作 - /// - /// - /// - /// - public static async Task MigrateOnInstallAsync(XncfDatabaseDbContext dbContext) - { - if (dbContext is null) - { - throw new ArgumentNullException(nameof(dbContext)); - } - //更新数据库 - var pendingMigs = await dbContext.Database.GetPendingMigrationsAsync(); - - SenparcTrace.SendCustomLog("MigrateOnInstallAsync", $"dbContext:{dbContext.GetType().FullName}"); - - if (pendingMigs.Count() > 0) - { - dbContext.ResetMigrate();//重置合并状态 - - SenparcTrace.SendCustomLog("MigrateOnInstallAsync", $"dbContext.ResetMigrate() 运行完毕"); - - try - { - var script = dbContext.Database.GenerateCreateScript(); - SenparcTrace.SendCustomLog($"MigrateOnInstallAsync", $"{dbContext.GetType().Name}.Database.GenerateCreateScript:\r\n{script}"); - - dbContext.Migrate();//进行合并 - SenparcTrace.SendCustomLog("MigrateOnInstallAsync", $"dbContext.Migrate() 运行完毕"); - } - catch (Exception ex) - { - var currentDatabaseConfiguration = DatabaseConfigurationFactory.Instance.Current; - SenparcTrace.BaseExceptionLog(new NcfDatabaseException(ex.Message, currentDatabaseConfiguration.GetType(), dbContext.GetType(), ex)); - } - } - } - } -} +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.Extensions.DependencyInjection; +using Senparc.CO2NET.Trace; +using Senparc.Ncf.Core.Config; +using Senparc.Ncf.Core.Exceptions; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Core.MultiTenant; +using Senparc.Ncf.Database; +using Senparc.Ncf.Database.MultipleMigrationDbContext; +using System; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; + +namespace Senparc.Ncf.XncfBase.Database +{ + /// + ///DbContext base class used by IXncfDatabase + /// + public abstract class XncfDatabaseDbContext : DbContext, ISenparcEntitiesDbContext, IMultipleMigrationDbContext + { + MultipleMigrationDbContextAttribute _multipleMigrationDbContext; + /// + /// MultipleMigrationDbContext + /// + public MultipleMigrationDbContextAttribute MultipleMigrationDbContext + { + get + { + if (_multipleMigrationDbContext == null) + { + _multipleMigrationDbContext = this.GetType().GetCustomAttribute(typeof(MultipleMigrationDbContextAttribute)) as MultipleMigrationDbContextAttribute; + + if (_multipleMigrationDbContext == null) + { + throw new NcfDatabaseException($"{this.GetType().Name} 未标记 {nameof(MultipleMigrationDbContextAttribute)} 特性!", null, this.GetType()); + } + } + return _multipleMigrationDbContext; + } + } + + /// + /// Current IXncfDatabase registration class instance + /// + public IXncfDatabase XncfDatabaseRegister => MultipleMigrationDbContext.XncfDatabaseRegister; + + + protected XncfDatabaseDbContext(DbContextOptions dbContextOptions) + : base(dbContextOptions) + { } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + if (XncfDatabaseRegister == null) + { + throw new ArgumentNullException(nameof(XncfDatabaseRegister)); + } + + XncfDatabaseRegister.OnModelCreating(modelBuilder); + + var types = modelBuilder.Model.GetEntityTypes().Where(e => typeof(EntityBase).IsAssignableFrom(e.ClrType)); + foreach (var entityType in types) + { + SetGlobalQueryMethodInfo(entityType, modelBuilder); + } + + base.OnModelCreating(modelBuilder); + } + + #region ISenparcEntities 接口 + + + public bool? _enableMultiTenant; + + /// + /// Whether to enable multi-tenancy, the default is to read SiteConfig.SenparcCoreSetting.EnableMultiTenant + /// + public bool EnableMultiTenant + { + get + { + if (!_enableMultiTenant.HasValue) + { + _enableMultiTenant = SiteConfig.SenparcCoreSetting.EnableMultiTenant; + } + return _enableMultiTenant.Value; + } + private set + { + _enableMultiTenant = value; + } + } + + private RequestTenantInfo _tenantInfo; + + /// + /// Tenant information in the current context + /// + public RequestTenantInfo TenantInfo + { + get + { + if (this.EnableMultiTenant && _tenantInfo == null) + { + var serviceProvicer = Senparc.CO2NET.SenparcDI.GetServiceProvider(); + _tenantInfo = MultiTenantHelper.TryGetAndCheckRequestTenantInfo(serviceProvicer, $"SenparcEntitiesDbContextBase.TenantInfo", this); + } + return _tenantInfo; + } + set + { + _tenantInfo = value; + } + } + + private static readonly bool[] _migrated = { true }; + + /// + /// + /// + private void SetGlobalQueryMethodInfo(IMutableEntityType entityType, ModelBuilder modelBuilder) + { + var dbContextType = this.GetType(); + dbContextType.GetMethods(BindingFlags.Public | BindingFlags.Instance) + .Single(t => t.IsGenericMethod && t.Name == "SetGlobalQuery") + .MakeGenericMethod(entityType.ClrType) + .Invoke(this, new object[] { modelBuilder }); + } + + + public void SetGlobalQuery(ModelBuilder builder) where T : EntityBase + { + builder.Entity().HasQueryFilter(z => !z.Flag); + } + + public void ResetMigrate() + { + _migrated[0] = false; + } + + public void Migrate() + { + if (!_migrated[0]) + { + lock (_migrated) + { + if (!_migrated[0]) + { + Database.Migrate(); // apply all migrations + _migrated[0] = true; + } + } + } + } + + + #endregion + + /// + /// Migrate series of operations used during installation + /// + /// + /// Register that implements the database interface, databaseRegister.TryGetXncfDatabaseDbContextType must return a subclass of XncfDatabaseDbContext + /// Whether the dbContextType type needs to be checked + /// + public static async Task MigrateOnInstallAsync(IServiceProvider serviceProvider, IXncfDatabase databaseRegister, bool checkdbContextType = true) + { + var dbContextType = databaseRegister.TryGetXncfDatabaseDbContextType; + if (checkdbContextType && !dbContextType.IsSubclassOf(typeof(XncfDatabaseDbContext))) + { + throw new NcfDatabaseException($"dbContextType 参数必须继承自 XncfDatabaseDbContext,当前类型:{dbContextType.FullName}", null, dbContextType); + } + + var xncfDatabaseDbContext = serviceProvider.GetService(dbContextType) as XncfDatabaseDbContext; + await MigrateOnInstallAsync(xncfDatabaseDbContext); + } + + + /// + /// Migrate series of operations used during installation + /// + /// + /// + /// + public static async Task MigrateOnInstallAsync(XncfDatabaseDbContext dbContext) + { + if (dbContext is null) + { + throw new ArgumentNullException(nameof(dbContext)); + } + //Update database + var pendingMigs = await dbContext.Database.GetPendingMigrationsAsync(); + + SenparcTrace.SendCustomLog("MigrateOnInstallAsync", $"dbContext:{dbContext.GetType().FullName}"); + + if (pendingMigs.Count() > 0) + { + dbContext.ResetMigrate();//Reset merge status + + SenparcTrace.SendCustomLog("MigrateOnInstallAsync", $"dbContext.ResetMigrate() 运行完毕"); + + try + { + var script = dbContext.Database.GenerateCreateScript(); + SenparcTrace.SendCustomLog($"MigrateOnInstallAsync", $"{dbContext.GetType().Name}.Database.GenerateCreateScript:\r\n{script}"); + + dbContext.Migrate();//merge + SenparcTrace.SendCustomLog("MigrateOnInstallAsync", $"dbContext.Migrate() 运行完毕"); + } + catch (Exception ex) + { + var currentDatabaseConfiguration = DatabaseConfigurationFactory.Instance.Current; + SenparcTrace.BaseExceptionLog(new NcfDatabaseException(ex.Message, currentDatabaseConfiguration.GetType(), dbContext.GetType(), ex)); + } + } + } + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Enums.cs b/src/Basic/Senparc.Ncf.XncfBase/Enums.cs index 61f5cc330..c027c2f7c 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Enums.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Enums.cs @@ -1,17 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.XncfBase -{ - /// - /// 版本更新类型 - /// - public enum UpdateVersionType - { - NoUpdate = 0, - MajorUpdate = 1, - MinorUpdate = 2, - PatchUpdate = 3, - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.XncfBase +{ + /// + /// version update type + /// + public enum UpdateVersionType + { + NoUpdate = 0, + MajorUpdate = 1, + MinorUpdate = 2, + PatchUpdate = 3, + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Exceptions/XncfFunctionException.cs b/src/Basic/Senparc.Ncf.XncfBase/Exceptions/XncfFunctionException.cs index 20601568a..aafcaabe6 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Exceptions/XncfFunctionException.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Exceptions/XncfFunctionException.cs @@ -1,21 +1,21 @@ -using Senparc.Ncf.Core.Exceptions; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.XncfBase -{ - /// - /// Xncf 模块方法执行异常 - /// - public class XncfFunctionException : NcfExceptionBase - { - public XncfFunctionException(string message, bool logged = false) : this(message, null, logged) - { - } - - public XncfFunctionException(string message, Exception inner, bool logged = false) : base(message, inner, logged) - { - } - } -} +using Senparc.Ncf.Core.Exceptions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.XncfBase +{ + /// + ///Xncf module method execution exception + /// + public class XncfFunctionException : NcfExceptionBase + { + public XncfFunctionException(string message, bool logged = false) : this(message, null, logged) + { + } + + public XncfFunctionException(string message, Exception inner, bool logged = false) : base(message, inner, logged) + { + } + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/FunctionRenders/FunctionAppRequestBase.cs b/src/Basic/Senparc.Ncf.XncfBase/FunctionRenders/FunctionAppRequestBase.cs index caf580f13..7c89f87ea 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/FunctionRenders/FunctionAppRequestBase.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/FunctionRenders/FunctionAppRequestBase.cs @@ -1,41 +1,41 @@ -using Senparc.CO2NET.Trace; -using Senparc.Ncf.Core.AppServices; -using Senparc.Ncf.XncfBase.Functions; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Ncf.XncfBase.FunctionRenders -{ - public interface IFunctionAppRequest : IAppRequest - { - Task LoadData(IServiceProvider serviceProvider); - } - - /// - /// FunctionAppRequest 基类 - /// - public class FunctionAppRequestBase : AppRequestBase, IFunctionAppRequest - { - public virtual Task LoadData(IServiceProvider serviceProvider) - { - return Task.CompletedTask; - } - - - /// - /// 记录日志 - /// - /// - /// - protected void RecordLog(StringBuilder sb, string msg) - { - FunctionHelper.RecordLog(sb, msg); - } - - } -} +using Senparc.CO2NET.Trace; +using Senparc.Ncf.Core.AppServices; +using Senparc.Ncf.XncfBase.Functions; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Ncf.XncfBase.FunctionRenders +{ + public interface IFunctionAppRequest : IAppRequest + { + Task LoadData(IServiceProvider serviceProvider); + } + + /// + /// FunctionAppRequest base class + /// + public class FunctionAppRequestBase : AppRequestBase, IFunctionAppRequest + { + public virtual Task LoadData(IServiceProvider serviceProvider) + { + return Task.CompletedTask; + } + + + /// + ///log + /// + /// + /// + protected void RecordLog(StringBuilder sb, string msg) + { + FunctionHelper.RecordLog(sb, msg); + } + + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/FunctionRenders/FunctionRenderCollection.cs b/src/Basic/Senparc.Ncf.XncfBase/FunctionRenders/FunctionRenderCollection.cs index 3afd01c34..46a5493e5 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/FunctionRenders/FunctionRenderCollection.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/FunctionRenders/FunctionRenderCollection.cs @@ -1,125 +1,125 @@ -using Senparc.Ncf.Core.AppServices; -using Senparc.Ncf.Core.Models; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace Senparc.Ncf.XncfBase.FunctionRenders -{ - /// - /// FunctionRender 集合。Key:唯一标识,value:MethodInfo - /// - public class FunctionRenderCollection : ConcurrentDictionary> - { - /// - /// 获取 单个 Group 的 Key - /// - /// - /// - public static string GeGrouptKey(MethodInfo methodInfo) - { - return $"{methodInfo.DeclaringType.FullName}-{methodInfo.Name}"; - } - - /// - /// 设置 FunctionRenderBag - /// - /// - /// - public FunctionRenderBag Add(MethodInfo methodInfo, FunctionRenderAttribute functionRenderAttribute) - { - Type registerType = functionRenderAttribute.RegisterType; - if (!this.ContainsKey(registerType)) - { - this[registerType] = new ConcurrentDictionary(); - } - - var registerGroup = this[registerType]; - - var groupKey = GeGrouptKey(methodInfo); - var functionRenderBag = new FunctionRenderBag(groupKey, methodInfo, functionRenderAttribute); - registerGroup[groupKey] = functionRenderBag; - return functionRenderBag; - } - - /// - /// 获取指定注册类型下的 FunctionRender 集合。 - /// - public IReadOnlyList GetByRegisterType(Type registerType) - { - if (registerType == null) - { - return Array.Empty(); - } - - if (!TryGetValue(registerType, out var registerGroup) || registerGroup == null) - { - return Array.Empty(); - } - - return registerGroup.Values.ToList(); - } - - /// - /// 通过模块 UID 获取 FunctionRender 集合。 - /// - public IReadOnlyList GetByModuleUid(string moduleUid) - { - if (string.IsNullOrWhiteSpace(moduleUid)) - { - return Array.Empty(); - } - - var register = XncfRegisterManager.RegisterList.FirstOrDefault(z => z.Uid.Equals(moduleUid, StringComparison.OrdinalIgnoreCase)); - if (register == null) - { - return Array.Empty(); - } - - return GetByRegisterType(register.GetType()); - } - - /// - /// 当输入包含 [#sym:FunctionRender] 时,将指定模块中的 FunctionRender 方法自动转换为可导入的插件对象。 - /// - public IReadOnlyDictionary ResolveSymbolPlugins(IServiceProvider serviceProvider, string symbolExpression, IEnumerable moduleUids) - { - if (!FunctionRenderSymbolHelper.HasFunctionRenderSymbol(symbolExpression) || serviceProvider == null || moduleUids == null) - { - return new Dictionary(StringComparer.OrdinalIgnoreCase); - } - - var pluginObjects = new Dictionary(StringComparer.OrdinalIgnoreCase); - var uidList = moduleUids.Where(z => !string.IsNullOrWhiteSpace(z)).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); - - foreach (var uid in uidList) - { - var functionBags = GetByModuleUid(uid); - if (functionBags.Count == 0) - { - continue; - } - - foreach (var declaringType in functionBags.Select(z => z.MethodInfo?.DeclaringType).Where(z => z != null).Distinct()) - { - var key = declaringType.FullName; - if (pluginObjects.ContainsKey(key)) - { - continue; - } - - var plugin = serviceProvider.GetService(declaringType) ?? Activator.CreateInstance(declaringType); - if (plugin != null) - { - pluginObjects[key] = plugin; - } - } - } - - return pluginObjects; - } - } -} +using Senparc.Ncf.Core.AppServices; +using Senparc.Ncf.Core.Models; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Senparc.Ncf.XncfBase.FunctionRenders +{ + /// + ///FunctionRender collection. Key: unique identifier, value: MethodInfo + /// + public class FunctionRenderCollection : ConcurrentDictionary> + { + /// + /// Get the Key of a single Group + /// + /// + /// + public static string GeGrouptKey(MethodInfo methodInfo) + { + return $"{methodInfo.DeclaringType.FullName}-{methodInfo.Name}"; + } + + /// + ///Set FunctionRenderBag + /// + /// + /// + public FunctionRenderBag Add(MethodInfo methodInfo, FunctionRenderAttribute functionRenderAttribute) + { + Type registerType = functionRenderAttribute.RegisterType; + if (!this.ContainsKey(registerType)) + { + this[registerType] = new ConcurrentDictionary(); + } + + var registerGroup = this[registerType]; + + var groupKey = GeGrouptKey(methodInfo); + var functionRenderBag = new FunctionRenderBag(groupKey, methodInfo, functionRenderAttribute); + registerGroup[groupKey] = functionRenderBag; + return functionRenderBag; + } + + /// + /// Get the FunctionRender collection under the specified registration type. + /// + public IReadOnlyList GetByRegisterType(Type registerType) + { + if (registerType == null) + { + return Array.Empty(); + } + + if (!TryGetValue(registerType, out var registerGroup) || registerGroup == null) + { + return Array.Empty(); + } + + return registerGroup.Values.ToList(); + } + + /// + /// Get the FunctionRender collection by module UID. + /// + public IReadOnlyList GetByModuleUid(string moduleUid) + { + if (string.IsNullOrWhiteSpace(moduleUid)) + { + return Array.Empty(); + } + + var register = XncfRegisterManager.RegisterList.FirstOrDefault(z => z.Uid.Equals(moduleUid, StringComparison.OrdinalIgnoreCase)); + if (register == null) + { + return Array.Empty(); + } + + return GetByRegisterType(register.GetType()); + } + + /// + /// When the input contains [#sym:FunctionRender], automatically convert the FunctionRender method in the specified module into an importable plug-in object. + /// + public IReadOnlyDictionary ResolveSymbolPlugins(IServiceProvider serviceProvider, string symbolExpression, IEnumerable moduleUids) + { + if (!FunctionRenderSymbolHelper.HasFunctionRenderSymbol(symbolExpression) || serviceProvider == null || moduleUids == null) + { + return new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + var pluginObjects = new Dictionary(StringComparer.OrdinalIgnoreCase); + var uidList = moduleUids.Where(z => !string.IsNullOrWhiteSpace(z)).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); + + foreach (var uid in uidList) + { + var functionBags = GetByModuleUid(uid); + if (functionBags.Count == 0) + { + continue; + } + + foreach (var declaringType in functionBags.Select(z => z.MethodInfo?.DeclaringType).Where(z => z != null).Distinct()) + { + var key = declaringType.FullName; + if (pluginObjects.ContainsKey(key)) + { + continue; + } + + var plugin = serviceProvider.GetService(declaringType) ?? Activator.CreateInstance(declaringType); + if (plugin != null) + { + pluginObjects[key] = plugin; + } + } + } + + return pluginObjects; + } + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Functions/EnumType.cs b/src/Basic/Senparc.Ncf.XncfBase/Functions/EnumType.cs index 8ba5821e3..1a72e76ea 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Functions/EnumType.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Functions/EnumType.cs @@ -1,22 +1,22 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.XncfBase.Functions -{ - /// - /// 枚举类型 - /// - /// - public class EnumType where T : struct - { - public T Value { get; set; } - public EnumType() { } - - public EnumType(T value) - { - Value = value; - } - } - -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.XncfBase.Functions +{ + /// + /// enum type + /// + /// + public class EnumType where T : struct + { + public T Value { get; set; } + public EnumType() { } + + public EnumType(T value) + { + Value = value; + } + } + +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Functions/FunctionHelper.cs b/src/Basic/Senparc.Ncf.XncfBase/Functions/FunctionHelper.cs index d6f8ae4ea..7a81488b0 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Functions/FunctionHelper.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Functions/FunctionHelper.cs @@ -1,244 +1,244 @@ -using Senparc.CO2NET.Extensions; -using Senparc.CO2NET.Helpers; -using Senparc.CO2NET.Trace; -using Senparc.Ncf.Core.AppServices; -using Senparc.Ncf.Core.Exceptions; -using Senparc.Ncf.XncfBase.FunctionRenders; -using Senparc.Ncf.XncfBase.Functions.Parameters; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Ncf.XncfBase.Functions -{ - /// - /// Function 帮助类 - /// - public class FunctionHelper - { - /// - /// 记录日志 - /// - /// - /// - public static void RecordLog(StringBuilder sb, string msg) - { - sb.AppendLine($"[{SystemTime.Now.ToString()}]\t{msg}"); - } - - /// - /// 获取所有参数的信息列表 - /// - /// - /// FunctionRenderBag - /// 是否尝试载入数据(参数必须实现 IFunctionParameterLoadDataBase 接口) - /// - public static async Task> GetFunctionParameterInfoAsync(IServiceProvider serviceProvider, FunctionRenderBag functionRenderBag, bool tryLoadData = true) - { - var functionParameterType = functionRenderBag.FunctionParameterType; - - IAppRequest paraObj = null; - - if (ReflectionHelper.HasParameterlessConstructor(functionParameterType)) - { - //包含不带参数的构造函数 - paraObj = functionParameterType.Assembly.CreateInstance(functionParameterType.FullName) as IAppRequest;//TODO:通过 DI 生成 - - if (tryLoadData && paraObj is FunctionAppRequestBase functionRequestPara) - { - try - { - await functionRequestPara.LoadData(serviceProvider);//载入原有信息 - } - catch (Exception ex) - { - SenparcTrace.BaseExceptionLog(ex); - throw; - } - } - } - - var props = functionParameterType.GetProperties(); - ParameterType parameterType = ParameterType.Text; - List result = new List(); - foreach (var prop in props) - { - SelectionList selectionList = null; - parameterType = ParameterType.Text;//默认为文本内容 - //判断是否存在选项 - if (prop.PropertyType == typeof(SelectionList)) - { - var selections = prop.GetValue(paraObj, null) as SelectionList; - switch (selections.SelectionType) - { - case SelectionType.DropDownList: - parameterType = ParameterType.DropDownList; - break; - case SelectionType.CheckBoxList: - parameterType = ParameterType.CheckBoxList; - break; - default: - //TODO: throw - break; - } - selectionList = selections; - } - else if (prop.Name.Contains("SECRET", StringComparison.OrdinalIgnoreCase) || prop.GetCustomAttribute() != null) - { - parameterType = ParameterType.Password; - } - - var name = prop.Name; - string title = null; - string description = null; - var isRequired = prop.GetCustomAttribute() != null; - var descriptionAttr = prop.GetCustomAttribute(); - var maxLengthAttr = prop.GetCustomAttribute(); - var maxLength = 0; - if (descriptionAttr != null && descriptionAttr.Description != null) - { - //分割:名称||说明 - var descriptionAttrArr = descriptionAttr.Description.Split(new[] { "||" }, StringSplitOptions.RemoveEmptyEntries); - title = descriptionAttrArr[0]; - if (descriptionAttrArr.Length > 1) - { - description = descriptionAttrArr[1]; - } - } - - if (maxLengthAttr != null) - { - maxLength = maxLengthAttr.Length; - } - - var systemType = prop.PropertyType.Name; - - object value = null; - try - { - value = prop.GetValue(paraObj); - } - catch (Exception ex) - { - SenparcTrace.BaseExceptionLog(ex); - } - - var functionParamInfo = new FunctionParameterInfo(name, title, description, isRequired, systemType, parameterType, - selectionList ?? new SelectionList(SelectionType.Unknown), value, maxLength); - result.Add(functionParamInfo); - } - return result; - } - - /// - /// 给 SelectionList 对象添加当前解决方案中的 XNCF 项目 - /// (扫描当前解决方案包含的所有领域项目) - /// - /// 当前解决方案是否必须包含 XNCF 项目 - /// 查找 XNCF 模块根目录,如果留 null,则使用 System.IO.Directory.GetCurrentDirectory()获取,并且查找 .sln 所在文件夹 - /// 除了标准的 XNCF 以外额外的项目 - public static List LoadXncfProjects(bool mustHaveXncfModule = false, string rootDir = null, params string[] additionalProjects) - { - var selectList = new List(); - - var currentDir = rootDir ?? System.IO.Directory.GetCurrentDirectory(); - - while (currentDir != null) - { - var slnFile = Directory.GetFiles(currentDir, "*.sln"); - if (slnFile.Length > 0) - { - break; - } - currentDir = Directory.GetParent(currentDir).FullName; - } - - if (currentDir != null) - { - //找到了 SLN 文件,开始展开地毯式搜索 - - //第一步:查找 XNCF - - var projectFolders = Directory.GetDirectories(currentDir, "*.XNCF.*", SearchOption.AllDirectories).ToList(); - - if (additionalProjects != null && additionalProjects.Length > 0) - { - foreach (var proj in additionalProjects) - { - var addProjectFolders = Directory.GetDirectories(currentDir, proj, SearchOption.AllDirectories); - if (addProjectFolders.Count() > 0) - { - projectFolders.AddRange(addProjectFolders); - } - } - } - - foreach (var projectFolder in projectFolders) - { - //第二步:查看 Register 文件是否存在 - var registerFilePath = Path.Combine(projectFolder, "Register.cs"); - if (!File.Exists(registerFilePath)) - { - continue;//不存在则跳过 - } - - //第三步:检查 Register 文件是否合格 - - var registerContent = File.ReadAllText(registerFilePath); - if (registerContent.Contains("[XncfRegister]") && - registerContent.Contains("IXncfRegister") && - registerContent.Contains("Uid")) - { - selectList.Add( - new SelectionItem( - projectFolder, - projectFolder, /*Path.GetFileName(projectFolder)*/ - projectFolder/*Path.GetDirectoryName(projectFolder)*/)); - } - } - } - - if (mustHaveXncfModule && (currentDir == null || selectList.Count == 0)) - { - selectList.Add( - new SelectionItem( - "N/A", - "没有发现任何可用的 XNCF 项目,请确保你正在一个标准的 NCF 开发环境中!")); - } - - return selectList; - } - - /// - /// 获取 xxxSenparcEntities.cs 数据库文件 - /// - /// - /// 数据库类型 - /// - /// - public static string GetSenparcEntitiesFilePath(string projectPath, string dbType) - { - var databaseModelPath = Path.Combine(projectPath, "Domain", "Models", "DatabaseModel"); - var files = Directory.GetFiles(databaseModelPath, "*SenparcEntities.cs"); - if (files.Length == 0) - { - throw new NcfExceptionBase($"目录 {databaseModelPath} 下没有找到 SenparcEntities.cs 结尾的文件"); - } - - var databaseFile = Path.GetFileName(files[0]).Replace(".cs", ""); - - if (!dbType.IsNullOrEmpty()) - { - databaseFile += "_" + dbType; - } - - return databaseFile; - } - } -} +using Senparc.CO2NET.Extensions; +using Senparc.CO2NET.Helpers; +using Senparc.CO2NET.Trace; +using Senparc.Ncf.Core.AppServices; +using Senparc.Ncf.Core.Exceptions; +using Senparc.Ncf.XncfBase.FunctionRenders; +using Senparc.Ncf.XncfBase.Functions.Parameters; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Ncf.XncfBase.Functions +{ + /// + ///Function helper class + /// + public class FunctionHelper + { + /// + ///log + /// + /// + /// + public static void RecordLog(StringBuilder sb, string msg) + { + sb.AppendLine($"[{SystemTime.Now.ToString()}]\t{msg}"); + } + + /// + /// Get the information list of all parameters + /// + /// + /// FunctionRenderBag + /// Whether to try to load data (the parameter must implement the IFunctionParameterLoadDataBase interface) + /// + public static async Task> GetFunctionParameterInfoAsync(IServiceProvider serviceProvider, FunctionRenderBag functionRenderBag, bool tryLoadData = true) + { + var functionParameterType = functionRenderBag.FunctionParameterType; + + IAppRequest paraObj = null; + + if (ReflectionHelper.HasParameterlessConstructor(functionParameterType)) + { + //Contains a constructor with no parameters + paraObj = functionParameterType.Assembly.CreateInstance(functionParameterType.FullName) as IAppRequest;//TODO: generated through DI + + if (tryLoadData && paraObj is FunctionAppRequestBase functionRequestPara) + { + try + { + await functionRequestPara.LoadData(serviceProvider);//Load original information + } + catch (Exception ex) + { + SenparcTrace.BaseExceptionLog(ex); + throw; + } + } + } + + var props = functionParameterType.GetProperties(); + ParameterType parameterType = ParameterType.Text; + List result = new List(); + foreach (var prop in props) + { + SelectionList selectionList = null; + parameterType = ParameterType.Text;//Defaults to text content + //Determine if option exists + if (prop.PropertyType == typeof(SelectionList)) + { + var selections = prop.GetValue(paraObj, null) as SelectionList; + switch (selections.SelectionType) + { + case SelectionType.DropDownList: + parameterType = ParameterType.DropDownList; + break; + case SelectionType.CheckBoxList: + parameterType = ParameterType.CheckBoxList; + break; + default: + //TODO: throw + break; + } + selectionList = selections; + } + else if (prop.Name.Contains("SECRET", StringComparison.OrdinalIgnoreCase) || prop.GetCustomAttribute() != null) + { + parameterType = ParameterType.Password; + } + + var name = prop.Name; + string title = null; + string description = null; + var isRequired = prop.GetCustomAttribute() != null; + var descriptionAttr = prop.GetCustomAttribute(); + var maxLengthAttr = prop.GetCustomAttribute(); + var maxLength = 0; + if (descriptionAttr != null && descriptionAttr.Description != null) + { + //Split: Name||Description + var descriptionAttrArr = descriptionAttr.Description.Split(new[] { "||" }, StringSplitOptions.RemoveEmptyEntries); + title = descriptionAttrArr[0]; + if (descriptionAttrArr.Length > 1) + { + description = descriptionAttrArr[1]; + } + } + + if (maxLengthAttr != null) + { + maxLength = maxLengthAttr.Length; + } + + var systemType = prop.PropertyType.Name; + + object value = null; + try + { + value = prop.GetValue(paraObj); + } + catch (Exception ex) + { + SenparcTrace.BaseExceptionLog(ex); + } + + var functionParamInfo = new FunctionParameterInfo(name, title, description, isRequired, systemType, parameterType, + selectionList ?? new SelectionList(SelectionType.Unknown), value, maxLength); + result.Add(functionParamInfo); + } + return result; + } + + /// + /// Add the XNCF items in the current solution to the SelectionList object + /// (Scan all domain projects contained in the current solution) + /// + /// Whether the current solution must contain the XNCF project + /// Find the XNCF module root directory. If left null, use System.IO.Directory.GetCurrentDirectory() to obtain it, and find the folder where the .sln is located + /// Additional projects besides standard XNCF + public static List LoadXncfProjects(bool mustHaveXncfModule = false, string rootDir = null, params string[] additionalProjects) + { + var selectList = new List(); + + var currentDir = rootDir ?? System.IO.Directory.GetCurrentDirectory(); + + while (currentDir != null) + { + var slnFile = Directory.GetFiles(currentDir, "*.sln"); + if (slnFile.Length > 0) + { + break; + } + currentDir = Directory.GetParent(currentDir).FullName; + } + + if (currentDir != null) + { + //Found the SLN file and started searching + + //Step 1: Find XNCF + + var projectFolders = Directory.GetDirectories(currentDir, "*.XNCF.*", SearchOption.AllDirectories).ToList(); + + if (additionalProjects != null && additionalProjects.Length > 0) + { + foreach (var proj in additionalProjects) + { + var addProjectFolders = Directory.GetDirectories(currentDir, proj, SearchOption.AllDirectories); + if (addProjectFolders.Count() > 0) + { + projectFolders.AddRange(addProjectFolders); + } + } + } + + foreach (var projectFolder in projectFolders) + { + //Step 2: Check whether the Register file exists + var registerFilePath = Path.Combine(projectFolder, "Register.cs"); + if (!File.Exists(registerFilePath)) + { + continue;//If it does not exist, skip it. + } + + //Step 3: Check whether the Register file is qualified + + var registerContent = File.ReadAllText(registerFilePath); + if (registerContent.Contains("[XncfRegister]") && + registerContent.Contains("IXncfRegister") && + registerContent.Contains("Uid")) + { + selectList.Add( + new SelectionItem( + projectFolder, + projectFolder, /*Path.GetFileName(projectFolder)*/ + projectFolder/*Path.GetDirectoryName(projectFolder)*/)); + } + } + } + + if (mustHaveXncfModule && (currentDir == null || selectList.Count == 0)) + { + selectList.Add( + new SelectionItem( + "N/A", + "没有发现任何可用的 XNCF 项目,请确保你正在一个标准的 NCF 开发环境中!")); + } + + return selectList; + } + + /// + /// Get xxxSenparcEntities.cs database file + /// + /// + /// Database type + /// + /// + public static string GetSenparcEntitiesFilePath(string projectPath, string dbType) + { + var databaseModelPath = Path.Combine(projectPath, "Domain", "Models", "DatabaseModel"); + var files = Directory.GetFiles(databaseModelPath, "*SenparcEntities.cs"); + if (files.Length == 0) + { + throw new NcfExceptionBase($"目录 {databaseModelPath} 下没有找到 SenparcEntities.cs 结尾的文件"); + } + + var databaseFile = Path.GetFileName(files[0]).Replace(".cs", ""); + + if (!dbType.IsNullOrEmpty()) + { + databaseFile += "_" + dbType; + } + + return databaseFile; + } + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Functions/Parameters/FunctionParameterInfo.cs b/src/Basic/Senparc.Ncf.XncfBase/Functions/Parameters/FunctionParameterInfo.cs index 9dc8a7dde..9e61ac0da 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Functions/Parameters/FunctionParameterInfo.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Functions/Parameters/FunctionParameterInfo.cs @@ -1,82 +1,82 @@ -using Senparc.Ncf.XncfBase.Functions; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.XncfBase -{ - /// - /// 参数类型 - /// 注意:请勿更改已经定义的顺序和值! - /// - public enum ParameterType - { - Text = 0, - DropDownList = 1, - CheckBoxList = 2, - Password = 3, - } - - /// - /// FunctionParameter 信息(供输出用) - /// - public class FunctionParameterInfo - { - /// - /// 参数名称 - /// - public string Name { get; set; } - /// - /// 标题(标签内容) - /// - public string Title { get; set; } - /// - /// 备注 - /// - public string Description { get; set; } - /// - /// 是否必须 - /// - public bool IsRequired { get; set; } - /// - /// 系统类型 - /// - public string SystemType { get; set; } - /// - /// 最大长度(一般应用于字符串) - public int MaxLength { get; set; } - - /// - /// 参数类型 - /// - public ParameterType ParameterType { get; set; } = ParameterType.Text; - - /// - /// 文本值(当文本类型时使用) - /// - public object Value { get; set; } - - /// - /// 选项(当出现非文本内容时使用) - /// - public SelectionList SelectionList { get; set; } - - public FunctionParameterInfo() - { - } - - public FunctionParameterInfo(string name, string title, string description, - bool isRequired, string systemType, ParameterType parameterType, SelectionList selectionList, object value, int maxLength) - { - Name = name; - Title = title; - Description = description; - IsRequired = isRequired; - SystemType = systemType; - SelectionList = selectionList; - ParameterType = parameterType; - Value = value; - MaxLength = maxLength; - } - } -} +using Senparc.Ncf.XncfBase.Functions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.XncfBase +{ + /// + /// parameter type + /// Note: Do not change the defined order and values! + /// + public enum ParameterType + { + Text = 0, + DropDownList = 1, + CheckBoxList = 2, + Password = 3, + } + + /// + ///FunctionParameter information (for output) + /// + public class FunctionParameterInfo + { + /// + /// parameter name + /// + public string Name { get; set; } + /// + /// title (tag content) + /// + public string Title { get; set; } + /// + /// Remark + /// + public string Description { get; set; } + /// + /// is it necessary + /// + public bool IsRequired { get; set; } + /// + /// system type + /// + public string SystemType { get; set; } + /// + /// Maximum length (generally applied to strings) + public int MaxLength { get; set; } + + /// + /// parameter type + /// + public ParameterType ParameterType { get; set; } = ParameterType.Text; + + /// + /// Text value (used when text type) + /// + public object Value { get; set; } + + /// + /// option (used when non-text content is present) + /// + public SelectionList SelectionList { get; set; } + + public FunctionParameterInfo() + { + } + + public FunctionParameterInfo(string name, string title, string description, + bool isRequired, string systemType, ParameterType parameterType, SelectionList selectionList, object value, int maxLength) + { + Name = name; + Title = title; + Description = description; + IsRequired = isRequired; + SystemType = systemType; + SelectionList = selectionList; + ParameterType = parameterType; + Value = value; + MaxLength = maxLength; + } + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Functions/Parameters/SelectionList.cs b/src/Basic/Senparc.Ncf.XncfBase/Functions/Parameters/SelectionList.cs index 71c49fa39..9b6499271 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Functions/Parameters/SelectionList.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Functions/Parameters/SelectionList.cs @@ -1,126 +1,126 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Text; - -namespace Senparc.Ncf.XncfBase.Functions -{ - /// - /// 选项列表 - /// - public class SelectionList - { - private string[] selectedValues; - - /// - /// 选项类型 - /// - public SelectionType SelectionType { get; set; } - ///// - ///// 选中的项的值(从客户端传入) - ///// - public string[] SelectedValues { get => selectedValues ?? new string[0]; set => selectedValues = value; } - - /// - /// 选项参数 - /// - public IList Items { get; set; } - - //public SelectionList() { } - - - public SelectionList(SelectionType selectionType) : this(selectionType, null) - { - } - - public SelectionList(SelectionType selectionType, IList items) - { - SelectionType = selectionType; - Items = items ?? new List(); - } - - /// - /// 判断 SelectedValues 中是否存在值 - /// - /// 获取 Items 中的索引项对应的 Value - /// - public bool IsSelected(int itemIndex) - { - if (itemIndex > Items.Count - 1) - { - throw new IndexOutOfRangeException($"{nameof(itemIndex)}({itemIndex}) 超出 {nameof(Items)} 索引值范围({Items.Count})!"); - } - - return IsSelected(Items[itemIndex].Value); - } - - /// - /// 判断 SelectedValues 中是否存在值 - /// - /// - /// - public bool IsSelected(string value) - { - return SelectedValues.Contains(value); - } - } - - /// - /// 选项 - /// - public class SelectionItem - { - /// - /// 文字标签 - /// - public string Text { get; set; } - /// - /// 值 - /// - public string Value { get; set; } - /// - /// (仅供显示时使用),是否默认选中 - /// - public bool DefaultSelected { get; set; } - /// - /// 说明 - /// - public string Note { get; set; } - - public object BindData { get; set; } - - public SelectionItem() { } - - public SelectionItem(string value, string text, string note = "", bool defaultSelected = false) - { - Text = text; - Value = value; - Note = note; - DefaultSelected = defaultSelected; - } - } - - /// - /// 选项集合类型 - /// - public enum SelectionType - { - /// - /// 未知参数 - /// - Unknown, - /// - /// 下拉列表(单选) - /// - DropDownList, - /// - /// 复选框列表(多选) - /// - CheckBoxList, - ///// - ///// 单选列表(单选) - ///// - //RadioButtonList - } -} +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; + +namespace Senparc.Ncf.XncfBase.Functions +{ + /// + ///option list + /// + public class SelectionList + { + private string[] selectedValues; + + /// + /// option type + /// + public SelectionType SelectionType { get; set; } + ///// + ///// The value of the selected item (passed in from the client) + ///// + public string[] SelectedValues { get => selectedValues ?? new string[0]; set => selectedValues = value; } + + /// + /// option parameters + /// + public IList Items { get; set; } + + //public SelectionList() { } + + + public SelectionList(SelectionType selectionType) : this(selectionType, null) + { + } + + public SelectionList(SelectionType selectionType, IList items) + { + SelectionType = selectionType; + Items = items ?? new List(); + } + + /// + /// Determine whether there is a value in SelectedValues + /// + /// Get the Value corresponding to the index item in Items + /// + public bool IsSelected(int itemIndex) + { + if (itemIndex > Items.Count - 1) + { + throw new IndexOutOfRangeException($"{nameof(itemIndex)}({itemIndex}) 超出 {nameof(Items)} 索引值范围({Items.Count})!"); + } + + return IsSelected(Items[itemIndex].Value); + } + + /// + /// Determine whether there is a value in SelectedValues + /// + /// + /// + public bool IsSelected(string value) + { + return SelectedValues.Contains(value); + } + } + + /// + ///options + /// + public class SelectionItem + { + /// + /// text label + /// + public string Text { get; set; } + /// + /// value + /// + public string Value { get; set; } + /// + /// (for display only), whether selected by default + /// + public bool DefaultSelected { get; set; } + /// + /// illustrate + /// + public string Note { get; set; } + + public object BindData { get; set; } + + public SelectionItem() { } + + public SelectionItem(string value, string text, string note = "", bool defaultSelected = false) + { + Text = text; + Value = value; + Note = note; + DefaultSelected = defaultSelected; + } + } + + /// + /// option collection type + /// + public enum SelectionType + { + /// + /// unknown parameters + /// + Unknown, + /// + /// drop-down list (single selection) + /// + DropDownList, + /// + /// Checkbox list (multiple selection) + /// + CheckBoxList, + ///// + ///// Single choice list (single choice) + ///// + //RadioButtonList + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Helpers/StartupHelper.cs b/src/Basic/Senparc.Ncf.XncfBase/Helpers/StartupHelper.cs index 1dd05017a..c987bab8a 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Helpers/StartupHelper.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Helpers/StartupHelper.cs @@ -1,33 +1,33 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Senparc.Ncf.Core.Models; - -namespace Senparc.Ncf.XncfBase.Helpers -{ - public class StartupHelper - { - /// - /// 获取模块版本号 - /// - /// - /// - internal static string GetXncfVersion() - where TXncfDatabaseRegister : class, IXncfDatabase, new() - { - try - { - var register = System.Activator.CreateInstance() as TXncfDatabaseRegister; - if (register is IXncfRegister xncfRegister) - { - return $" / XNCF {xncfRegister.Name} {xncfRegister.Version}"; - } - return "Dev"; - } - catch - { - return "Dev / Not IXncfRegister"; - } - } - } -} +using System; +using System.Collections.Generic; +using System.Text; +using Senparc.Ncf.Core.Models; + +namespace Senparc.Ncf.XncfBase.Helpers +{ + public class StartupHelper + { + /// + /// Get module version number + /// + /// + /// + internal static string GetXncfVersion() + where TXncfDatabaseRegister : class, IXncfDatabase, new() + { + try + { + var register = System.Activator.CreateInstance() as TXncfDatabaseRegister; + if (register is IXncfRegister xncfRegister) + { + return $" / XNCF {xncfRegister.Name} {xncfRegister.Version}"; + } + return "Dev"; + } + catch + { + return "Dev / Not IXncfRegister"; + } + } + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfMiddleware.cs b/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfMiddleware.cs index 797f18581..9162acdca 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfMiddleware.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfMiddleware.cs @@ -1,20 +1,20 @@ -using Microsoft.AspNetCore.Builder; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.XncfBase -{ - /// - /// 中间件接口 - /// - public interface IXncfMiddleware - { - /// - /// 使用中间件 - /// - /// - /// - IApplicationBuilder UseMiddleware(IApplicationBuilder app); - } -} +using Microsoft.AspNetCore.Builder; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.XncfBase +{ + /// + /// middleware interface + /// + public interface IXncfMiddleware + { + /// + /// use middleware + /// + /// + /// + IApplicationBuilder UseMiddleware(IApplicationBuilder app); + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfRazorRuntimeCompilation.cs b/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfRazorRuntimeCompilation.cs index 67e121673..3a26ee29e 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfRazorRuntimeCompilation.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfRazorRuntimeCompilation.cs @@ -1,17 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.XncfBase -{ - /// - /// 需要使用 AddRazorRuntimeCompilation() 方法时,需要设置对应当前项目相对于 Senparc.Web 的路径 - /// - public interface IXncfRazorRuntimeCompilation - { - /// - /// 相对路径,如:Path.GetFullPath(Path.Combine(SiteConfig.WebRootPath, "..", "..", "Senparc.Areas.Admin")); - /// - string LibraryPath { get; } - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.XncfBase +{ + /// + /// When you need to use the AddRazorRuntimeCompilation() method, you need to set the path corresponding to the current project relative to Senparc.Web + /// + public interface IXncfRazorRuntimeCompilation + { + /// + /// Relative path, such as: Path.GetFullPath(Path.Combine(SiteConfig.WebRootPath, "..", "..", "Senparc.Areas.Admin")); + /// + string LibraryPath { get; } + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfRegister.cs b/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfRegister.cs index 89c79e48d..5a9c52ac4 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfRegister.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfRegister.cs @@ -1,144 +1,144 @@ -using AutoMapper; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Senparc.CO2NET.RegisterServices; -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.XncfBase.Database; -using Senparc.Ncf.XncfBase.Threads; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Senparc.Ncf.XncfBase -{ - public interface IXncfRegister - { - /// - /// 是否忽略安装(但不影响执行注册代码) - /// - bool IgnoreInstall { get; } - - /// - /// 是否启用 MCP 服务器(MCP Server) - /// - bool EnableMcpServer { get; } - - /// - /// 模块名称,要求全局唯一 - /// - string Name { get; } - - /// - /// 编号,要求全局唯一 - /// - string Uid { get; } - - /// - /// 版本号 - /// - string Version { get; } - - /// - /// 菜单名称 - /// - string MenuName { get; } - - /// - /// Icon图标 - /// - string Icon { get; } - - /// - /// 说明 - /// - string Description { get; } - - ///// - ///// 注册方法,注册的顺序决定了界面中排列的顺序 - ///// - //IList Functions { get; } - - /// - /// 添加 AutoMap 映射 - /// - ConcurrentBag> AutoMapMappingConfigs { get; set; } - - /// - /// 获取当前模块的已注册线程信息 - /// - IEnumerable> RegisteredThreadInfo { get; } - - /// - /// 安装代码 - /// - Task InstallOrUpdateAsync(IServiceProvider serviceProvider, InstallOrUpdate installOrUpdate); - - /// - /// 卸载代码 - /// - Task UninstallAsync(IServiceProvider serviceProvider, Func unsinstallFunc); - - /// - /// 获取首页Url - /// 仅限实现了 IAreaRegister 接口之后的 Register,否则将返回 null - /// - /// - string GetAreaHomeUrl(); - - /// - /// 获取 Area 其他页面的 URL - /// - /// URL 路径(不带 uid 参数) - /// - string GetAreaUrl(string path); - - /// - /// 在 ConfigureServices 启动时注册当前模块 - /// - /// IServiceCollection - /// Configuration - /// - /// - IServiceCollection AddXncfModule(IServiceCollection services, IConfiguration configuration, IHostEnvironment env); - - /// - /// 添加AutoMap的映射关系 - /// - /// - void AddAutoMapMapping(Action mapping); - - /// - /// 执行 AutoMapper 映射 - /// - void OnAutoMapMapping(IServiceCollection services, IConfiguration configuration); - - /// - /// 在 startup.cs 的 Configure() 方法中执行配置 - /// - /// - /// CO2NET 注册对象 - /// - IApplicationBuilder UseXncfModule(IApplicationBuilder app, IRegisterService registerService); - - #region MCP Server - - /// - /// 添加 MCP 服务器 - /// - /// - void AddMcpServer(IServiceCollection services, IXncfRegister xncfRegister); - /// - /// 使用 MCP 服务器 - /// - /// - /// - void UseMcpServer(IApplicationBuilder app, IRegisterService registerService); - - #endregion - } -} +using AutoMapper; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Senparc.CO2NET.RegisterServices; +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.XncfBase.Database; +using Senparc.Ncf.XncfBase.Threads; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Senparc.Ncf.XncfBase +{ + public interface IXncfRegister + { + /// + /// Whether to ignore installation (but does not affect the execution of registration code) + /// + bool IgnoreInstall { get; } + + /// + /// Whether to enable MCP server (MCP Server) + /// + bool EnableMcpServer { get; } + + /// + ///Module name, required to be globally unique + /// + string Name { get; } + + /// + /// Number, required to be globally unique + /// + string Uid { get; } + + /// + /// version number + /// + string Version { get; } + + /// + ///menu name + /// + string MenuName { get; } + + /// + ///icon + /// + string Icon { get; } + + /// + /// illustrate + /// + string Description { get; } + + ///// + ///// Registration method, the order of registration determines the order of arrangement in the interface + ///// + //IList Functions { get; } + + /// + ///Add AutoMap mapping + /// + ConcurrentBag> AutoMapMappingConfigs { get; set; } + + /// + /// Get the registered thread information of the current module + /// + IEnumerable> RegisteredThreadInfo { get; } + + /// + ///Installation code + /// + Task InstallOrUpdateAsync(IServiceProvider serviceProvider, InstallOrUpdate installOrUpdate); + + /// + ///uninstall code + /// + Task UninstallAsync(IServiceProvider serviceProvider, Func unsinstallFunc); + + /// + /// Get homepage Url + /// Only Register after implementing the IAreaRegister interface, otherwise null will be returned + /// + /// + string GetAreaHomeUrl(); + + /// + /// Get the URLs of other pages in Area + /// + /// URL path (without uid parameter) + /// + string GetAreaUrl(string path); + + /// + /// Register the current module when ConfigureServices starts + /// + /// IServiceCollection + /// Configuration + /// + /// + IServiceCollection AddXncfModule(IServiceCollection services, IConfiguration configuration, IHostEnvironment env); + + /// + /// Add AutoMap mapping relationship + /// + /// + void AddAutoMapMapping(Action mapping); + + /// + ///Execute AutoMapper mapping + /// + void OnAutoMapMapping(IServiceCollection services, IConfiguration configuration); + + /// + /// Perform configuration in the Configure() method of startup.cs + /// + /// + /// CO2NET registration object + /// + IApplicationBuilder UseXncfModule(IApplicationBuilder app, IRegisterService registerService); + + #region MCP Server + + /// + ///Add MCP server + /// + /// + void AddMcpServer(IServiceCollection services, IXncfRegister xncfRegister); + /// + /// use MCP server + /// + /// + /// + void UseMcpServer(IApplicationBuilder app, IRegisterService registerService); + + #endregion + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfThread.cs b/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfThread.cs index 9334a0778..eabe389e2 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfThread.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Interfaces/IXncfThread.cs @@ -1,19 +1,19 @@ -using Senparc.Ncf.XncfBase.Threads; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.XncfBase -{ - /// - /// XNCF 线程模块接口 - /// - public interface IXncfThread - { - /// - /// 线程配置 - /// - /// - void ThreadConfig(XncfThreadBuilder xncfThreadBuilder); - } -} +using Senparc.Ncf.XncfBase.Threads; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.XncfBase +{ + /// + ///XNCF thread module interface + /// + public interface IXncfThread + { + /// + ///thread configuration + /// + /// + void ThreadConfig(XncfThreadBuilder xncfThreadBuilder); + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Threads/ThreadInfo.cs b/src/Basic/Senparc.Ncf.XncfBase/Threads/ThreadInfo.cs index fe6d3e1ec..1728f09bb 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Threads/ThreadInfo.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Threads/ThreadInfo.cs @@ -1,70 +1,70 @@ -using Microsoft.AspNetCore.Builder; -using Senparc.CO2NET.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Ncf.XncfBase.Threads -{ - /// - /// ThreadInfo - /// - public class ThreadInfo - { - /// - /// 用于识别 Thread,请确保单个 XNCF 模块中唯一 - /// - public string Name { get; set; } - /// - /// 间隔时间 - /// - public TimeSpan IntervalTime { get; set; } - /// - /// 执行任务 - /// - public Func Task { get; set; } - /// - /// 发生异常时的处理 - /// - public Func ExceptionHandler { get; set; } - /// - /// 最后故事记录 - /// - private List Stories { get; set; } = new List(); - - /// - /// 获取故事 HTML代码 - /// - /// - public string StoryHtml => string.Join("

", Stories.Select(z => z.HtmlEncode()).ToArray()); - - public ThreadInfo(string name, TimeSpan intervalTime, Func task, Func exceptionHandler = null) - { - Name = name; - IntervalTime = intervalTime; - Task = task; - ExceptionHandler = exceptionHandler; - } - - /// - /// 记录故事 - /// - /// - /// - public string RecordStory(string msg) - { - while (Stories.Count > 10) - { - Stories.RemoveAt(0); - } - var story = $@"{SystemTime.Now.ToString()} -{msg}"; - Stories.Add(story); - return story; - } - - - } -} +using Microsoft.AspNetCore.Builder; +using Senparc.CO2NET.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Ncf.XncfBase.Threads +{ + /// + /// ThreadInfo + /// + public class ThreadInfo + { + /// + /// is used to identify Thread, please ensure it is unique in a single XNCF module + /// + public string Name { get; set; } + /// + ///interval time + /// + public TimeSpan IntervalTime { get; set; } + /// + /// perform tasks + /// + public Func Task { get; set; } + /// + /// Handling when an exception occurs + /// + public Func ExceptionHandler { get; set; } + /// + ///Last story record + /// + private List Stories { get; set; } = new List(); + + /// + /// Get story HTML code + /// + /// + public string StoryHtml => string.Join("

", Stories.Select(z => z.HtmlEncode()).ToArray()); + + public ThreadInfo(string name, TimeSpan intervalTime, Func task, Func exceptionHandler = null) + { + Name = name; + IntervalTime = intervalTime; + Task = task; + ExceptionHandler = exceptionHandler; + } + + /// + ///record story + /// + /// + /// + public string RecordStory(string msg) + { + while (Stories.Count > 10) + { + Stories.RemoveAt(0); + } + var story = $@"{SystemTime.Now.ToString()} +{msg}"; + Stories.Add(story); + return story; + } + + + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/Threads/XncfThreadBuilder.cs b/src/Basic/Senparc.Ncf.XncfBase/Threads/XncfThreadBuilder.cs index 4b2560db7..51f3e6c1e 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/Threads/XncfThreadBuilder.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/Threads/XncfThreadBuilder.cs @@ -1,85 +1,85 @@ -using Microsoft.AspNetCore.Builder; -using Senparc.CO2NET.Trace; -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Senparc.Ncf.XncfBase.Threads -{ - //TODO: 线程备份及后台操作需要考虑租户问题 - - /// - /// XNCF Thread 模块,线程配置 - /// - public class XncfThreadBuilder - { - private List _threadInfoList = new List(); - public void AddThreadInfo(ThreadInfo threadInfo) - { - _threadInfoList.Add(threadInfo); - } - - internal void Build(IApplicationBuilder app, IXncfRegister register) - { - var threadRegister = register as IXncfThread; - if (threadRegister == null) - { - return; - } - - //return;//TODO:多租户完成之前暂时不启用后台线程,需要解决线程和租户的对应关系 - - var i = 0; - //遍历单个 XNCF 内所有线程配置 - foreach (var threadInfo in _threadInfoList) - { - if (threadInfo.Task == null) - { - continue; - } - try - { - i++; - //定义线程 - Thread thread = new Thread(async () => - { - SenparcTrace.SendCustomLog("启动线程", $"{register.Name}-{threadInfo.Name}"); - await Task.Delay(TimeSpan.FromSeconds(i)); - while (true) - { - try - { - await threadInfo.Task.Invoke(app, threadInfo); - // 建议开发者自己在内部做好线程内的异常处理 - } - catch (Exception ex) - { - if (threadInfo.ExceptionHandler != null) - { - await threadInfo.ExceptionHandler.Invoke(ex); - } - else - { - SenparcTrace.BaseExceptionLog(ex); - } - } - finally { - //进行延迟 - await Task.Delay(threadInfo.IntervalTime); - } - } - }); - thread.Name = $"{register.Uid}-{threadInfo.Name ?? Guid.NewGuid().ToString()}"; - thread.Start();//启动 - Register.ThreadCollection[threadInfo] = thread; - } - catch (Exception ex) - { - SenparcTrace.BaseExceptionLog(ex); - } - } - } - } -} +using Microsoft.AspNetCore.Builder; +using Senparc.CO2NET.Trace; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Senparc.Ncf.XncfBase.Threads +{ + //TODO: Thread backup and background operations need to consider tenant issues + + /// + ///XNCF Thread module, thread configuration + /// + public class XncfThreadBuilder + { + private List _threadInfoList = new List(); + public void AddThreadInfo(ThreadInfo threadInfo) + { + _threadInfoList.Add(threadInfo); + } + + internal void Build(IApplicationBuilder app, IXncfRegister register) + { + var threadRegister = register as IXncfThread; + if (threadRegister == null) + { + return; + } + + //return;//TODO: Background threads will not be enabled until multi-tenancy is completed. The corresponding relationship between threads and tenants needs to be resolved. + + var i = 0; + //Traverse all thread configurations within a single XNCF + foreach (var threadInfo in _threadInfoList) + { + if (threadInfo.Task == null) + { + continue; + } + try + { + i++; + //Define thread + Thread thread = new Thread(async () => + { + SenparcTrace.SendCustomLog("启动线程", $"{register.Name}-{threadInfo.Name}"); + await Task.Delay(TimeSpan.FromSeconds(i)); + while (true) + { + try + { + await threadInfo.Task.Invoke(app, threadInfo); + // It is recommended that developers handle exceptions within threads internally + } + catch (Exception ex) + { + if (threadInfo.ExceptionHandler != null) + { + await threadInfo.ExceptionHandler.Invoke(ex); + } + else + { + SenparcTrace.BaseExceptionLog(ex); + } + } + finally { + //delay + await Task.Delay(threadInfo.IntervalTime); + } + } + }); + thread.Name = $"{register.Uid}-{threadInfo.Name ?? Guid.NewGuid().ToString()}"; + thread.Start();//start up + Register.ThreadCollection[threadInfo] = thread; + } + catch (Exception ex) + { + SenparcTrace.BaseExceptionLog(ex); + } + } + } + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/VersionManager/VersionHelper.cs b/src/Basic/Senparc.Ncf.XncfBase/VersionManager/VersionHelper.cs index 986b0053c..959a0308c 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/VersionManager/VersionHelper.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/VersionManager/VersionHelper.cs @@ -1,176 +1,176 @@ -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis; -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.RegularExpressions; -using System.Linq; -using System.Net.WebSockets; -using Senparc.Ncf.Core.Exceptions; - -namespace Senparc.Ncf.XncfBase.VersionManager -{ - /* GPT-4 Prompt: - 1、请为我编写一个能够识别软件版本号的正则表达式 - 2、创建一个独立的类:VersionInfo,用于储存版本信息 - 3、为这个正则表达式编写一个尽量涵盖各种版本情况的完整的单元测试(使用 .NET 自带的单元测试)。 - */ - - public static class VersionHelper - { - //这个正则表达式匹配遵循语义化版本规范(semver)的版本号。它包括主版本号、次版本号、修订号、可选的构建号、可选的预发布版本标签和可选的元数据标签。 - //private const string VersionRegex = @"^(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?(?:-([a-zA-Z\d\-]+))?(?:\+([a-zA-Z\d\-]+))?$"; - //private const string VersionRegex = @"^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z\d\-]+))?(?:\.(\d+))?(?:\+([a-zA-Z\d\-]+))?$"; - //private const string VersionRegex = @"^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z\d\-]+(\.\d+)?))?(?:\+([a-zA-Z\d\-]+))?$"; - //private const string VersionRegex = @"^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z\d\-]+(\.\d+)?))?(?:\+([a-zA-Z\d\-.]+))?$"; - //private const string VersionRegex = @"^(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?(?:-([a-zA-Z\d\-]+(\.\d+)?))?(?:\+([a-zA-Z\d\-.]+))?$"; - private const string VersionRegex = @"^(\d+)\.(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([a-zA-Z\d\-]+(\.\d+)?))?(?:\+([a-zA-Z\d\-.]+))?$"; - - - //private const string VersionInCodeRegex = @"Version\s*=>\s*""(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?(?:-([a-zA-Z\d\-]+(\.\d+)?))?(?:\+([a-zA-Z\d\-.]+))?"""; - private const string VersionInCodeRegex = @"Version\s*=>\s*""(\d+)\.(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([a-zA-Z\d\-]+(\.\d+)?))?(?:\+([a-zA-Z\d\-.]+))?"""; - - /// - /// 解析版本字符串并返回一个 VersionInfo 对象。 - /// - /// 要解析的版本字符串。 - /// 表示解析后的版本信息的 VersionInfo 对象。 - public static VersionInfo Parse(string versionString) - { - var regex = new Regex(VersionRegex); - var match = regex.Match(versionString); - - if (!match.Success) - { - throw new ArgumentException("无效的版本字符串格式。", nameof(versionString)); - } - - var versionInfo = new VersionInfo - { - Major = int.Parse(match.Groups[1].Value), - Minor = int.Parse(match.Groups[2].Value), - Patch = match.Groups[3].Success ? int.Parse(match.Groups[3].Value) : 0, // 如果不存在 Patch,则使用默认值 0 - Build = match.Groups[4].Success ? int.Parse(match.Groups[4].Value) : (int?)null, - PreRelease = match.Groups[5].Success ? match.Groups[5].Value : null, - Metadata = match.Groups[7].Success ? match.Groups[7].Value : null - }; - - return versionInfo; - } - - - /// - /// 从 Register.cs 代码中获取版本号 - /// - /// - /// - /// - public static (VersionInfo VersionInfo, string RawVersionString) ParseFromCode(string code) - { - var regex = new Regex(VersionInCodeRegex); - var match = regex.Match(code); - - if (!match.Success) - { - throw new ArgumentException("无法从代码中找到有效的版本字符串。", nameof(code)); - } - - var major = int.Parse(match.Groups[1].Value); - var minor = int.Parse(match.Groups[2].Value); - var patch = match.Groups[3].Success ? int.Parse(match.Groups[3].Value) : 0; // 如果不存在 Patch,则使用默认值 0 - - var versionString = $"{major}.{minor}.{patch}"; - - if (match.Groups[4].Success) - { - versionString += $".{match.Groups[4].Value}"; - } - - if (match.Groups[5].Success) - { - versionString += $"-{match.Groups[5].Value}"; - } - - if (match.Groups[7].Success) - { - versionString += $"+{match.Groups[7].Value}"; - } - - return (VersionInfo: Parse(versionString), RawVersionString: match.Value); - } - - - /// - /// 替换版本号 - /// - /// - /// 原始版本定位字符串,如:Version => "0.1.1" - /// - /// - public static string ReplaceVersionInCode(string code, string rawVersionString, VersionInfo newVersionInfo) - { - var newVersionString = newVersionInfo.ToString(); - - var regex = new Regex(Regex.Escape(rawVersionString)); - var replacedCode = regex.Replace(code, $"Version => \"{newVersionString}\""); - - return replacedCode; - } - - - public static string UpdateVersionInCodeWithRoslyn(string fileContent, UpdateVersionType updateType) - { - // 使用 Roslyn 解析 C# 代码 - SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(fileContent); - CompilationUnitSyntax root = syntaxTree.GetCompilationUnitRoot(); - - // 查找 Version 属性所在的节点 - PropertyDeclarationSyntax versionProperty = root.DescendantNodes() - .OfType() - .FirstOrDefault(p => p.Identifier.Text == "Version"); - - if (versionProperty != null) - { - //获取旧的版本号 - var oldVersionString = versionProperty.ExpressionBody.Expression.ToString().Replace("\"", ""); - Console.WriteLine("oldVersionString:" + oldVersionString); - var oldVersion = Parse(oldVersionString); - - // 如果找到了 Version 属性,修改其值 - var newVersion = new VersionInfo(); - - switch (updateType) - { - case UpdateVersionType.NoUpdate: - break; - case UpdateVersionType.MajorUpdate: - newVersion = oldVersion with { Major = oldVersion.Major + 1 }; - break; - case UpdateVersionType.MinorUpdate: - newVersion = oldVersion with { Minor = oldVersion.Minor + 1 }; - break; - case UpdateVersionType.PatchUpdate: - newVersion = oldVersion with { Patch = oldVersion.Patch + 1 }; - break; - default: - throw new NcfExceptionBase("无法识别的版本更新类型"); - - } - - ExpressionSyntax newVersionExpression = SyntaxFactory.ParseExpression($"\"{newVersion}\""); - ArrowExpressionClauseSyntax newExpressionBody = SyntaxFactory.ArrowExpressionClause(newVersionExpression); - PropertyDeclarationSyntax newVersionProperty = versionProperty.WithExpressionBody(newExpressionBody); - - // 将修改后的语法树规范化并转换为字符串,并写回到原始的 .cs 文件中 - SyntaxNode newRoot = root.ReplaceNode(versionProperty, newVersionProperty); - string newContent = newRoot - //.NormalizeWhitespace() - .ToFullString(); - - return newContent; - } - return null; - } - } -} +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; +using System.Linq; +using System.Net.WebSockets; +using Senparc.Ncf.Core.Exceptions; + +namespace Senparc.Ncf.XncfBase.VersionManager +{ + /* GPT-4 Prompt: + 1. Please write me a regular expression that can identify the software version number. + 2. Create an independent class: VersionInfo, used to store version information + 3. Write a complete unit test for this regular expression that covers various versions as much as possible (use the unit test that comes with .NET). + */ + + public static class VersionHelper + { + //This regular expression matches version numbers that follow the semantic versioning specification (semver). It includes a major version number, a minor version number, a revision number, an optional build number, an optional pre-release tag, and an optional metadata tag. + //private const string VersionRegex = @"^(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?(?:-([a-zA-Z\d\-]+))?(?:\+([a-zA-Z\d\-]+))?$"; + //private const string VersionRegex = @"^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z\d\-]+))?(?:\.(\d+))?(?:\+([a-zA-Z\d\-]+))?$"; + //private const string VersionRegex = @"^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z\d\-]+(\.\d+)?))?(?:\+([a-zA-Z\d\-]+))?$"; + //private const string VersionRegex = @"^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z\d\-]+(\.\d+)?))?(?:\+([a-zA-Z\d\-.]+))?$"; + //private const string VersionRegex = @"^(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?(?:-([a-zA-Z\d\-]+(\.\d+)?))?(?:\+([a-zA-Z\d\-.]+))?$"; + private const string VersionRegex = @"^(\d+)\.(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([a-zA-Z\d\-]+(\.\d+)?))?(?:\+([a-zA-Z\d\-.]+))?$"; + + + //private const string VersionInCodeRegex = @"Version\s*=>\s*""(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?(?:-([a-zA-Z\d\-]+(\.\d+)?))?(?:\+([a-zA-Z\d\-.]+))?"""; + private const string VersionInCodeRegex = @"Version\s*=>\s*""(\d+)\.(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([a-zA-Z\d\-]+(\.\d+)?))?(?:\+([a-zA-Z\d\-.]+))?"""; + + /// + /// Parses the version string and returns a VersionInfo object. + /// + /// The version string to parse. + /// A VersionInfo object representing the parsed version information. + public static VersionInfo Parse(string versionString) + { + var regex = new Regex(VersionRegex); + var match = regex.Match(versionString); + + if (!match.Success) + { + throw new ArgumentException("无效的版本字符串格式。", nameof(versionString)); + } + + var versionInfo = new VersionInfo + { + Major = int.Parse(match.Groups[1].Value), + Minor = int.Parse(match.Groups[2].Value), + Patch = match.Groups[3].Success ? int.Parse(match.Groups[3].Value) : 0, // If no patch exists, the default value 0 is used + Build = match.Groups[4].Success ? int.Parse(match.Groups[4].Value) : (int?)null, + PreRelease = match.Groups[5].Success ? match.Groups[5].Value : null, + Metadata = match.Groups[7].Success ? match.Groups[7].Value : null + }; + + return versionInfo; + } + + + /// + /// Get the version number from the Register.cs code + /// + /// + /// + /// + public static (VersionInfo VersionInfo, string RawVersionString) ParseFromCode(string code) + { + var regex = new Regex(VersionInCodeRegex); + var match = regex.Match(code); + + if (!match.Success) + { + throw new ArgumentException("无法从代码中找到有效的版本字符串。", nameof(code)); + } + + var major = int.Parse(match.Groups[1].Value); + var minor = int.Parse(match.Groups[2].Value); + var patch = match.Groups[3].Success ? int.Parse(match.Groups[3].Value) : 0; // If no patch exists, the default value 0 is used + + var versionString = $"{major}.{minor}.{patch}"; + + if (match.Groups[4].Success) + { + versionString += $".{match.Groups[4].Value}"; + } + + if (match.Groups[5].Success) + { + versionString += $"-{match.Groups[5].Value}"; + } + + if (match.Groups[7].Success) + { + versionString += $"+{match.Groups[7].Value}"; + } + + return (VersionInfo: Parse(versionString), RawVersionString: match.Value); + } + + + /// + /// Replace version number + /// + /// + /// Original version positioning string, such as: Version => "0.1.1" + /// + /// + public static string ReplaceVersionInCode(string code, string rawVersionString, VersionInfo newVersionInfo) + { + var newVersionString = newVersionInfo.ToString(); + + var regex = new Regex(Regex.Escape(rawVersionString)); + var replacedCode = regex.Replace(code, $"Version => \"{newVersionString}\""); + + return replacedCode; + } + + + public static string UpdateVersionInCodeWithRoslyn(string fileContent, UpdateVersionType updateType) + { + // Parse C# code with Roslyn + SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(fileContent); + CompilationUnitSyntax root = syntaxTree.GetCompilationUnitRoot(); + + // Find the node where the Version property is located + PropertyDeclarationSyntax versionProperty = root.DescendantNodes() + .OfType() + .FirstOrDefault(p => p.Identifier.Text == "Version"); + + if (versionProperty != null) + { + //Get old version number + var oldVersionString = versionProperty.ExpressionBody.Expression.ToString().Replace("\"", ""); + Console.WriteLine("oldVersionString:" + oldVersionString); + var oldVersion = Parse(oldVersionString); + + // If the Version property is found, modify its value + var newVersion = new VersionInfo(); + + switch (updateType) + { + case UpdateVersionType.NoUpdate: + break; + case UpdateVersionType.MajorUpdate: + newVersion = oldVersion with { Major = oldVersion.Major + 1 }; + break; + case UpdateVersionType.MinorUpdate: + newVersion = oldVersion with { Minor = oldVersion.Minor + 1 }; + break; + case UpdateVersionType.PatchUpdate: + newVersion = oldVersion with { Patch = oldVersion.Patch + 1 }; + break; + default: + throw new NcfExceptionBase("无法识别的版本更新类型"); + + } + + ExpressionSyntax newVersionExpression = SyntaxFactory.ParseExpression($"\"{newVersion}\""); + ArrowExpressionClauseSyntax newExpressionBody = SyntaxFactory.ArrowExpressionClause(newVersionExpression); + PropertyDeclarationSyntax newVersionProperty = versionProperty.WithExpressionBody(newExpressionBody); + + // Normalize and convert the modified syntax tree to a string and write it back to the original .cs file + SyntaxNode newRoot = root.ReplaceNode(versionProperty, newVersionProperty); + string newContent = newRoot + //.NormalizeWhitespace() + .ToFullString(); + + return newContent; + } + return null; + } + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/VersionManager/VersionInfo.cs b/src/Basic/Senparc.Ncf.XncfBase/VersionManager/VersionInfo.cs index 6f92ca6e8..a0a69c49e 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/VersionManager/VersionInfo.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/VersionManager/VersionInfo.cs @@ -1,75 +1,75 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Senparc.Ncf.XncfBase.VersionManager -{ - /// - /// 软件版本信息 - /// - public record class VersionInfo - { - /// - /// 主版本 - /// - public int Major { get; set; } - - /// - /// 次版本 - /// - public int Minor { get; set; } - - /// - /// 修订版本 - /// - public int Patch { get; set; } - - /// - /// 可选的构建版本 - /// - public int? Build { get; set; } - - /// - /// 可选的预发布版本标签 - /// - public string PreRelease { get; set; } - - /// - /// 可选的元数据标签 - /// - public string Metadata { get; set; } - - - /// - /// 将 VersionInfo 对象转换为版本字符串。 - /// - /// 表示版本信息的字符串。 - /// - /// 将 VersionInfo 对象转换为版本字符串。 - /// - /// 表示版本信息的字符串。 - public override string ToString() - { - var versionString = $"{Major}.{Minor}.{Patch}"; - - // 如果存在 Build 属性,则将其添加到版本字符串中 - if (Build.HasValue) - { - versionString += $".{Build.Value}"; - } - - if (!string.IsNullOrEmpty(PreRelease)) - { - versionString += $"-{PreRelease}"; - } - - if (!string.IsNullOrEmpty(Metadata)) - { - versionString += $"+{Metadata}"; - } - - return versionString; - } - - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace Senparc.Ncf.XncfBase.VersionManager +{ + /// + ///Software version information + /// + public record class VersionInfo + { + /// + ///major version + /// + public int Major { get; set; } + + /// + ///minor version + /// + public int Minor { get; set; } + + /// + ///revision + /// + public int Patch { get; set; } + + /// + /// optional build version + /// + public int? Build { get; set; } + + /// + /// optional pre-release tag + /// + public string PreRelease { get; set; } + + /// + /// Optional metadata tag + /// + public string Metadata { get; set; } + + + /// + /// Convert a VersionInfo object to a version string. + /// + ///A string representing version information. + /// + /// Convert a VersionInfo object to a version string. + /// + ///A string representing version information. + public override string ToString() + { + var versionString = $"{Major}.{Minor}.{Patch}"; + + // If the Build attribute is present, add it to the version string + if (Build.HasValue) + { + versionString += $".{Build.Value}"; + } + + if (!string.IsNullOrEmpty(PreRelease)) + { + versionString += $"-{PreRelease}"; + } + + if (!string.IsNullOrEmpty(Metadata)) + { + versionString += $"+{Metadata}"; + } + + return versionString; + } + + } +} diff --git a/src/Basic/Senparc.Ncf.XncfBase/XncfRegisterManager.cs b/src/Basic/Senparc.Ncf.XncfBase/XncfRegisterManager.cs index b44c059b3..2a1a0c3b8 100644 --- a/src/Basic/Senparc.Ncf.XncfBase/XncfRegisterManager.cs +++ b/src/Basic/Senparc.Ncf.XncfBase/XncfRegisterManager.cs @@ -1,95 +1,95 @@ -using Senparc.Ncf.Core.Cache; -using Senparc.Ncf.Core.Models; -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using System.Linq; -using Senparc.Ncf.XncfBase.MCP; - -namespace Senparc.Ncf.XncfBase -{ - /// - /// XncfRegister 管理器 - /// - public class XncfRegisterManager - { - #region 静态方法 - - /// - /// 模块和方法集合。模块已经按照 Order 倒叙排序(优先级从高到低) - /// - public static List RegisterList { get; set; } = new List(); - - /// - /// 带有数据库的模块 TODO:可放置到缓存中 / TODO:可移除 - /// - public static List XncfDatabaseList => RegisterList.Where(z => z is IXncfDatabase).Select(z => z as IXncfDatabase).ToList(); - - public static McpServerInfoCollection McpServerInfoCollection =new McpServerInfoCollection(); - - /// - /// 判断指定名称的模块是否已注册 - /// - /// - /// - public static bool IsRegistered(string name) => RegisterList.Exists(z => z.Name == name); - - /// - /// 判断指定名称的模块是否已注册(推荐) - /// - /// XncfRegister - /// - public static bool IsRegistered(IXncfRegister xncfRegister) => RegisterList.Exists(z => z.Uid == xncfRegister.Uid); - - - #endregion - - private readonly IServiceProvider _serviceProvider; - - public XncfRegisterManager(IServiceProvider serviceProvider) - { - this._serviceProvider = serviceProvider; - } - - /// - /// 检查数据库(缓存)中的模块信息,是否开放,并为开放状态 - /// - /// - /// - private async Task CheckOpenStateAsync(string xncfName) - { - //检查数据库中的安装状态 - var fullXncfModuleCache = this._serviceProvider.GetService(); - var fullXncfModule = await fullXncfModuleCache.GetObjectAsync(xncfName); - return fullXncfModule != null && fullXncfModule.State == Core.Enums.XncfModules_State.开放; - } - - /// - /// 检查 XNCF 模块是否可用(推荐) - /// - /// - /// - public async Task CheckXncfAvailable(IXncfRegister xncfRegister) - { - //检查内存中是否存在 - return xncfRegister!=null && IsRegistered(xncfRegister) - ? await CheckOpenStateAsync(xncfRegister.Name).ConfigureAwait(false) - : false; - } - - /// - /// 检查 XNCF 模块是否可用 - /// - /// - /// - public async Task CheckXncfAvailable(string xncfName) - { - //检查内存中是否存在 - return IsRegistered(xncfName) - ? await CheckOpenStateAsync(xncfName).ConfigureAwait(false) - : false; - } - } -} +using Senparc.Ncf.Core.Cache; +using Senparc.Ncf.Core.Models; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using System.Linq; +using Senparc.Ncf.XncfBase.MCP; + +namespace Senparc.Ncf.XncfBase +{ + /// + ///XncfRegister Manager + /// + public class XncfRegisterManager + { + #region 静态方法 + + /// + /// Collection of modules and methods. Modules have been sorted backwards according to Order (priority from high to low) + /// + public static List RegisterList { get; set; } = new List(); + + /// + /// Module with database TODO: can be placed in cache / TODO: can be removed + /// + public static List XncfDatabaseList => RegisterList.Where(z => z is IXncfDatabase).Select(z => z as IXncfDatabase).ToList(); + + public static McpServerInfoCollection McpServerInfoCollection =new McpServerInfoCollection(); + + /// + /// Determine whether the module with the specified name has been registered + /// + /// + /// + public static bool IsRegistered(string name) => RegisterList.Exists(z => z.Name == name); + + /// + /// Determine whether the module with the specified name has been registered (recommended) + /// + /// XncfRegister + /// + public static bool IsRegistered(IXncfRegister xncfRegister) => RegisterList.Exists(z => z.Uid == xncfRegister.Uid); + + + #endregion + + private readonly IServiceProvider _serviceProvider; + + public XncfRegisterManager(IServiceProvider serviceProvider) + { + this._serviceProvider = serviceProvider; + } + + /// + /// Check the module information in the database (cache) to see if it is open and in the open state + /// + /// + /// + private async Task CheckOpenStateAsync(string xncfName) + { + //Check installation status in database + var fullXncfModuleCache = this._serviceProvider.GetService(); + var fullXncfModule = await fullXncfModuleCache.GetObjectAsync(xncfName); + return fullXncfModule != null && fullXncfModule.State == Core.Enums.XncfModules_State.开放; + } + + /// + /// Check if XNCF module is available (recommended) + /// + /// + /// + public async Task CheckXncfAvailable(IXncfRegister xncfRegister) + { + //Check if it exists in memory + return xncfRegister!=null && IsRegistered(xncfRegister) + ? await CheckOpenStateAsync(xncfRegister.Name).ConfigureAwait(false) + : false; + } + + /// + /// Check if XNCF module is available + /// + /// + /// + public async Task CheckXncfAvailable(string xncfName) + { + //Check if it exists in memory + return IsRegistered(xncfName) + ? await CheckOpenStateAsync(xncfName).ConfigureAwait(false) + : false; + } + } +} diff --git a/src/Basic/Unpublished/Senparc.Ncf.FileExtension/FileExtension.cs b/src/Basic/Unpublished/Senparc.Ncf.FileExtension/FileExtension.cs index aae101ff3..bc116df2c 100644 --- a/src/Basic/Unpublished/Senparc.Ncf.FileExtension/FileExtension.cs +++ b/src/Basic/Unpublished/Senparc.Ncf.FileExtension/FileExtension.cs @@ -1,64 +1,64 @@ -using Microsoft.AspNetCore.Http; -using System; -using System.IO; -using System.Threading.Tasks; - -namespace Senparc.Ncf.FileExtension -{ - public static class FileExtension - { - /// - /// - /// - /// - /// 绝对路径 - /// - public static async Task Upload(IFormFile formFile, string outPath) - { - if (formFile == null || formFile.Length <= 0) - { - throw new NullReferenceException("IFormFile is null"); - } - // full path to file in temp location - var filePath = Path.GetDirectoryName(outPath); - if (!System.IO.File.Exists(filePath)) - { - Directory.CreateDirectory(filePath); - } - - using (var stream = new FileStream(outPath, FileMode.Create)) - { - await formFile.CopyToAsync(stream); - stream.Flush(); - } - return true; - } - - /// - /// - /// - /// - /// - /// - /// - public static async Task Upload(IFormFile formFile, string fileName, string paths) - { - if (formFile == null || formFile.Length <= 0) - { - throw new NullReferenceException("IFormFile is null"); - } - // full path to file in temp location - if (!System.IO.File.Exists(paths)) - { - Directory.CreateDirectory(paths); - } - var filePath = Path.Combine(paths, fileName); - using (var stream = new FileStream(filePath, FileMode.Create)) - { - await formFile.CopyToAsync(stream); - stream.Flush(); - } - return true; - } - } -} +using Microsoft.AspNetCore.Http; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace Senparc.Ncf.FileExtension +{ + public static class FileExtension + { + /// + /// + /// + /// + /// absolute path + /// + public static async Task Upload(IFormFile formFile, string outPath) + { + if (formFile == null || formFile.Length <= 0) + { + throw new NullReferenceException("IFormFile is null"); + } + // full path to file in temp location + var filePath = Path.GetDirectoryName(outPath); + if (!System.IO.File.Exists(filePath)) + { + Directory.CreateDirectory(filePath); + } + + using (var stream = new FileStream(outPath, FileMode.Create)) + { + await formFile.CopyToAsync(stream); + stream.Flush(); + } + return true; + } + + /// + /// + /// + /// + /// + /// + /// + public static async Task Upload(IFormFile formFile, string fileName, string paths) + { + if (formFile == null || formFile.Length <= 0) + { + throw new NullReferenceException("IFormFile is null"); + } + // full path to file in temp location + if (!System.IO.File.Exists(paths)) + { + Directory.CreateDirectory(paths); + } + var filePath = Path.Combine(paths, fileName); + using (var stream = new FileStream(filePath, FileMode.Create)) + { + await formFile.CopyToAsync(stream); + stream.Flush(); + } + return true; + } + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/Areas/Admin/Pages/AIAgentsHub/DatabaseSample.cshtml b/src/Extensions/Senparc.Xncf.AIAgentsHub/Areas/Admin/Pages/AIAgentsHub/DatabaseSample.cshtml index b8c6fa780..bdd2ef545 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/Areas/Admin/Pages/AIAgentsHub/DatabaseSample.cshtml +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/Areas/Admin/Pages/AIAgentsHub/DatabaseSample.cshtml @@ -1,100 +1,100 @@ -@page -@model Senparc.Xncf.AIAgentsHub.Areas.AIAgentsHub.Pages.DatabaseSample -@{ - ViewData["Title"] = "数据库操作示例"; - Layout = "_Layout_Vue"; -} - -@section HeaderContent{ - -} - -@section breadcrumbs { - 扩展模块 - AI Agent Hub - 数据库操作示例 -} - -
- - -  数据库操作示例 - - - - - @(await Html.PartialAsync("_SideMenu")) - - - -

{{moduleData.xncfModuleDto.description}}

-
-

安装时间:{{formaTableTime(moduleData.colorDto.addTime)}}

-

当前数据库类型:@Model.MultipleDatabaseType

-
- - 变亮 - 变暗 - 随机 -
-
-
-
-
-@section scripts{ - -} +@page +@model Senparc.Xncf.AIAgentsHub.Areas.AIAgentsHub.Pages.DatabaseSample +@{ + ViewData["Title"] = "数据库操作示例"; + Layout = "_Layout_Vue"; +} + +@section HeaderContent{ + +} + +@section breadcrumbs { + 扩展模块 + AI Agent Hub + 数据库操作示例 +} + +
+ + +  数据库操作示例 + + + + + @(await Html.PartialAsync("_SideMenu")) + + + +

{{moduleData.xncfModuleDto.description}}

+
+

安装时间:{{formaTableTime(moduleData.colorDto.addTime)}}

+

当前数据库类型:@Model.MultipleDatabaseType

+
+ + 变亮 + 变暗 + 随机 +
+
+
+
+
+@section scripts{ + +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/DatabaseModel/AIAgentsHubSenparcEntities.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/DatabaseModel/AIAgentsHubSenparcEntities.cs index cf3bf3f3c..026cc3d99 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/DatabaseModel/AIAgentsHubSenparcEntities.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/DatabaseModel/AIAgentsHubSenparcEntities.cs @@ -1,24 +1,24 @@ -using Microsoft.EntityFrameworkCore; -using Senparc.Ncf.Database; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.XncfBase.Database; - -namespace Senparc.Xncf.AIAgentsHub.Models -{ - public class AIAgentsHubSenparcEntities : XncfDatabaseDbContext - { - public AIAgentsHubSenparcEntities(DbContextOptions dbContextOptions) : base(dbContextOptions) - { - } - - public DbSet Colors { get; set; } - - //DOT REMOVE OR MODIFY THIS LINE 请勿移除或修改本行 - Entities Point - //ex. public DbSet Colors { get; set; } - - //如无特殊需需要,OnModelCreating 方法可以不用写,已经在 Register 中要求注册 - //protected override void OnModelCreating(ModelBuilder modelBuilder) - //{ - //} - } -} +using Microsoft.EntityFrameworkCore; +using Senparc.Ncf.Database; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.XncfBase.Database; + +namespace Senparc.Xncf.AIAgentsHub.Models +{ + public class AIAgentsHubSenparcEntities : XncfDatabaseDbContext + { + public AIAgentsHubSenparcEntities(DbContextOptions dbContextOptions) : base(dbContextOptions) + { + } + + public DbSet Colors { get; set; } + + //DOT REMOVE OR MODIFY THIS LINE Do not remove or modify this bank - Entities Point + //ex. public DbSet Colors { get; set; } + + //If there is no special need, the OnModelCreating method does not need to be written. Registration is already required in Register. + //protected override void OnModelCreating(ModelBuilder modelBuilder) + //{ + //} + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/DatabaseModel/Color.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/DatabaseModel/Color.cs index 8cb788373..b4dbcd758 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/DatabaseModel/Color.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/DatabaseModel/Color.cs @@ -1,81 +1,81 @@ -using Senparc.Ncf.Core.Models; -using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel.Dto; -using System; -using System.ComponentModel.DataAnnotations.Schema; - -namespace Senparc.Xncf.AIAgentsHub -{ - /// - /// Color 实体类 - /// - [Table(Register.DATABASE_PREFIX + nameof(Color))]//必须添加前缀,防止全系统中发生冲突 - [Serializable] - public class Color : EntityBase - { - /// - /// 颜色码,0-255 - /// - public int Red { get; private set; } - /// - /// 颜色码,0-255 - /// - public int Green { get; private set; } - - /// - /// 颜色码,0-255 - /// - public int Blue { get; private set; } - - /// - /// 附加列,测试多次数据库 Migrate - /// - public string AdditionNote { get; private set; } - - private Color() { } - - public Color(int red, int green, int blue) - { - if (red < 0 || green < 0 || blue < 0) - { - Random();//随机 - } - else - { - Red = red; - Green = green; - Blue = blue; - } - } - - public Color(ColorDto colorDto) - { - Red = colorDto.Red; - Green = colorDto.Green; - Blue = colorDto.Blue; - } - - public void Random() - { - //随机产生颜色代码 - var radom = new Random(); - Func getRadomColorCode = () => radom.Next(0, 255); - Red = getRadomColorCode(); - Green = getRadomColorCode(); - Blue = getRadomColorCode(); - } - - public void Brighten() - { - Red = Math.Min(255, Red + 10); - Green = Math.Min(255, Green + 10); - Blue = Math.Min(255, Blue + 10); - } - - public void Darken() - { - Red = Math.Max(0, Red - 10); - Green = Math.Max(0, Green - 10); - Blue = Math.Max(0, Blue - 10); - } - } -} +using Senparc.Ncf.Core.Models; +using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel.Dto; +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Senparc.Xncf.AIAgentsHub +{ + /// + /// Color Entity class + /// + [Table(Register.DATABASE_PREFIX + nameof(Color))]//The prefix must be added to prevent conflicts system-wide. + [Serializable] + public class Color : EntityBase + { + /// + /// Color code, 0-255 + /// + public int Red { get; private set; } + /// + /// Color code, 0-255 + /// + public int Green { get; private set; } + + /// + /// Color code, 0-255 + /// + public int Blue { get; private set; } + + /// + /// Additional columns, test database multiple times Migrate + /// + public string AdditionNote { get; private set; } + + private Color() { } + + public Color(int red, int green, int blue) + { + if (red < 0 || green < 0 || blue < 0) + { + Random();//random + } + else + { + Red = red; + Green = green; + Blue = blue; + } + } + + public Color(ColorDto colorDto) + { + Red = colorDto.Red; + Green = colorDto.Green; + Blue = colorDto.Blue; + } + + public void Random() + { + //Randomly generate color codes + var radom = new Random(); + Func getRadomColorCode = () => radom.Next(0, 255); + Red = getRadomColorCode(); + Green = getRadomColorCode(); + Blue = getRadomColorCode(); + } + + public void Brighten() + { + Red = Math.Min(255, Red + 10); + Green = Math.Min(255, Green + 10); + Blue = Math.Min(255, Blue + 10); + } + + public void Darken() + { + Red = Math.Max(0, Red - 10); + Green = Math.Max(0, Green - 10); + Blue = Math.Max(0, Blue - 10); + } + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/DatabaseModel/Dto/ColorDto.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/DatabaseModel/Dto/ColorDto.cs index 6d85fca9f..7181a9ba1 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/DatabaseModel/Dto/ColorDto.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/DatabaseModel/Dto/ColorDto.cs @@ -1,22 +1,22 @@ -using Senparc.Ncf.Core.Models; - -namespace Senparc.Xncf.AIAgentsHub.Models.DatabaseModel.Dto -{ - public class ColorDto : DtoBase - { - /// - /// 颜色码,0-255 - /// - public int Red { get; private set; } - /// - /// 颜色码,0-255 - /// - public int Green { get; private set; } - /// - /// 颜色码,0-255 - /// - public int Blue { get; private set; } - - private ColorDto() { } - } -} +using Senparc.Ncf.Core.Models; + +namespace Senparc.Xncf.AIAgentsHub.Models.DatabaseModel.Dto +{ + public class ColorDto : DtoBase + { + /// + /// Color code, 0-255 + /// + public int Red { get; private set; } + /// + /// Color code, 0-255 + /// + public int Green { get; private set; } + /// + /// Color code, 0-255 + /// + public int Blue { get; private set; } + + private ColorDto() { } + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_Dm.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_Dm.cs index de0fbfa2c..d2304e949 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_Dm.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_Dm.cs @@ -1,41 +1,41 @@ - -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Senparc.Ncf.Database; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.XncfBase.Database; -using System; -using System.IO; -using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel; -using Microsoft.AspNetCore.Builder; - -namespace Senparc.Xncf.AIAgentsHub.Models -{ - [MultipleMigrationDbContext(MultipleDatabaseType.Dm, typeof(Register))] - public class AIAgentsHubSenparcEntities_Dm : AIAgentsHubSenparcEntities - { - public AIAgentsHubSenparcEntities_Dm(DbContextOptions dbContextOptions) : base(dbContextOptions) - { - } - } - - - /// - /// 设计时 DbContext 创建(仅在开发时创建 Code-First 的数据库 Migration 使用,在生产环境不会执行) - /// 1、切换至 Debug 模式 - /// 2、运行:PM> add-migration [更新名称] -c AIAgentsHubSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm - /// - public class SenparcDbContextFactory_Dm : SenparcDesignTimeDbContextFactoryBase - { - protected override Action AppAction => app => - { - //指定其他数据库 - app.UseNcfDatabase("Senparc.Ncf.Database.Dm", "Senparc.Ncf.Database.Dm", "DmDatabaseConfiguration"); - }; - - public SenparcDbContextFactory_Dm() : base(SenparcDbContextFactoryConfig.RootDirectoryPath) - { - - } - } -} + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Senparc.Ncf.Database; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.XncfBase.Database; +using System; +using System.IO; +using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel; +using Microsoft.AspNetCore.Builder; + +namespace Senparc.Xncf.AIAgentsHub.Models +{ + [MultipleMigrationDbContext(MultipleDatabaseType.Dm, typeof(Register))] + public class AIAgentsHubSenparcEntities_Dm : AIAgentsHubSenparcEntities + { + public AIAgentsHubSenparcEntities_Dm(DbContextOptions dbContextOptions) : base(dbContextOptions) + { + } + } + + + /// + /// DbContext creation at design time (Code-First database migration is only used during development and will not be executed in the production environment) + /// 1、Switch to Debug mode + /// 2、Run: PM> add-migration [Update name] -c AIAgentsHubSenparcEntities_Dm -o Domain/Migrations/Migrations.Dm + /// + public class SenparcDbContextFactory_Dm : SenparcDesignTimeDbContextFactoryBase + { + protected override Action AppAction => app => + { + //Specify another database + app.UseNcfDatabase("Senparc.Ncf.Database.Dm", "Senparc.Ncf.Database.Dm", "DmDatabaseConfiguration"); + }; + + public SenparcDbContextFactory_Dm() : base(SenparcDbContextFactoryConfig.RootDirectoryPath) + { + + } + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_MySql.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_MySql.cs index cf0aa1be1..0e926ee19 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_MySql.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_MySql.cs @@ -1,40 +1,40 @@ - -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Senparc.Ncf.Database; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.XncfBase.Database; -using System; -using System.IO; -using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel; -using Microsoft.AspNetCore.Builder; - -namespace Senparc.Xncf.AIAgentsHub.Models -{ - [MultipleMigrationDbContext(MultipleDatabaseType.MySql, typeof(Register))] - public class AIAgentsHubSenparcEntities_MySql : AIAgentsHubSenparcEntities - { - public AIAgentsHubSenparcEntities_MySql(DbContextOptions dbContextOptions) : base(dbContextOptions) - { - } - } - - /// - /// 设计时 DbContext 创建(仅在开发时创建 Code-First 的数据库 Migration 使用,在生产环境不会执行) - /// 1、切换至 Debug 模式 - /// 2、运行:PM> add-migration [更新名称] -c AIAgentsHubSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql - /// - public class SenparcDbContextFactory_MySql : SenparcDesignTimeDbContextFactoryBase - { - protected override Action AppAction => app => - { - //指定其他数据库 - app.UseNcfDatabase("Senparc.Ncf.Database.MySql", "Senparc.Ncf.Database.MySql", "MySqlDatabaseConfiguration"); - }; - - public SenparcDbContextFactory_MySql() : base(SenparcDbContextFactoryConfig.RootDirectoryPath) - { - - } - } -} + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Senparc.Ncf.Database; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.XncfBase.Database; +using System; +using System.IO; +using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel; +using Microsoft.AspNetCore.Builder; + +namespace Senparc.Xncf.AIAgentsHub.Models +{ + [MultipleMigrationDbContext(MultipleDatabaseType.MySql, typeof(Register))] + public class AIAgentsHubSenparcEntities_MySql : AIAgentsHubSenparcEntities + { + public AIAgentsHubSenparcEntities_MySql(DbContextOptions dbContextOptions) : base(dbContextOptions) + { + } + } + + /// + /// DbContext creation at design time (Code-First database migration is only used during development and will not be executed in the production environment) + /// 1、Switch to Debug mode + /// 2、Run: PM> add-migration [Update name] -c AIAgentsHubSenparcEntities_MySql -o Domain/Migrations/Migrations.MySql + /// + public class SenparcDbContextFactory_MySql : SenparcDesignTimeDbContextFactoryBase + { + protected override Action AppAction => app => + { + //Specify another database + app.UseNcfDatabase("Senparc.Ncf.Database.MySql", "Senparc.Ncf.Database.MySql", "MySqlDatabaseConfiguration"); + }; + + public SenparcDbContextFactory_MySql() : base(SenparcDbContextFactoryConfig.RootDirectoryPath) + { + + } + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_Oracle.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_Oracle.cs index ee7b07afd..8528418a0 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_Oracle.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_Oracle.cs @@ -1,41 +1,41 @@ - -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Senparc.Ncf.Database; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.XncfBase.Database; -using System; -using System.IO; -using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel; -using Microsoft.AspNetCore.Builder; - -namespace Senparc.Xncf.AIAgentsHub.Models -{ - [MultipleMigrationDbContext(MultipleDatabaseType.Oracle, typeof(Register))] - public class AIAgentsHubSenparcEntities_Oracle : AIAgentsHubSenparcEntities - { - public AIAgentsHubSenparcEntities_Oracle(DbContextOptions dbContextOptions) : base(dbContextOptions) - { - } - } - - - /// - /// 设计时 DbContext 创建(仅在开发时创建 Code-First 的数据库 Migration 使用,在生产环境不会执行) - /// 1、切换至 Debug 模式 - /// 2、运行:PM> add-migration [更新名称] -c AIAgentsHubSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle - /// - public class SenparcDbContextFactory_Oracle : SenparcDesignTimeDbContextFactoryBase - { - protected override Action AppAction => app => - { - //指定其他数据库 - app.UseNcfDatabase("Senparc.Ncf.Database.Oracle", "Senparc.Ncf.Database.Oracle", "OracleDatabaseConfiguration"); - }; - - public SenparcDbContextFactory_Oracle() : base(SenparcDbContextFactoryConfig.RootDirectoryPath) - { - - } - } -} + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Senparc.Ncf.Database; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.XncfBase.Database; +using System; +using System.IO; +using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel; +using Microsoft.AspNetCore.Builder; + +namespace Senparc.Xncf.AIAgentsHub.Models +{ + [MultipleMigrationDbContext(MultipleDatabaseType.Oracle, typeof(Register))] + public class AIAgentsHubSenparcEntities_Oracle : AIAgentsHubSenparcEntities + { + public AIAgentsHubSenparcEntities_Oracle(DbContextOptions dbContextOptions) : base(dbContextOptions) + { + } + } + + + /// + /// DbContext creation at design time (Code-First database migration is only used during development and will not be executed in the production environment) + /// 1、Switch to Debug mode + /// 2、Run: PM> add-migration [Update name] -c AIAgentsHubSenparcEntities_Oracle -o Domain/Migrations/Migrations.Oracle + /// + public class SenparcDbContextFactory_Oracle : SenparcDesignTimeDbContextFactoryBase + { + protected override Action AppAction => app => + { + //Specify another database + app.UseNcfDatabase("Senparc.Ncf.Database.Oracle", "Senparc.Ncf.Database.Oracle", "OracleDatabaseConfiguration"); + }; + + public SenparcDbContextFactory_Oracle() : base(SenparcDbContextFactoryConfig.RootDirectoryPath) + { + + } + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_PostgreSQL.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_PostgreSQL.cs index 6837c28eb..21bb4d6af 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_PostgreSQL.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_PostgreSQL.cs @@ -1,41 +1,41 @@ - -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Senparc.Ncf.Database; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.XncfBase.Database; -using System; -using System.IO; -using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel; -using Microsoft.AspNetCore.Builder; - -namespace Senparc.Xncf.AIAgentsHub.Models -{ - [MultipleMigrationDbContext(MultipleDatabaseType.PostgreSQL, typeof(Register))] - public class AIAgentsHubSenparcEntities_PostgreSQL : AIAgentsHubSenparcEntities - { - public AIAgentsHubSenparcEntities_PostgreSQL(DbContextOptions dbContextOptions) : base(dbContextOptions) - { - } - } - - - /// - /// 设计时 DbContext 创建(仅在开发时创建 Code-First 的数据库 Migration 使用,在生产环境不会执行) - /// 1、切换至 Debug 模式 - /// 2、运行:PM> add-migration [更新名称] -c AIAgentsHubSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL - /// - public class SenparcDbContextFactory_PostgreSQL : SenparcDesignTimeDbContextFactoryBase - { - protected override Action AppAction => app => - { - //指定其他数据库 - app.UseNcfDatabase("Senparc.Ncf.Database.PostgreSQL", "Senparc.Ncf.Database.PostgreSQL", "PostgreSQLDatabaseConfiguration"); - }; - - public SenparcDbContextFactory_PostgreSQL() : base(SenparcDbContextFactoryConfig.RootDirectoryPath) - { - - } - } -} + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Senparc.Ncf.Database; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.XncfBase.Database; +using System; +using System.IO; +using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel; +using Microsoft.AspNetCore.Builder; + +namespace Senparc.Xncf.AIAgentsHub.Models +{ + [MultipleMigrationDbContext(MultipleDatabaseType.PostgreSQL, typeof(Register))] + public class AIAgentsHubSenparcEntities_PostgreSQL : AIAgentsHubSenparcEntities + { + public AIAgentsHubSenparcEntities_PostgreSQL(DbContextOptions dbContextOptions) : base(dbContextOptions) + { + } + } + + + /// + /// DbContext creation at design time (Code-First database migration is only used during development and will not be executed in the production environment) + /// 1、Switch to Debug mode + /// 2、Run: PM> add-migration [Update name] -c AIAgentsHubSenparcEntities_PostgreSQL -o Migrations/Migrations.PostgreSQL + /// + public class SenparcDbContextFactory_PostgreSQL : SenparcDesignTimeDbContextFactoryBase + { + protected override Action AppAction => app => + { + //Specify another database + app.UseNcfDatabase("Senparc.Ncf.Database.PostgreSQL", "Senparc.Ncf.Database.PostgreSQL", "PostgreSQLDatabaseConfiguration"); + }; + + public SenparcDbContextFactory_PostgreSQL() : base(SenparcDbContextFactoryConfig.RootDirectoryPath) + { + + } + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_SQLite.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_SQLite.cs index a136565dd..ec66b2351 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_SQLite.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_SQLite.cs @@ -1,38 +1,38 @@ - -using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.EntityFrameworkCore; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Database; -using Senparc.Ncf.XncfBase.Database; - -namespace Senparc.Xncf.AIAgentsHub.Models -{ - [MultipleMigrationDbContext(MultipleDatabaseType.Sqlite, typeof(Register))] - public class AIAgentsHubSenparcEntities_Sqlite : AIAgentsHubSenparcEntities - { - public AIAgentsHubSenparcEntities_Sqlite(DbContextOptions dbContextOptions) : base(dbContextOptions) - { - } - } - - - /// - /// 设计时 DbContext 创建(仅在开发时创建 Code-First 的数据库 Migration 使用,在生产环境不会执行) - /// 1、切换至 Debug 模式 - /// 2、运行:PM> add-migration [更新名称] -c AIAgentsHubSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite - /// - public class SenparcDbContextFactory_Sqlite : SenparcDesignTimeDbContextFactoryBase - { - protected override Action AppAction => app => - { - //指定其他数据库 - app.UseNcfDatabase("Senparc.Ncf.Database.Sqlite", "Senparc.Ncf.Database.Sqlite", "SqliteMemoryDatabaseConfiguration"); - }; - - public SenparcDbContextFactory_Sqlite() : base(SenparcDbContextFactoryConfig.RootDirectoryPath) - { - - } - } -} + +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.EntityFrameworkCore; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Database; +using Senparc.Ncf.XncfBase.Database; + +namespace Senparc.Xncf.AIAgentsHub.Models +{ + [MultipleMigrationDbContext(MultipleDatabaseType.Sqlite, typeof(Register))] + public class AIAgentsHubSenparcEntities_Sqlite : AIAgentsHubSenparcEntities + { + public AIAgentsHubSenparcEntities_Sqlite(DbContextOptions dbContextOptions) : base(dbContextOptions) + { + } + } + + + /// + /// DbContext creation at design time (Code-First database migration is only used during development and will not be executed in the production environment) + /// 1、Switch to Debug mode + /// 2、Run: PM> add-migration [Update name] -c AIAgentsHubSenparcEntities_Sqlite -o Domain/Migrations/Migrations.Sqlite + /// + public class SenparcDbContextFactory_Sqlite : SenparcDesignTimeDbContextFactoryBase + { + protected override Action AppAction => app => + { + //Specify another database + app.UseNcfDatabase("Senparc.Ncf.Database.Sqlite", "Senparc.Ncf.Database.Sqlite", "SqliteMemoryDatabaseConfiguration"); + }; + + public SenparcDbContextFactory_Sqlite() : base(SenparcDbContextFactoryConfig.RootDirectoryPath) + { + + } + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_SqlServer.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_SqlServer.cs index a7a269836..3a11ce7a7 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_SqlServer.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/AIAgentsHubSenparcEntities_SqlServer.cs @@ -1,41 +1,41 @@ - -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Senparc.Ncf.Database; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.XncfBase.Database; -using System; -using System.IO; -using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel; -using Microsoft.AspNetCore.Builder; - -namespace Senparc.Xncf.AIAgentsHub.Models -{ - [MultipleMigrationDbContext(MultipleDatabaseType.SqlServer, typeof(Register))] - public class AIAgentsHubSenparcEntities_SqlServer : AIAgentsHubSenparcEntities - { - public AIAgentsHubSenparcEntities_SqlServer(DbContextOptions dbContextOptions) : base(dbContextOptions) - { - } - } - - - /// - /// 设计时 DbContext 创建(仅在开发时创建 Code-First 的数据库 Migration 使用,在生产环境不会执行) - /// 1、切换至 Debug 模式 - /// 2、运行:PM> add-migration [更新名称] -c AIAgentsHubSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer - /// - public class SenparcDbContextFactory_SqlServer : SenparcDesignTimeDbContextFactoryBase - { - protected override Action AppAction => app => - { - //指定其他数据库 - app.UseNcfDatabase("Senparc.Ncf.Database.SqlServer", "Senparc.Ncf.Database.SqlServer", "SqlServerDatabaseConfiguration"); - }; - - public SenparcDbContextFactory_SqlServer() : base(SenparcDbContextFactoryConfig.RootDirectoryPath) - { - - } - } -} + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Senparc.Ncf.Database; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.XncfBase.Database; +using System; +using System.IO; +using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel; +using Microsoft.AspNetCore.Builder; + +namespace Senparc.Xncf.AIAgentsHub.Models +{ + [MultipleMigrationDbContext(MultipleDatabaseType.SqlServer, typeof(Register))] + public class AIAgentsHubSenparcEntities_SqlServer : AIAgentsHubSenparcEntities + { + public AIAgentsHubSenparcEntities_SqlServer(DbContextOptions dbContextOptions) : base(dbContextOptions) + { + } + } + + + /// + /// DbContext creation at design time (Code-First database migration is only used during development and will not be executed in the production environment) + /// 1、Switch to Debug mode + /// 2、Run: PM> add-migration [Update name] -c AIAgentsHubSenparcEntities_SqlServer -o Domain/Migrations/Migrations.SqlServer + /// + public class SenparcDbContextFactory_SqlServer : SenparcDesignTimeDbContextFactoryBase + { + protected override Action AppAction => app => + { + //Specify another database + app.UseNcfDatabase("Senparc.Ncf.Database.SqlServer", "Senparc.Ncf.Database.SqlServer", "SqlServerDatabaseConfiguration"); + }; + + public SenparcDbContextFactory_SqlServer() : base(SenparcDbContextFactoryConfig.RootDirectoryPath) + { + + } + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs index 45cdc0025..5ff179474 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Models/MultipleDatabase/SenparcDbContextFactoryConfig.cs @@ -1,41 +1,41 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - -namespace Senparc.Xncf.AIAgentsHub.Models -{ - /// - /// SenparcDbContextFactory 的公共配置 - /// - public static class SenparcDbContextFactoryConfig - { - private static string _rootDirectoryPath = null; - - /// - /// 用于寻找 App_Data 文件夹,从而找到数据库连接字符串配置信息 - /// - public static string RootDirectoryPath - { - get - { - if (_rootDirectoryPath == null) - { - var projectPath = Path.GetFullPath($"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}", AppContext.BaseDirectory);//项目根目录 - - var webPath = Path.GetFullPath($"..{Path.DirectorySeparatorChar}Senparc.Web",/*找到 Web目录,以获取统一的数据库连接字符串配置*/ - projectPath); - if (Directory.Exists(webPath)) - { - _rootDirectoryPath = webPath;//优先使用Web统一配置 - } - else - { - _rootDirectoryPath = projectPath; - } - } - return _rootDirectoryPath; - } - } - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Senparc.Xncf.AIAgentsHub.Models +{ + /// + /// SenparcDbContextFactory public configuration + /// + public static class SenparcDbContextFactoryConfig + { + private static string _rootDirectoryPath = null; + + /// + /// Used to find apps_Data folder to find the database connection string configuration information + /// + public static string RootDirectoryPath + { + get + { + if (_rootDirectoryPath == null) + { + var projectPath = Path.GetFullPath($"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}", AppContext.BaseDirectory);//Project root directory + + var webPath = Path.GetFullPath($"..{Path.DirectorySeparatorChar}Senparc.Web",/*Locate the web directory for unified database connection string configuration*/ + projectPath); + if (Directory.Exists(webPath)) + { + _rootDirectoryPath = webPath;//Prefer using Web unified configuration + } + else + { + _rootDirectoryPath = projectPath; + } + } + return _rootDirectoryPath; + } + } + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Services/ColorService.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Services/ColorService.cs index 04cc576d7..9419c7dcf 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Services/ColorService.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/Domain/Services/ColorService.cs @@ -1,68 +1,68 @@ -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.Repository; -using Senparc.Ncf.Service; -using Senparc.Xncf.AIAgentsHub.Domain.Services; -using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel.Dto; -using System; -using System.Threading.Tasks; - -namespace Senparc.Xncf.AIAgentsHub.Domain.Services -{ - public class ColorService : ServiceBase - { - public ColorService(IRepositoryBase repo, IServiceProvider serviceProvider) - : base(repo, serviceProvider) - { - } - - public async Task CreateNewColor() - { - Color color = new Color(-1, -1, -1); - await base.SaveObjectAsync(color).ConfigureAwait(false); - ColorDto colorDto = base.Mapper.Map(color); - return colorDto; - } - - public async Task GetOrInitColor() - { - var color = await base.GetObjectAsync(z => true); - if (color == null)//如果是纯第一次安装,理论上不会有残留数据 - { - //创建默认颜色 - ColorDto colorDto = await this.CreateNewColor().ConfigureAwait(false); - return colorDto; - } - - return base.Mapper.Map(color); - } - - public async Task Brighten() - { - //TODO:异步方法需要添加排序功能 - var obj = this.GetObject(z => true, z => z.Id, OrderingType.Descending); - obj.Brighten(); - await base.SaveObjectAsync(obj).ConfigureAwait(false); - return base.Mapper.Map(obj); - } - - public async Task Darken() - { - //TODO:异步方法需要添加排序功能 - var obj = this.GetObject(z => true, z => z.Id, OrderingType.Descending); - obj.Darken(); - await base.SaveObjectAsync(obj).ConfigureAwait(false); - return base.Mapper.Map(obj); - } - - public async Task Random() - { - //TODO:异步方法需要添加排序功能 - var obj = this.GetObject(z => true, z => z.Id, OrderingType.Descending); - obj.Random(); - await base.SaveObjectAsync(obj).ConfigureAwait(false); - return base.Mapper.Map(obj); - } - - //TODO: 更多业务方法可以写到这里 - } -} +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.Repository; +using Senparc.Ncf.Service; +using Senparc.Xncf.AIAgentsHub.Domain.Services; +using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel.Dto; +using System; +using System.Threading.Tasks; + +namespace Senparc.Xncf.AIAgentsHub.Domain.Services +{ + public class ColorService : ServiceBase + { + public ColorService(IRepositoryBase repo, IServiceProvider serviceProvider) + : base(repo, serviceProvider) + { + } + + public async Task CreateNewColor() + { + Color color = new Color(-1, -1, -1); + await base.SaveObjectAsync(color).ConfigureAwait(false); + ColorDto colorDto = base.Mapper.Map(color); + return colorDto; + } + + public async Task GetOrInitColor() + { + var color = await base.GetObjectAsync(z => true); + if (color == null)//If this is a purely first-time installation, theoretically there will be no residual data. + { + //Create default colors + ColorDto colorDto = await this.CreateNewColor().ConfigureAwait(false); + return colorDto; + } + + return base.Mapper.Map(color); + } + + public async Task Brighten() + { + //TODO:Asynchronous methods need to add sorting functionality + var obj = this.GetObject(z => true, z => z.Id, OrderingType.Descending); + obj.Brighten(); + await base.SaveObjectAsync(obj).ConfigureAwait(false); + return base.Mapper.Map(obj); + } + + public async Task Darken() + { + //TODO:Asynchronous methods need to add sorting functionality + var obj = this.GetObject(z => true, z => z.Id, OrderingType.Descending); + obj.Darken(); + await base.SaveObjectAsync(obj).ConfigureAwait(false); + return base.Mapper.Map(obj); + } + + public async Task Random() + { + //TODO:Asynchronous methods need to add sorting functionality + var obj = this.GetObject(z => true, z => z.Id, OrderingType.Descending); + obj.Random(); + await base.SaveObjectAsync(obj).ConfigureAwait(false); + return base.Mapper.Map(obj); + } + + //TODO: More business methods can be written here + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/OHS/Local/AppService/ApiAppService.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/OHS/Local/AppService/ApiAppService.cs index 4322ab496..7557cb274 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/OHS/Local/AppService/ApiAppService.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/OHS/Local/AppService/ApiAppService.cs @@ -1,64 +1,61 @@ -using Senparc.CO2NET; -using Senparc.CO2NET.WebApi; -using Senparc.Ncf.Core.AppServices; -using Senparc.Ncf.Core.Exceptions; -using Senparc.Xncf.AIAgentsHub.OHS.Local.PL; -using System; -using System.Threading.Tasks; - - -namespace Senparc.Xncf.AIAgentsHub.OHS.Local.AppService -{ - public class ApiAppService : AppServiceBase - { - public ApiAppService(IServiceProvider serviceProvider) : base(serviceProvider) - { - } - - /* - * 使用 [ApiBind] 可将任意方法或类快速创建动态 WebApi。 - * 在 DDD 系统中,出于安全和防腐考虑,建议只在 AppService 上使用。 - * 当 AppService 上添加 [ApiBind] 标签满足不了需求时,仍然可以手动创建 ApiController。 - */ - - /// - /// 将 AppService 暴露为 WebApi - /// - /// - [ApiBind] - public async Task> MyApi() - { - return await this.GetResponseAsync(async (response, logger) => - { - await Task.Delay(100); - return 200; - }); - } - - /// - /// 自定义 Post 类型和复杂参数,同时测试异常抛出和自定义状态码 - /// - /// - [ApiBind(ApiRequestMethod = ApiRequestMethod.Post)] - public async Task MyCustomApi(Api_MyCustomApiRequest request) - { - //StringAppResponse 是 AppResponseBase 的快捷写法 - return await this.GetStringResponseAsync(async (response, logger) => - { - throw new NcfExceptionBase($"抛出异常测试,传输参数:{request.FirstName} {request.LastName}"); - response.StateCode = 100; - }, - exceptionHandler: (ex, response, logger) => - { - logger.Append($"正在处理异常,信息:{ex.Message}"); - }, - afterFunc: (response, logger) => - { - if (response.Success != true) - { - response.StateCode = 101; - } - }); - } - } -} +using Senparc.CO2NET; +using Senparc.CO2NET.WebApi; +using Senparc.Ncf.Core.AppServices; +using Senparc.Ncf.Core.Exceptions; +using Senparc.Xncf.AIAgentsHub.OHS.Local.PL; +using System; +using System.Threading.Tasks; + + +namespace Senparc.Xncf.AIAgentsHub.OHS.Local.AppService +{ + public class ApiAppService : AppServiceBase + { + public ApiAppService(IServiceProvider serviceProvider) : base(serviceProvider) + { + } + + /* + * use[ApiBind] Any method or class can be quickly created into a dynamic WebApi.* In DDD systems, for security and anti-corrosion considerations, it is recommended to only use AppService.* When added on AppService[ApiBind] When the label cannot meet the needs, you can still create an ApiController manually.*/ + + /// + /// Expose AppService as WebApi + /// + /// + [ApiBind] + public async Task> MyApi() + { + return await this.GetResponseAsync(async (response, logger) => + { + await Task.Delay(100); + return 200; + }); + } + + /// + /// Customize Post types and complex parameters, while testing exception throwing and custom status codes + /// + /// + [ApiBind(ApiRequestMethod = ApiRequestMethod.Post)] + public async Task MyCustomApi(Api_MyCustomApiRequest request) + { + //StringAppResponse is AppResponseBase shortcut for writing + return await this.GetStringResponseAsync(async (response, logger) => + { + throw new NcfExceptionBase($"Throw an exception test and transfer parameters:{request.FirstName} {request.LastName}"); + response.StateCode = 100; + }, + exceptionHandler: (ex, response, logger) => + { + logger.Append($"Handling exception, message:{ex.Message}"); + }, + afterFunc: (response, logger) => + { + if (response.Success != true) + { + response.StateCode = 101; + } + }); + } + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/OHS/Local/AppService/ColorAppService.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/OHS/Local/AppService/ColorAppService.cs index 6dc8efc16..892be6460 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/OHS/Local/AppService/ColorAppService.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/OHS/Local/AppService/ColorAppService.cs @@ -1,43 +1,43 @@ -using Senparc.CO2NET; -using Senparc.Ncf.Core.AppServices; -using Senparc.Xncf.AIAgentsHub.Domain.Services; -using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel.Dto; -using Senparc.Xncf.AIAgentsHub.OHS.Local.PL; -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Xncf.AIAgentsHub.OHS.Local.AppService -{ - public class ColorAppService : AppServiceBase - { - private readonly ColorService _colorService; - - public ColorAppService(IServiceProvider serviceProvider, ColorService colorService) : base(serviceProvider) - { - this._colorService = colorService; - } - - - /// - /// 获取或初始化一个 ColorDto 对象 - /// - /// - public async Task> GetOrInitColorAsync() - { - return await this.GetResponseAsync(async (response, logger) => - { - var dt1 = SystemTime.Now;//开始计时 - - var colorDto = await _colorService.GetOrInitColor();//获取或初始化颜色参数 - - var costMs = SystemTime.DiffTotalMS(dt1);//记录耗时 - - Color_GetOrInitColorResponse result = new(colorDto.Red, colorDto.Green, colorDto.Blue, costMs); - - return result; - }); - } - } -} +using Senparc.CO2NET; +using Senparc.Ncf.Core.AppServices; +using Senparc.Xncf.AIAgentsHub.Domain.Services; +using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel.Dto; +using Senparc.Xncf.AIAgentsHub.OHS.Local.PL; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Xncf.AIAgentsHub.OHS.Local.AppService +{ + public class ColorAppService : AppServiceBase + { + private readonly ColorService _colorService; + + public ColorAppService(IServiceProvider serviceProvider, ColorService colorService) : base(serviceProvider) + { + this._colorService = colorService; + } + + + /// + /// Get or initialize a ColorDto object + /// + /// + public async Task> GetOrInitColorAsync() + { + return await this.GetResponseAsync(async (response, logger) => + { + var dt1 = SystemTime.Now;//Start timing + + var colorDto = await _colorService.GetOrInitColor();//Get or initialize color parameters + + var costMs = SystemTime.DiffTotalMS(dt1);//Recording time + + Color_GetOrInitColorResponse result = new(colorDto.Red, colorDto.Green, colorDto.Blue, costMs); + + return result; + }); + } + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/OHS/Local/AppService/MyFuctionAppService.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/OHS/Local/AppService/MyFuctionAppService.cs index 8a984a5df..e3150437f 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/OHS/Local/AppService/MyFuctionAppService.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/OHS/Local/AppService/MyFuctionAppService.cs @@ -1,87 +1,81 @@ -using Microsoft.EntityFrameworkCore; -using Senparc.CO2NET; -using Senparc.CO2NET.Extensions; -using Senparc.Ncf.Core.AppServices; -using Senparc.Ncf.Core.Models; -using Senparc.Xncf.AIAgentsHub.Domain.Services; -using Senparc.Xncf.AIAgentsHub.OHS.Local.PL; -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; - - -namespace Senparc.Xncf.AIAgentsHub.OHS.Local.AppService -{ - public class MyFuctionAppService: AppServiceBase - { - private ColorService _colorService; - public MyFuctionAppService(IServiceProvider serviceProvider, ColorService colorService) : base(serviceProvider) - { - _colorService = colorService; - } - - [FunctionRender("我的函数", "我的函数的注释", typeof(Register))] - public async Task Calculate(MyFunction_CaculateRequest request) - { - return await this.GetStringResponseAsync(async (response, logger) => - { - /* 页面上点击“执行”后,将调用这里的方法 - * - * 参数说明: - * response:已经初始化后的返回结果 - * logger:日志 - * - * 如果直接对 response 的属性修改,则最终 return null, - * 否则可以返回一个新的 response 对象,系统将自动覆盖原有对象 - */ - - double calcResult = request.Number1; - var theOperator = request.Operator.SelectedValues.FirstOrDefault(); - switch (theOperator) - { - case "+": - calcResult = calcResult + request.Number2; - break; - case "-": - calcResult = calcResult - request.Number2; - break; - case "×": - calcResult = calcResult * request.Number2; - break; - case "÷": - if (request.Number2 == 0) - { - response.Success = false; - response.ErrorMessage = "被除数不能为0!"; - return null; - } - calcResult = calcResult / request.Number2; - break; - default: - response.Success = false; - response.ErrorMessage = $"未知的运算符:{theOperator}"; - return null; - } - - logger.Append($"进行运算:{request.Number1} {theOperator} {request.Number2} = {calcResult}"); - - Action raisePower = power => - { - if (request.Power.SelectedValues.Contains(power.ToString())) - { - var oldValue = calcResult; - calcResult = Math.Pow(calcResult, power); - logger.Append($"进行{power}次方运算:{oldValue}{(power == 2 ? "²" : "³")} = {calcResult}"); - } - }; - - raisePower(2); - raisePower(3); - - response.Data = $"【{request.Name}】计算结果:{calcResult}。计算过程请看日志"; - return null; - }); - } - } -} +using Microsoft.EntityFrameworkCore; +using Senparc.CO2NET; +using Senparc.CO2NET.Extensions; +using Senparc.Ncf.Core.AppServices; +using Senparc.Ncf.Core.Models; +using Senparc.Xncf.AIAgentsHub.Domain.Services; +using Senparc.Xncf.AIAgentsHub.OHS.Local.PL; +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + + +namespace Senparc.Xncf.AIAgentsHub.OHS.Local.AppService +{ + public class MyFuctionAppService: AppServiceBase + { + private ColorService _colorService; + public MyFuctionAppService(IServiceProvider serviceProvider, ColorService colorService) : base(serviceProvider) + { + _colorService = colorService; + } + + [FunctionRender("我的函数", "我的函数的注释", typeof(Register))] + public async Task Calculate(MyFunction_CaculateRequest request) + { + return await this.GetStringResponseAsync(async (response, logger) => + { + /* After clicking "Execute" on the page, the method here will be called* + * Parameter description:* response:Return result after initialization* logger:log* + * If the properties of response are modified directly, null will eventually be returned.* Otherwise, a new response object can be returned, and the system will automatically overwrite the original object.*/ + + double calcResult = request.Number1; + var theOperator = request.Operator.SelectedValues.FirstOrDefault(); + switch (theOperator) + { + case "+": + calcResult = calcResult + request.Number2; + break; + case "-": + calcResult = calcResult - request.Number2; + break; + case "×": + calcResult = calcResult * request.Number2; + break; + case "÷": + if (request.Number2 == 0) + { + response.Success = false; + response.ErrorMessage = "被除数不能为0!"; + return null; + } + calcResult = calcResult / request.Number2; + break; + default: + response.Success = false; + response.ErrorMessage = $"未知的运算符:{theOperator}"; + return null; + } + + logger.Append($"进行运算:{request.Number1} {theOperator} {request.Number2} = {calcResult}"); + + Action raisePower = power => + { + if (request.Power.SelectedValues.Contains(power.ToString())) + { + var oldValue = calcResult; + calcResult = Math.Pow(calcResult, power); + logger.Append($"进行{power}次方运算:{oldValue}{(power == 2 ? "²" : "³")} = {calcResult}"); + } + }; + + raisePower(2); + raisePower(3); + + response.Data = $"【{request.Name}】计算结果:{calcResult}。计算过程请看日志"; + return null; + }); + } + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/OHS/Local/PL/ColorResponse.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/OHS/Local/PL/ColorResponse.cs index c1741d2f9..fb8f5d181 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/OHS/Local/PL/ColorResponse.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/OHS/Local/PL/ColorResponse.cs @@ -1,36 +1,36 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Senparc.Xncf.AIAgentsHub.OHS.Local.PL -{ - public class Color_GetOrInitColorResponse - { - /// - /// 颜色码,0-255 - /// - public int Red { get; private set; } - /// - /// 颜色码,0-255 - /// - public int Green { get; private set; } - /// - /// 颜色码,0-255 - /// - public int Blue { get; private set; } - /// - /// 花费时间 - /// - public double CostMillionSeconds { get; set; } - - public Color_GetOrInitColorResponse(int red, int green, int blue, double costMillionSeconds) - { - Red = red; - Green = green; - Blue = blue; - CostMillionSeconds = costMillionSeconds; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Senparc.Xncf.AIAgentsHub.OHS.Local.PL +{ + public class Color_GetOrInitColorResponse + { + /// + /// Color code, 0-255 + /// + public int Red { get; private set; } + /// + /// Color code, 0-255 + /// + public int Green { get; private set; } + /// + /// Color code, 0-255 + /// + public int Blue { get; private set; } + /// + /// spend time + /// + public double CostMillionSeconds { get; set; } + + public Color_GetOrInitColorResponse(int red, int green, int blue, double costMillionSeconds) + { + Red = red; + Green = green; + Blue = blue; + CostMillionSeconds = costMillionSeconds; + } + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/Register.Area.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/Register.Area.cs index 1e795e373..dba2128b9 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/Register.Area.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/Register.Area.cs @@ -1,44 +1,44 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Senparc.CO2NET.Trace; -using Senparc.Ncf.Core.Areas; -using Senparc.Ncf.Core.Config; -using System; -using Senparc.Ncf.XncfBase; -using System.Collections.Generic; -using System.IO; -using Microsoft.Extensions.Hosting; - -namespace Senparc.Xncf.AIAgentsHub -{ - public partial class Register : IAreaRegister, //注册 XNCF 页面接口(按需选用) - IXncfRazorRuntimeCompilation //赋能 RazorPage 运行时编译 - { - #region IAreaRegister 接口 - - public string HomeUrl => "/Admin/AIAgentsHub/Index"; - - public List AreaPageMenuItems => new List() { - new AreaPageMenuItem(GetAreaHomeUrl(),"首页","fa fa-laptop"), - new AreaPageMenuItem(GetAreaUrl($"/Admin/AIAgentsHub/DatabaseSample"),"数据库操作示例","fa fa-bookmark-o") - }; - - public IMvcBuilder AuthorizeConfig(IMvcBuilder builder, IHostEnvironment env) - { - builder.AddRazorPagesOptions(options => - { - //此处可配置页面权限 - }); - - SenparcTrace.SendCustomLog("AIAgentsHub 启动", "完成 Area:Senparc.Xncf.AIAgentsHub 注册"); - - return builder; - } - - #endregion - - #region IXncfRazorRuntimeCompilation 接口 - public string LibraryPath => Path.GetFullPath(Path.Combine(SiteConfig.WebRootPath, "..", "..", "Senparc.Xncf.AIAgentsHub")); - #endregion - } -} +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Senparc.CO2NET.Trace; +using Senparc.Ncf.Core.Areas; +using Senparc.Ncf.Core.Config; +using System; +using Senparc.Ncf.XncfBase; +using System.Collections.Generic; +using System.IO; +using Microsoft.Extensions.Hosting; + +namespace Senparc.Xncf.AIAgentsHub +{ + public partial class Register : IAreaRegister, //Register XNCF page interface (optional on demand) + IXncfRazorRuntimeCompilation //Enable RazorPage runtime compilation + { + #region IAreaRegister interface + + public string HomeUrl=> "/Admin/AIAgentsHub/Index"; + + public List AreaPageMenuItems => new List() { + new AreaPageMenuItem(GetAreaHomeUrl(),"front page","fa fa-laptop"), + new AreaPageMenuItem(GetAreaUrl($"/Admin/AIAgentsHub/DatabaseSample"),"Database operation example","fa fa-bookmark-o") + }; + + public IMvcBuilder AuthorizeConfig(IMvcBuilder builder, IHostEnvironment env) + { + builder.AddRazorPagesOptions(options => + { + //Page permissions can be configured here + }); + + SenparcTrace.SendCustomLog("AIAgentsHub start up", "Complete Area:Senparc.Xncf.AIAgentsHub register"); + + return builder; + } + + #endregion + + #region IXncfRazorRuntimeCompilation interface + public string LibraryPath=> Path.GetFullPath(Path.Combine(SiteConfig.WebRootPath, "..", "..", "Senparc.Xncf.AIAgentsHub")); + #endregion + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/Register.Database.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/Register.Database.cs index 3eab01e49..964c30679 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/Register.Database.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/Register.Database.cs @@ -1,42 +1,40 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using System; -using Senparc.Ncf.Database; -using Senparc.Ncf.Core.Models; - -namespace Senparc.Xncf.AIAgentsHub -{ - public partial class Register : IXncfDatabase //注册 XNCF 模块数据库(按需选用) - { - #region IXncfDatabase 接口 - - /// - /// 数据库前缀 - /// - public const string DATABASE_PREFIX = "Senparc_AIAgentsHub_"; - - /// - /// 数据库前缀 - /// - public string DatabaseUniquePrefix => DATABASE_PREFIX; - - /// - /// 动态获取数据库上下文 - /// - public Type TryGetXncfDatabaseDbContextType => MultipleDatabasePool.Instance.GetXncfDbContextType(this); - - public void OnModelCreating(ModelBuilder modelBuilder) - { - //实现 [XncfAutoConfigurationMapping] 特性之后,可以自动执行,无需手动添加 - //modelBuilder.ApplyConfiguration(new AreaTemplate_ColorConfigurationMapping()); - } - - public void AddXncfDatabaseModule(IServiceCollection services) - { - //DOT REMOVE OR MODIFY THIS LINE 请勿移除或修改本行 - Entities Point - //ex. services.AddScoped(typeof(Color)); - } - - #endregion - } -} +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using System; +using Senparc.Ncf.Database; +using Senparc.Ncf.Core.Models; + +namespace Senparc.Xncf.AIAgentsHub +{ + public partial class Register : IXncfDatabase //Register the XNCF module database (optional) + { + #region IXncfDatabase interface/// + /// Database prefix + /// + public const string DATABASE_PREFIX = "Senparc_AIAgentsHub_"; + + /// + /// Database prefix + /// + public string DatabaseUniquePrefix => DATABASE_PREFIX; + + /// + /// Dynamically obtain database context + /// + public Type TryGetXncfDatabaseDbContextType => MultipleDatabasePool.Instance.GetXncfDbContextType(this); + + public void OnModelCreating(ModelBuilder modelBuilder) + { + //accomplish[XncfAutoConfigurationMapping] After the feature is added, it can be executed automatically without adding it manually. + //modelBuilder.ApplyConfiguration(new AreaTemplate_ColorConfigurationMapping()); + } + + public void AddXncfDatabaseModule(IServiceCollection services) + { + //DOT REMOVE OR MODIFY THIS LINE Do not remove or modify this bank - Entities Point + //ex. services.AddScoped(typeof(Color)); + } + + #endregion + } +} diff --git a/src/Extensions/Senparc.Xncf.AIAgentsHub/Register.cs b/src/Extensions/Senparc.Xncf.AIAgentsHub/Register.cs index 411ab4ac8..76231602e 100644 --- a/src/Extensions/Senparc.Xncf.AIAgentsHub/Register.cs +++ b/src/Extensions/Senparc.Xncf.AIAgentsHub/Register.cs @@ -1,89 +1,89 @@ -using Senparc.Ncf.Core.Enums; -using Senparc.Ncf.XncfBase; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using System.Linq; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; - -using Senparc.Xncf.AIAgentsHub.Models; -using Senparc.Xncf.AIAgentsHub.OHS.Local.AppService; -using Senparc.Ncf.Core.Models; -using Senparc.Ncf.Database; -using Senparc.Ncf.XncfBase.Database; -using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel.Dto; - -namespace Senparc.Xncf.AIAgentsHub -{ - [XncfRegister] - public partial class Register : XncfRegisterBase, IXncfRegister - { - #region IXncfRegister 接口 - - public override string Name => "Senparc.Xncf.AIAgentsHub"; - - public override string Uid => "49EFBD38-B093-46B1-9297-31FF8E6E8B29";//必须确保全局唯一,生成后必须固定,已自动生成,也可自行修改 - - public override string Version => "0.1.0";//必须填写版本号 - - public override string MenuName => "AI Agent Hub"; - - public override string Icon => "fa fa-robot"; - - public override string Description => "人工智能代理枢纽模块"; - - public override async Task InstallOrUpdateAsync(IServiceProvider serviceProvider, InstallOrUpdate installOrUpdate) - { - //安装或升级版本时更新数据库 - await XncfDatabaseDbContext.MigrateOnInstallAsync(serviceProvider, this); - - //根据安装或更新不同条件执行逻辑 - switch (installOrUpdate) - { - case InstallOrUpdate.Install: - //新安装 - #region 初始化数据库数据 - var colorService = serviceProvider.GetService(); - var colorResult = await colorService.GetOrInitColorAsync(); - #endregion - break; - case InstallOrUpdate.Update: - //更新 - break; - default: - throw new ArgumentOutOfRangeException(); - } - } - - public override async Task UninstallAsync(IServiceProvider serviceProvider, Func unsinstallFunc) - { - #region 删除数据库(演示) - - var mySenparcEntitiesType = this.TryGetXncfDatabaseDbContextType; - AIAgentsHubSenparcEntities mySenparcEntities = serviceProvider.GetService(mySenparcEntitiesType) as AIAgentsHubSenparcEntities; - - //指定需要删除的数据实体 - - //注意:这里作为演示,在卸载模块的时候删除了所有本模块创建的表,实际操作过程中,请谨慎操作,并且按照删除顺序对实体进行排序! - var dropTableKeys = EntitySetKeys.GetEntitySetInfo(this.TryGetXncfDatabaseDbContextType).Keys.ToArray(); - await base.DropTablesAsync(serviceProvider, mySenparcEntities, dropTableKeys); - - #endregion - await unsinstallFunc().ConfigureAwait(false); - } - #endregion - - public override IServiceCollection AddXncfModule(IServiceCollection services, IConfiguration configuration, IHostEnvironment env) - { - services.AddScoped(); - - services.AddAutoMapper(z => - { - z.CreateMap().ReverseMap(); - }); - return base.AddXncfModule(services, configuration, env); - } - } -} +using Senparc.Ncf.Core.Enums; +using Senparc.Ncf.XncfBase; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; + +using Senparc.Xncf.AIAgentsHub.Models; +using Senparc.Xncf.AIAgentsHub.OHS.Local.AppService; +using Senparc.Ncf.Core.Models; +using Senparc.Ncf.Database; +using Senparc.Ncf.XncfBase.Database; +using Senparc.Xncf.AIAgentsHub.Models.DatabaseModel.Dto; + +namespace Senparc.Xncf.AIAgentsHub +{ + [XncfRegister] + public partial class Register : XncfRegisterBase, IXncfRegister + { + #region IXncfRegister 接口 + + public override string Name => "Senparc.Xncf.AIAgentsHub"; + + public override string Uid => "49EFBD38-B093-46B1-9297-31FF8E6E8B29";//It must be globally unique and must be fixed after generation. It has been automatically generated and can also be modified by yourself. + + public override string Version => "0.1.0";//Version number is required + + public override string MenuName => "AI Agent Hub"; + + public override string Icon => "fa fa-robot"; + + public override string Description => "Artificial Intelligence Agent Hub Module"; + + public override async Task InstallOrUpdateAsync(IServiceProvider serviceProvider, InstallOrUpdate installOrUpdate) + { + //Update database when installing or upgrading a version + await XncfDatabaseDbContext.MigrateOnInstallAsync(serviceProvider, this); + + //Execute logic based on different conditions for installation or update + switch (installOrUpdate) + { + case InstallOrUpdate.Install: + //New installation + #region Initialize database data + var colorService= serviceProvider.GetService(); + var colorResult = await colorService.GetOrInitColorAsync(); + #endregion + break; + case InstallOrUpdate.Update: + //renew + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override async Task UninstallAsync(IServiceProvider serviceProvider, Func unsinstallFunc) + { + #region Delete database (demo) + + var mySenparcEntitiesType= this.TryGetXncfDatabaseDbContextType; + AIAgentsHubSenparcEntities mySenparcEntities = serviceProvider.GetService(mySenparcEntitiesType) as AIAgentsHubSenparcEntities; + + //Specify the data entity to be deleted + + //Note: As a demonstration, all tables created by this module are deleted when uninstalling the module. During actual operation, please operate with caution and sort the entities in the order of deletion! + var dropTableKeys = EntitySetKeys.GetEntitySetInfo(this.TryGetXncfDatabaseDbContextType).Keys.ToArray(); + await base.DropTablesAsync(serviceProvider, mySenparcEntities, dropTableKeys); + + #endregion + await unsinstallFunc().ConfigureAwait(false); + } + #endregion + + public override IServiceCollection AddXncfModule(IServiceCollection services, IConfiguration configuration, IHostEnvironment env) + { + services.AddScoped(); + + services.AddAutoMapper(z => + { + z.CreateMap().ReverseMap(); + }); + return base.AddXncfModule(services, configuration, env); + } + } +} diff --git a/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIKernel/Index.cshtml b/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIKernel/Index.cshtml index 51fae8409..864770f98 100644 --- a/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIKernel/Index.cshtml +++ b/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIKernel/Index.cshtml @@ -1,295 +1,295 @@ -@page -@model Senparc.Xncf.AIKernel.Areas.AIKernel.Pages.Index -@{ - ViewData["Title"] = "AIKernel 首页"; - Layout = "_Layout_Vue"; -} - -@section Style { - -} - -@section breadcrumbs { - 扩展模块 - AI 核心模块 - 首页 -} - -
-
- 添加 - 导入 NeuCharAI 云端模型算力 -
- - - @* alias *@ - - - @* *@ - - - - - - - - - @* apiKey: only show options of copy *@ - - - - - - - - @* 操作 *@ - - - - -
- - -
- - - @* dialog for 添加模型 *@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @* ,[ApiVersion] *@ - - - - @* ,[ApiKey] *@ - - - - - - - - - - - - - @* *@ - @* $1$ use switch #1# *@ - @* *@ - @* *@ - - - - - - 取 消 - 确 定 - - - @* dialog for 编辑模型 *@ - - - - - - - @* NeuCharAI = 4, *@ - @* OpenAI = 8, *@ - @* AzureOpenAI = 16, *@ - @* HuggingFace = 32 *@ - @* FastAPI = 128 *@ - @* Ollama = 256 *@ - @* DeepSeek = 512 *@ - @* *@ - @* use el-select *@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @* ,[ApiVersion] *@ - - - - @* ,[ApiKey] *@ - - - - - - - - - - - - - - @* use switch *@ - - - @* ,[MaxToken] *@ - @* ,[AdminRemark] *@ - @* ,[Remark] *@ - - - - - - 取 消 - 确 定 - - - - - - - - - - - - -

   可从 https://www.neuchar.com/Developer/AiApp 页面中,点击【显示 ApiKey】按钮查看到 DeveloperId 及 ApiKey,如果您有多项,可分批次导入。

-
- - 取消 - 确定 - -
- -
- -@section scripts{ - +@page +@model Senparc.Xncf.AIKernel.Areas.AIKernel.Pages.Index +@{ + ViewData["Title"] = "AIKernel 首页"; + Layout = "_Layout_Vue"; +} + +@section Style { + +} + +@section breadcrumbs { + 扩展模块 + AI 核心模块 + 首页 +} + +
+
+ 添加 + 导入 NeuCharAI 云端模型算力 +
+ + + @* alias *@ + + + @* *@ + + + + + + + + + @* apiKey: only show options of copy *@ + + + + + + + + @* operate*@ + + + + +
+ + +
+ + + @* dialog for Add model*@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @* ,[ApiVersion] *@ + + + + @* ,[ApiKey] *@ + + + + + + + + + + + + + @* *@ + @* $1$ use switch #1# *@ + @* *@ + @* *@ + + + + + + 取 消 + 确 定 + + + @* dialog for Edit model*@ + + + + + + + @* NeuCharAI = 4, *@ + @* OpenAI = 8, *@ + @* AzureOpenAI = 16, *@ + @* HuggingFace = 32 *@ + @* FastAPI = 128 *@ + @* Ollama = 256 *@ + @* DeepSeek = 512 *@ + @* *@ + @* use el-select *@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @* ,[ApiVersion] *@ + + + + @* ,[ApiKey] *@ + + + + + + + + + + + + + + @* use switch *@ + + + @* ,[MaxToken] *@ + @* ,[AdminRemark] *@ + @* ,[Remark] *@ + + + + + + 取 消 + 确 定 + + + + + + + + + + + + +

   可从 https://www.neuchar.com/Developer/AiApp On the page, click the [Show ApiKey] button to view the DeveloperId and ApiKey. If you have multiple items, you can import them in batches.

+
+ + 取消 + 确定 + +
+ +
+ +@section scripts{ + } \ No newline at end of file diff --git a/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIVector/Index.cshtml b/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIVector/Index.cshtml index 94a7e5d5f..7a678f47f 100644 --- a/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIVector/Index.cshtml +++ b/src/Extensions/Senparc.Xncf.AIKernel/Areas/Admin/Pages/AIVector/Index.cshtml @@ -54,7 +54,7 @@ {{ dateformatter(scope.row.addTime) }} - @* 操作 *@ + @* operate*@