From a8890c81e986639c642bdbf72531062ee4ff0493 Mon Sep 17 00:00:00 2001 From: yanpeng200212 <32453@DESKTOP-HTJ2T0B> Date: Thu, 22 Oct 2026 10:30:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=B4=A8=E9=87=8F=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=EF=BC=88=E5=B7=A5=E8=89=BA=E6=89=A7=E8=A1=8C=E4=B9=A6?= =?UTF-8?q?=20/=20=E6=A0=B7=E8=8C=A7=E8=AF=95=E7=BC=AB=EF=BC=89=E4=B8=8E?= =?UTF-8?q?=E6=8A=A5=E8=A1=A8=E4=B8=AD=E5=BF=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 质量模块: - 11 张工艺执行书表:封面、试产、选剥茧(含混茧明细)、试缫与大样、煮茧(含分车间配温、副煮车间)、自动缫(含大样)、后缫 - 10 张样茧试缫表:原料小样/综合样主表 + 选茧、解舒、茧幅、切剖、清洁洁净、茧丝百回纤度 - 服务与接口、主控指针回写(列表 √ 标记)、封面编号自增、报表数据包 - 前端 8 张报表打印版式、7 类工艺单据与 6 类调查录入弹窗、字段驱动表单/表格组件 - 菜单种子新增「质量」目录,样茧子表增加 YjssType 判别列,避免原料小样与综合样自增 Id 重号串数据 报表中心: - 报表模板管理、锐浪 grhtml5 加载与预览、全屏设计面板、数据源 Provider(落丝记录、职工花名册) 其他:同步工作进度 1-11 文档、开发数据库快照、.gitignore 忽略运行日志目录 --- .gitignore | 1 + .../Controllers/QC/GyzxsController.cs | 134 +++ .../Controllers/QC/YjssController.cs | 139 +++ .../Controllers/SysReportController.cs | 351 ++++++++ backend/F9WebAiTest.Api/Program.cs | 7 +- .../F9WebAiTest.Api/f9_web_ai_test_rbac.db | Bin 1167360 -> 1503232 bytes .../Commands/RolePermission_AssignCommand.cs | 11 + .../Queries/GetEmployeeDirectoryRepQuery.cs | 62 ++ .../SYS/Reports/Queries/GetLsjlRepQuery.cs | 149 ++++ .../DependencyInjection.cs | 15 + .../Dtos/QC/GyzxsDtos.cs | 62 ++ .../Dtos/QC/YjssDtos.cs | 60 ++ .../Dtos/SYS/Reports/ReportDataSourceDto.cs | 21 + .../Dtos/SYS/Reports/ReportDtos.cs | 82 ++ .../Dtos/SYS/Reports/ReportScaffoldDtos.cs | 55 ++ .../F9WebAiTest.Application.csproj | 1 + .../Interfaces/Reports/IReportDataProvider.cs | 22 + .../Services/QC/GyzxsService.cs | 760 +++++++++++++++++ .../Services/QC/YjssService.cs | 551 ++++++++++++ .../Services/Reports/IReportService.cs | 41 + .../Reports/Providers/RepDt_Prod_Lsjl.cs | 47 ++ .../Reports/Providers/RepDt_Sys_Users.cs | 31 + .../Services/Reports/ReportService.cs | 652 ++++++++++++++ .../Entities/QC/Gyzxs/GyzxsDocVersion.cs | 25 + .../Entities/QC/Gyzxs/GyzxsFengMian.cs | 23 + .../Entities/QC/Gyzxs/GyzxsHs.cs | 57 ++ .../Entities/QC/Gyzxs/GyzxsShichan.cs | 65 ++ .../Entities/QC/Gyzxs/GyzxsXbj.cs | 61 ++ .../Entities/QC/Gyzxs/GyzxsXbjSsDy.cs | 45 + .../Entities/QC/Gyzxs/GyzxsZds.cs | 112 +++ .../Entities/QC/Gyzxs/GyzxsZhuJian.cs | 98 +++ .../Entities/QC/Yjss/YjssDcJfdc.cs | 28 + .../Entities/QC/Yjss/YjssDcJsbhxd.cs | 33 + .../Entities/QC/Yjss/YjssDcJsdc.cs | 72 ++ .../Entities/QC/Yjss/YjssDcQjjj.cs | 50 ++ .../Entities/QC/Yjss/YjssDcQpdc.cs | 29 + .../Entities/QC/Yjss/YjssDcXjdc.cs | 45 + .../Entities/QC/Yjss/YjssMain.cs | 41 + .../Entities/SYS/Reports/Report.cs | 130 +++ .../Entities/SYS/Reports/ReportFile.cs | 35 + .../SYS/Reports/Report_ChangedDomainEvent.cs | 6 + .../ReportTemplates/employee_directory.grf | 376 +++++++++ .../Assets/ReportTemplates/lsjl.grf | 312 +++++++ .../Data/DbSeeder.cs | 467 ++++++++++- .../F9WebAiTest.Infrastructure.csproj | 2 + .../Departments/DepartmentWriteRepository.cs | 6 +- start.bat | 9 +- web/public/grlib/grhtml5-7-min.js | 9 + web/src/components/report/F9ParamControl.vue | 294 +++++++ web/src/components/report/ReportDesign.vue | 793 ++++++++++++++++++ web/src/components/report/ReportEditor.vue | 768 +++++++++++++++++ .../select/CraftZhuangKouSelect.vue | 52 ++ web/src/components/select/F9SelectPage.vue | 190 +++++ web/src/components/select/index.js | 39 + web/src/router/index.js | 6 +- web/src/utils/departments.js | 233 +++++ .../utils/gridreport/controls/antdControls.js | 99 +++ .../utils/gridreport/controls/f9Controls.js | 441 ++++++++++ web/src/utils/gridreport/controls/index.js | 83 ++ web/src/utils/gridreport/controls/registry.js | 203 +++++ web/src/utils/gridreport/grwebapp.js | 504 +++++++++++ web/src/utils/gridreport/loadGrhtml5.js | 36 + web/src/utils/gridreport/reportConfig.js | 93 ++ web/src/utils/request.js | 4 + web/src/views/prod/lsjl/index.vue | 34 +- web/src/views/qc/components/QcGrid.vue | 82 ++ web/src/views/qc/components/SchemaForm.vue | 83 ++ web/src/views/qc/gyzxs/DocDialog.vue | 249 ++++++ web/src/views/qc/gyzxs/PrintDialog.vue | 660 +++++++++++++++ web/src/views/qc/gyzxs/index.vue | 156 ++++ web/src/views/qc/gyzxs/schema.js | 388 +++++++++ web/src/views/qc/yjss/YjssDocDialog.vue | 364 ++++++++ web/src/views/qc/yjss/YjssPrintDialog.vue | 290 +++++++ web/src/views/qc/yjss/index.vue | 268 ++++++ web/src/views/qc/yjss/schema.js | 206 +++++ web/src/views/report/center/index.vue | 363 ++++++++ web/src/views/report/manage/index.vue | 188 +++++ web/src/views/sys/department/index.vue | 245 ++++-- web/src/views/sys/role/index.vue | 47 +- 工作进度/工作进度-1.md | 337 ++++++++ 工作进度/工作进度-10.md | 69 ++ 工作进度/工作进度-11.md | 152 ++++ 工作进度/工作进度-2.md | 167 ++++ 工作进度/工作进度-3.md | 207 +++++ 工作进度/工作进度-4.md | 271 ++++++ 工作进度/工作进度-5.md | 189 +++++ 工作进度/工作进度-6.md | 177 ++++ 工作进度/工作进度-7.md | 167 ++++ 工作进度/工作进度-8.md | 133 +++ 工作进度/工作进度-9.md | 135 +++ 消息推送系统_迁移指南.docx | Bin 54324 -> 0 bytes 消息推送系统与RBAC审批设计说明书.docx | Bin 36389 -> 0 bytes 92 files changed, 14785 insertions(+), 70 deletions(-) create mode 100644 backend/F9WebAiTest.Api/Controllers/QC/GyzxsController.cs create mode 100644 backend/F9WebAiTest.Api/Controllers/QC/YjssController.cs create mode 100644 backend/F9WebAiTest.Api/Controllers/SysReportController.cs create mode 100644 backend/F9WebAiTest.Application/CQRS/SYS/Reports/Queries/GetEmployeeDirectoryRepQuery.cs create mode 100644 backend/F9WebAiTest.Application/CQRS/SYS/Reports/Queries/GetLsjlRepQuery.cs create mode 100644 backend/F9WebAiTest.Application/Dtos/QC/GyzxsDtos.cs create mode 100644 backend/F9WebAiTest.Application/Dtos/QC/YjssDtos.cs create mode 100644 backend/F9WebAiTest.Application/Dtos/SYS/Reports/ReportDataSourceDto.cs create mode 100644 backend/F9WebAiTest.Application/Dtos/SYS/Reports/ReportDtos.cs create mode 100644 backend/F9WebAiTest.Application/Dtos/SYS/Reports/ReportScaffoldDtos.cs create mode 100644 backend/F9WebAiTest.Application/Interfaces/Reports/IReportDataProvider.cs create mode 100644 backend/F9WebAiTest.Application/Services/QC/GyzxsService.cs create mode 100644 backend/F9WebAiTest.Application/Services/QC/YjssService.cs create mode 100644 backend/F9WebAiTest.Application/Services/Reports/IReportService.cs create mode 100644 backend/F9WebAiTest.Application/Services/Reports/Providers/RepDt_Prod_Lsjl.cs create mode 100644 backend/F9WebAiTest.Application/Services/Reports/Providers/RepDt_Sys_Users.cs create mode 100644 backend/F9WebAiTest.Application/Services/Reports/ReportService.cs create mode 100644 backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsDocVersion.cs create mode 100644 backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsFengMian.cs create mode 100644 backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsHs.cs create mode 100644 backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsShichan.cs create mode 100644 backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsXbj.cs create mode 100644 backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsXbjSsDy.cs create mode 100644 backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsZds.cs create mode 100644 backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsZhuJian.cs create mode 100644 backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcJfdc.cs create mode 100644 backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcJsbhxd.cs create mode 100644 backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcJsdc.cs create mode 100644 backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcQjjj.cs create mode 100644 backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcQpdc.cs create mode 100644 backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcXjdc.cs create mode 100644 backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssMain.cs create mode 100644 backend/F9WebAiTest.Domain/Entities/SYS/Reports/Report.cs create mode 100644 backend/F9WebAiTest.Domain/Entities/SYS/Reports/ReportFile.cs create mode 100644 backend/F9WebAiTest.Domain/Events/SYS/Reports/Report_ChangedDomainEvent.cs create mode 100644 backend/F9WebAiTest.Infrastructure/Assets/ReportTemplates/employee_directory.grf create mode 100644 backend/F9WebAiTest.Infrastructure/Assets/ReportTemplates/lsjl.grf create mode 100644 web/public/grlib/grhtml5-7-min.js create mode 100644 web/src/components/report/F9ParamControl.vue create mode 100644 web/src/components/report/ReportDesign.vue create mode 100644 web/src/components/report/ReportEditor.vue create mode 100644 web/src/components/select/CraftZhuangKouSelect.vue create mode 100644 web/src/components/select/F9SelectPage.vue create mode 100644 web/src/components/select/index.js create mode 100644 web/src/utils/departments.js create mode 100644 web/src/utils/gridreport/controls/antdControls.js create mode 100644 web/src/utils/gridreport/controls/f9Controls.js create mode 100644 web/src/utils/gridreport/controls/index.js create mode 100644 web/src/utils/gridreport/controls/registry.js create mode 100644 web/src/utils/gridreport/grwebapp.js create mode 100644 web/src/utils/gridreport/loadGrhtml5.js create mode 100644 web/src/utils/gridreport/reportConfig.js create mode 100644 web/src/views/qc/components/QcGrid.vue create mode 100644 web/src/views/qc/components/SchemaForm.vue create mode 100644 web/src/views/qc/gyzxs/DocDialog.vue create mode 100644 web/src/views/qc/gyzxs/PrintDialog.vue create mode 100644 web/src/views/qc/gyzxs/index.vue create mode 100644 web/src/views/qc/gyzxs/schema.js create mode 100644 web/src/views/qc/yjss/YjssDocDialog.vue create mode 100644 web/src/views/qc/yjss/YjssPrintDialog.vue create mode 100644 web/src/views/qc/yjss/index.vue create mode 100644 web/src/views/qc/yjss/schema.js create mode 100644 web/src/views/report/center/index.vue create mode 100644 web/src/views/report/manage/index.vue create mode 100644 工作进度/工作进度-1.md create mode 100644 工作进度/工作进度-10.md create mode 100644 工作进度/工作进度-11.md create mode 100644 工作进度/工作进度-2.md create mode 100644 工作进度/工作进度-3.md create mode 100644 工作进度/工作进度-4.md create mode 100644 工作进度/工作进度-5.md create mode 100644 工作进度/工作进度-6.md create mode 100644 工作进度/工作进度-7.md create mode 100644 工作进度/工作进度-8.md create mode 100644 工作进度/工作进度-9.md delete mode 100644 消息推送系统_迁移指南.docx delete mode 100644 消息推送系统与RBAC审批设计说明书.docx diff --git a/.gitignore b/.gitignore index 0f4d799..9ee5486 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ publish/ # 运行时临时文件 / 日志 *.log +.run-logs/ _runlog/ _tmp_hubtest/ _rename_backup/ diff --git a/backend/F9WebAiTest.Api/Controllers/QC/GyzxsController.cs b/backend/F9WebAiTest.Api/Controllers/QC/GyzxsController.cs new file mode 100644 index 0000000..a264e98 --- /dev/null +++ b/backend/F9WebAiTest.Api/Controllers/QC/GyzxsController.cs @@ -0,0 +1,134 @@ +using F9WebAiTest.Application.Common; +using F9WebAiTest.Application.Dtos.QC; +using F9WebAiTest.Application.Services.QC; +using F9WebAiTest.Domain.Entities.QC.Gyzxs; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace F9WebAiTest.Api.Controllers.QC; + +/// +/// 质量模块 —— 工艺执行书(对应 F9 生产\质量\工艺执行书)。 +/// 主列表按「庄口 + 年度」展示,每张子单据一个入口;保存子单据会联动主控表。 +/// +[ApiController] +[Route("api/qc/gyzxs")] +[Authorize] +public class GyzxsController : ControllerBase +{ + private readonly IGyzxsService _svc; + public GyzxsController(IGyzxsService svc) => _svc = svc; + + // ---------- 主列表 ---------- + [HttpGet] + public async Task GetPaged([FromQuery] int pageIndex = 1, [FromQuery] int pageSize = 20, + [FromQuery] int year = 0, [FromQuery] string keyword = "") + => Ok(ApiResult>.Ok(await _svc.GetDocVersionPagedAsync(pageIndex, pageSize, year, keyword))); + + [HttpPost("version")] + public async Task CreateVersion([FromQuery] long gyzkId) + { + try { return Ok(ApiResult.Ok(await _svc.CreateDocVersionAsync(gyzkId), "创建成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + [HttpDelete("version/{id}")] + public async Task DeleteVersion(long id) + { + try { await _svc.DeleteDocVersionAsync(id); return Ok(ApiResult.Ok(null, "删除成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + // ---------- 封面 ---------- + [HttpGet("fengmian")] + public async Task GetFengMian([FromQuery] long gyzkId) + => Ok(ApiResult.Ok(await _svc.GetFengMianAsync(gyzkId))); + + [HttpPost("fengmian")] + public async Task SaveFengMian([FromBody] GyzxsFengMian dto) + { + try { return Ok(ApiResult.Ok(await _svc.SaveFengMianAsync(dto), "保存成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + // ---------- 试产(试缫工艺单) ---------- + [HttpGet("shichan")] + public async Task GetShichan([FromQuery] long gyzkId, [FromQuery] string guige = "") + => Ok(ApiResult.Ok(await _svc.GetShichanAsync(gyzkId, guige))); + + [HttpPost("shichan")] + public async Task SaveShichan([FromBody] GyzxsShichan dto) + { + try { return Ok(ApiResult.Ok(await _svc.SaveShichanAsync(dto), "保存成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + // ---------- 混剥选茧 ---------- + [HttpGet("xbj")] + public async Task GetXbj([FromQuery] long gyzkId) + => Ok(ApiResult.Ok(await _svc.GetXbjAsync(gyzkId))); + + [HttpPost("xbj")] + public async Task SaveXbj([FromBody] GyzxsXbjDto dto) + { + try { return Ok(ApiResult.Ok(await _svc.SaveXbjAsync(dto), "保存成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + // ---------- 试缫与大样 ---------- + [HttpGet("ssdy")] + public async Task GetXbjSsDy([FromQuery] long gyzkId, [FromQuery] string guige = "") + => Ok(ApiResult.Ok(await _svc.GetXbjSsDyAsync(gyzkId, guige))); + + [HttpPost("ssdy")] + public async Task SaveXbjSsDy([FromBody] GyzxsXbjSsDy dto) + { + try { return Ok(ApiResult.Ok(await _svc.SaveXbjSsDyAsync(dto), "保存成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + // ---------- 煮茧 ---------- + [HttpGet("zhujian")] + public async Task GetZhuJian([FromQuery] long gyzkId) + => Ok(ApiResult.Ok(await _svc.GetZhuJianAsync(gyzkId))); + + [HttpPost("zhujian")] + public async Task SaveZhuJian([FromBody] GyzxsZhuJianDto dto) + { + try { return Ok(ApiResult.Ok(await _svc.SaveZhuJianAsync(dto), "保存成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + // ---------- 自动缫 ---------- + [HttpGet("zds")] + public async Task GetZds([FromQuery] long gyzkId, [FromQuery] string guige = "") + => Ok(ApiResult.Ok(await _svc.GetZdsAsync(gyzkId, guige))); + + [HttpPost("zds")] + public async Task SaveZds([FromBody] GyzxsZdsDto dto) + { + try { return Ok(ApiResult.Ok(await _svc.SaveZdsAsync(dto), "保存成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + // ---------- 后缫(工艺执行书) ---------- + [HttpGet("hs")] + public async Task GetHs([FromQuery] long gyzkId, [FromQuery] string guige = "") + => Ok(ApiResult.Ok(await _svc.GetHsAsync(gyzkId, guige))); + + [HttpPost("hs")] + public async Task SaveHs([FromBody] GyzxsHs dto) + { + try { return Ok(ApiResult.Ok(await _svc.SaveHsAsync(dto), "保存成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + // ---------- 报表数据 ---------- + /// 报表数据(type: fengmian / gyzxs / xbj / shichan / ssdy / zhujian / zds / zdsdy) + [HttpGet("print/{type}")] + public async Task GetPrintData(string type, [FromQuery] long gyzkId, [FromQuery] string guige = "") + { + try { return Ok(ApiResult.Ok(await _svc.GetPrintDataAsync(type, gyzkId, guige))); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } +} diff --git a/backend/F9WebAiTest.Api/Controllers/QC/YjssController.cs b/backend/F9WebAiTest.Api/Controllers/QC/YjssController.cs new file mode 100644 index 0000000..ceb5670 --- /dev/null +++ b/backend/F9WebAiTest.Api/Controllers/QC/YjssController.cs @@ -0,0 +1,139 @@ +using F9WebAiTest.Application.Common; +using F9WebAiTest.Application.Dtos.QC; +using F9WebAiTest.Application.Services.QC; +using F9WebAiTest.Domain.Entities.QC.Yjss; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace F9WebAiTest.Api.Controllers.QC; + +/// +/// 质量模块 —— 样茧试缫(对应 F9 生产\质量\样茧试缫)。 +/// type: 1=原料小样 2=综合样。 +/// +[ApiController] +[Route("api/qc/yjss")] +[Authorize] +public class YjssController : ControllerBase +{ + private readonly IYjssService _svc; + public YjssController(IYjssService svc) => _svc = svc; + + // ---------- 主列表 / 主表 ---------- + [HttpGet] + public async Task GetPaged([FromQuery] int pageIndex = 1, [FromQuery] int pageSize = 20, + [FromQuery] int type = 1, [FromQuery] int year = 0, [FromQuery] string keyword = "") + => Ok(ApiResult>.Ok(await _svc.GetPagedAsync(pageIndex, pageSize, type, year, keyword))); + + [HttpPost("ylxy")] + public async Task SaveYlxy([FromBody] YjssMainYlxy dto) + { + try { return Ok(ApiResult.Ok(await _svc.SaveYlxyAsync(dto), "保存成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + [HttpPost("zhy")] + public async Task SaveZhy([FromBody] YjssMainZhy dto) + { + try { return Ok(ApiResult.Ok(await _svc.SaveZhyAsync(dto), "保存成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + [HttpDelete("{type}/{id}")] + public async Task Delete(int type, long id) + { + try { await _svc.DeleteAsync(type, id); return Ok(ApiResult.Ok(null, "删除成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + // ---------- 选茧调查 ---------- + [HttpGet("xjdc/{yjssId}")] + public async Task GetXjdc(long yjssId, [FromQuery] int type = 1) + => Ok(ApiResult.Ok(await _svc.GetXjdcAsync(type, yjssId))); + + [HttpPost("xjdc")] + public async Task SaveXjdc([FromQuery] int type, [FromBody] YjssDcXjdc dto) + { + try { return Ok(ApiResult.Ok(await _svc.SaveXjdcAsync(type, dto), "保存成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + // ---------- 解舒调查 ---------- + [HttpGet("jsdc/{yjssId}")] + public async Task GetJsdc(long yjssId, [FromQuery] int type = 1) + => Ok(ApiResult>.Ok(await _svc.GetJsdcAsync(type, yjssId))); + + [HttpPost("jsdc")] + public async Task SaveJsdc([FromQuery] int type, [FromBody] YjssDcJsdc dto) + { + try { return Ok(ApiResult.Ok(await _svc.SaveJsdcAsync(type, dto), "保存成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + [HttpDelete("jsdc/{type}/{id}")] + public async Task DeleteJsdc(int type, long id) + { + try { await _svc.DeleteJsdcAsync(type, id); return Ok(ApiResult.Ok(null, "删除成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + // ---------- 茧幅调查 ---------- + [HttpGet("jfdc/{yjssId}")] + public async Task GetJfdc(long yjssId, [FromQuery] int type = 1) + => Ok(ApiResult.Ok(await _svc.GetJfdcAsync(type, yjssId))); + + [HttpPost("jfdc")] + public async Task SaveJfdc([FromQuery] int type, [FromBody] YjssJfdcDto dto) + { + try { return Ok(ApiResult.Ok(await _svc.SaveJfdcAsync(type, dto), "保存成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + // ---------- 切剖调查 ---------- + [HttpGet("qpdc/{yjssId}")] + public async Task GetQpdc(long yjssId, [FromQuery] int type = 1) + => Ok(ApiResult.Ok(await _svc.GetQpdcAsync(type, yjssId))); + + [HttpPost("qpdc")] + public async Task SaveQpdc([FromQuery] int type, [FromBody] YjssDcQpdc dto) + { + try { return Ok(ApiResult.Ok(await _svc.SaveQpdcAsync(type, dto), "保存成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + // ---------- 清洁洁净 ---------- + [HttpGet("qjjj/{yjssId}")] + public async Task GetQjjj(long yjssId, [FromQuery] int type = 1) + => Ok(ApiResult.Ok(await _svc.GetQjjjAsync(type, yjssId))); + + [HttpPost("qjjj")] + public async Task SaveQjjj([FromQuery] int type, [FromBody] YjssDcQjjj dto) + { + try { return Ok(ApiResult.Ok(await _svc.SaveQjjjAsync(type, dto), "保存成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + // ---------- 茧丝百回纤度 ---------- + [HttpGet("jsbhxd/{yjssId}")] + public async Task GetJsbhxd(long yjssId, [FromQuery] int type = 1) + => Ok(ApiResult.Ok(await _svc.GetJsbhxdAsync(type, yjssId))); + + [HttpPost("jsbhxd")] + public async Task SaveJsbhxd([FromQuery] int type, [FromBody] YjssJsbhxdDto dto) + { + try { return Ok(ApiResult.Ok(await _svc.SaveJsbhxdAsync(type, dto), "保存成功")); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + // ---------- 整页 / 报表 ---------- + [HttpGet("full/{type}/{id}")] + public async Task GetFull(int type, long id) + { + try { return Ok(ApiResult.Ok(await _svc.GetFullAsync(type, id))); } + catch (ServiceException ex) { return StatusCode(ex.Code, ApiResult.Fail(ex.Message, ex.Code)); } + } + + [HttpGet("jsdc-summary/{yjssId}")] + public async Task GetJsdcSummary(long yjssId, [FromQuery] int type = 1) + => Ok(ApiResult>.Ok(await _svc.GetJsdcSummaryAsync(type, yjssId))); +} diff --git a/backend/F9WebAiTest.Api/Controllers/SysReportController.cs b/backend/F9WebAiTest.Api/Controllers/SysReportController.cs new file mode 100644 index 0000000..527cd11 --- /dev/null +++ b/backend/F9WebAiTest.Api/Controllers/SysReportController.cs @@ -0,0 +1,351 @@ +using System.Security.Claims; +using F9WebAiTest.Application.Common; +using F9WebAiTest.Application.Common.Exceptions; +using F9WebAiTest.Application.Dtos.SYS.Reports; +using F9WebAiTest.Application.Services.Auth; +using F9WebAiTest.Application.Services.Reports; +using F9WebAiTest.Domain.Entities.SYS.RBAC; +using F9WebAiTest.Domain.Enums.SYS.RBAC; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace F9WebAiTest.Api.Controllers; + +/// 报表中心:报表定义维护 + 模板下载 + 数据查询(Grid++Report 7 Web 嵌入) +[ApiController] +[Route("api/sys/report")] +[Authorize] +public class SysReportController : ControllerBase +{ + private readonly IReportService _reportService; + private readonly IFreeSql _fsql; + private readonly IJwtService _jwt; + private readonly IConfiguration _config; + + public SysReportController(IReportService reportService, IFreeSql fsql, IJwtService jwt, IConfiguration config) + { + _reportService = reportService; + _fsql = fsql; + _jwt = jwt; + _config = config; + } + + // ==================== 报表管理 ==================== + + [HttpGet] + public async Task GetPaged( + [FromQuery] string like = "", + [FromQuery] string category = "", + [FromQuery] bool? isActive = null, + [FromQuery] int pageIndex = 1, + [FromQuery] int pageSize = 50) + { + var data = await _reportService.GetPagedAsync(like ?? "", category ?? "", isActive, pageIndex, pageSize); + return Ok(ApiResult.Ok(data)); + } + + [HttpGet("all")] + public async Task GetAllActive() + => Ok(ApiResult.Ok(await _reportService.GetAllActiveAsync())); + + [HttpGet("{id}")] + public async Task GetById(long id) + { + try { return Ok(ApiResult.Ok(await _reportService.GetByIdAsync(id))); } + catch (F9NotFoundException ex) { return NotFound(ApiResult.Fail(ex.Message, 404)); } + } + + [HttpPost] + public async Task Create([FromBody] ReportCreateDto dto) + { + try { return Ok(ApiResult.Ok(new { id = await _reportService.CreateAsync(dto) }, "新增成功")); } + catch (F9OperationException ex) { return BadRequest(ApiResult.Fail(ex.Message, 400)); } + } + + [HttpPut("{id}")] + public async Task Update(long id, [FromBody] ReportUpdateDto dto) + { + try { await _reportService.UpdateAsync(id, dto); return Ok(ApiResult.Ok(new { id }, "修改成功")); } + catch (F9NotFoundException ex) { return NotFound(ApiResult.Fail(ex.Message, 404)); } + catch (F9OperationException ex) { return BadRequest(ApiResult.Fail(ex.Message, 400)); } + } + + [HttpDelete("{id}")] + public async Task Delete(long id) + { + try { await _reportService.DeleteAsync(id); return Ok(ApiResult.Ok(new { id }, "删除成功")); } + catch (F9NotFoundException ex) { return NotFound(ApiResult.Fail(ex.Message, 404)); } + } + + /// 上传 .grf 模板文件。 + /// 常规编辑流程已改为「模板随报表一起保存」(POST/PUT 携带 templateJson), + /// 本接口保留给 f9designer:// 本机设计器协议回传使用。 + [HttpPost("{id}/template")] + [RequestSizeLimit(50 * 1024 * 1024)] + public async Task UploadTemplate(long id, IFormFile file) + { + if (file == null || file.Length == 0) return BadRequest(ApiResult.Fail("请选择要上传的 .grf 模板文件", 400)); + if (!file.FileName.EndsWith(".grf", StringComparison.OrdinalIgnoreCase)) + return BadRequest(ApiResult.Fail("仅支持上传 .grf 模板文件", 400)); + try + { + using var ms = new MemoryStream(); + await file.CopyToAsync(ms); + await _reportService.UploadTemplateAsync(id, ms.ToArray(), file.FileName); + return Ok(ApiResult.Ok(new { size = file.Length }, "上传成功")); + } + catch (F9NotFoundException ex) { return NotFound(ApiResult.Fail(ex.Message, 404)); } + catch (F9OperationException ex) { return BadRequest(ApiResult.Fail(ex.Message, 400)); } + } + + /// + /// 直接保存模板文本(前端在线编辑「模板源码」后保存,免下载 / 上传 .grf)。 + /// 与上传接口的区别:这里提交的是文本(JSON 或 "_WR" 压缩文本),不是文件流。 + /// + [HttpPut("{id}/template/content")] + [RequestSizeLimit(50 * 1024 * 1024)] + public async Task SaveTemplateContent(long id, [FromBody] ReportTemplateContentDto dto) + { + try + { + var size = await _reportService.SaveTemplateContentAsync(id, dto?.Content ?? ""); + return Ok(ApiResult.Ok(new { id, size, hasTemplate = true }, "模板已保存")); + } + catch (F9NotFoundException ex) { return NotFound(ApiResult.Fail(ex.Message, 404)); } + catch (F9OperationException ex) { return BadRequest(ApiResult.Fail(ex.Message, 400)); } + } + + /// 下载 .grf 模板文件。保留给 f9designer:// 本机设计器协议(下载 → 编辑 → 自动回传)使用。 + [HttpGet("{id}/template/download")] + public async Task DownloadTemplate(long id) + { + var r = await _fsql.Select().Where(x => x.Id == id && !x.IsDeleted).FirstAsync(); + var content = r == null ? null : await _reportService.GetTemplateContentAsync(r); + if (string.IsNullOrEmpty(content)) + return NotFound(ApiResult.Fail("模板尚未上传", 404)); + return File(ToGrfBytes(content), "application/octet-stream", $"{r!.Code}.grf"); + } + + // ==================== 新增报表辅助:数据源 + 结构探测 ==================== + + /// 数据源清单(IReportDataProvider 注册表 + 各数据源声明的查询参数) + [HttpGet("datasources")] + public async Task GetDataSources() + => Ok(ApiResult.Ok(await _reportService.GetDataSourcesAsync())); + + /// + /// 探测数据源结构:按 DsName(或 DataSql)执行一次查询, + /// 返回可自动配置的查询参数 + 明细字段(列)+ 少量样本行。 + /// + [HttpPost("scaffold")] + public async Task Scaffold([FromBody] ReportProbeRequestDto dto) + { + var result = await _reportService.ProbeAsync(dto ?? new ReportProbeRequestDto()); + return Ok(ApiResult.Ok(result)); + } + + // ==================== 模板与数据公开端点(供 HTML5 渲染 / 客户端调用) ==================== + // grhtml5.insertReportViewer 的模板参数是普通 GET,浏览器无法附加 Authorization header, + // 故模板/数据接口接受 ?access_token=xxx 方式鉴权(仅内部网络使用,与 /hubs 相同的处理思路)。 + + /// 下载报表 .grf 模板(带权限校验,支持 query token 鉴权)。 + /// 额外暴露 template.json / template.grf 别名:Grid++ 的 JS 库与客户端对 URL 中不含 + /// ".grf/.txt/.xml/.json" 的请求会用 POST(取模板会误入上传接口)。 + [HttpGet("{id}/template")] + [HttpGet("{id}/template.json")] + [HttpGet("{id}/template.grf")] + [AllowAnonymous] + public async Task GetTemplate(long id, [FromQuery(Name = "access_token")] string? accessToken = null) + { + var (userId, isAdmin) = await ResolveCallerAsync(accessToken); + if (userId <= 0) return Unauthorized(new { code = 401, message = "未授权" }); + + var r = await _fsql.Select().Where(x => x.Id == id && !x.IsDeleted).FirstAsync(); + var content = r == null ? null : await _reportService.GetTemplateContentAsync(r); + if (string.IsNullOrEmpty(content)) + return NotFound(new { code = 404, message = "模板尚未上传" }); + if (!await CanAccessReportAsync(userId, isAdmin, r!)) return ForbidReport(); + + // 不输出 BOM:HTML5 渲染库按 { 或 _WR 前缀判断模板格式,BOM 会破坏该判断 + return File(System.Text.Encoding.UTF8.GetBytes(content!), "application/json; charset=utf-8", $"{r!.Code}.grf"); + } + + /// 执行报表数据查询(GET/POST 均可)。data.json 为 GET 别名(客户端 URL 方法判定约定) + [HttpGet("{id}/data")] + [HttpGet("{id}/data.json")] + [AllowAnonymous] + public async Task GetData(long id, [FromQuery] string? access_token = null, [FromQuery] string? params_ = null) + => await RunDataAsync(id, access_token, params_); + + [HttpPost("{id}/data")] + [AllowAnonymous] + public async Task PostData(long id, [FromQuery(Name = "access_token")] string? accessToken = null) + { + string? paramsJson = null; + try + { + Request.EnableBuffering(); + Request.Body.Position = 0; + using var reader = new StreamReader(Request.Body, leaveOpen: true); + var body = await reader.ReadToEndAsync(); + if (!string.IsNullOrEmpty(body)) paramsJson = body; + } + catch { /* 忽略读取错误 */ } + return await RunDataAsync(id, accessToken, paramsJson); + } + + private async Task RunDataAsync(long id, string? accessToken, string? paramsJson) + { + var (userId, isAdmin) = await ResolveCallerAsync(accessToken); + if (userId <= 0) return Unauthorized(new { code = 401, message = "未授权" }); + + var r = await _fsql.Select().Where(x => x.Id == id && !x.IsDeleted).FirstAsync(); + if (r == null) return NotFound(new { code = 404, message = "报表不存在" }); + if (!await CanAccessReportAsync(userId, isAdmin, r)) return ForbidReport(); + + var p = ParseParams(paramsJson); + try + { + var data = await _reportService.ExecuteDataAsync(r, p); + return Ok(data); + } + catch (F9OperationException ex) { return BadRequest(ApiResult.Fail(ex.Message, 400)); } + } + + private static System.Collections.Generic.Dictionary ParseParams(string? json) + { + var result = new System.Collections.Generic.Dictionary(StringComparer.Ordinal); + if (string.IsNullOrWhiteSpace(json)) return result; + try + { + using var doc = System.Text.Json.JsonDocument.Parse(json); + if (doc.RootElement.ValueKind == System.Text.Json.JsonValueKind.Object) + { + foreach (var prop in doc.RootElement.EnumerateObject()) + result[prop.Name] = System.Text.Json.JsonSerializer.Deserialize(prop.Value.GetRawText()); + } + else if (doc.RootElement.ValueKind == System.Text.Json.JsonValueKind.Array) + { + // 支持 [{name,value}] 形式(与 Grid++ WEB 客户端参数一致) + foreach (var el in doc.RootElement.EnumerateArray()) + { + if (el.TryGetProperty("name", out var n) && el.TryGetProperty("value", out var v)) + result[n.GetString() ?? ""] = System.Text.Json.JsonSerializer.Deserialize(v.GetRawText()); + } + } + } + catch { /* 忽略解析错误,返回空 */ } + return result; + } + + // ==================== 报表中心(用户视图) ==================== + + /// 当前用户可见的报表(按权限过滤) + [HttpGet("visible")] + public async Task GetVisible() + { + var userId = GetUserId(); + var isAdmin = await IsAdminAsync(userId); + var perms = await GetPermissionsAsync(userId, isAdmin); + return Ok(ApiResult.Ok(await _reportService.GetVisibleReportsAsync(userId, isAdmin, perms))); + } + + /// 当前用户预览某报表(带数据) + [HttpGet("{id}/preview")] + public async Task Preview(long id, [FromQuery] string? params_ = null) + { + var userId = GetUserId(); + var isAdmin = await IsAdminAsync(userId); + var perms = await GetPermissionsAsync(userId, isAdmin); + try + { + await _reportService.EnsureReportAccessAsync(userId, isAdmin, perms, id); + var r = await _fsql.Select().Where(x => x.Id == id && !x.IsDeleted).FirstAsync(); + if (r == null) return NotFound(ApiResult.Fail("报表不存在", 404)); + if (r.ReportFileId == 0) + return BadRequest(ApiResult.Fail("该报表尚未配置模板", 400)); + var p = ParseParams(params_); + var data = await _reportService.ExecuteDataAsync(r, p); + return Ok(ApiResult.Ok(new + { + report = new + { + r.Id, + r.Code, + r.Name, + r.Category, + r.Description, + config = r.Config, + hasTemplate = true + }, + templateUrl = $"/api/sys/report/{r.Id}/template.json", + data + })); + } + catch (F9NotFoundException ex) { return NotFound(ApiResult.Fail(ex.Message, 404)); } + catch (F9OperationException ex) { return BadRequest(ApiResult.Fail(ex.Message, 400)); } + } + + // ==================== 内部辅助 ==================== + + private long GetUserId() => long.Parse(User.FindFirst("id")!.Value); + + private async Task IsAdminAsync(long userId) + { + var roleIds = await _fsql.Select().Where(ur => ur.UserId == userId).ToListAsync(x => x.RoleId); + if (roleIds.Count == 0) return false; + return await _fsql.Select().Where(r => roleIds.Contains(r.Id) && r.Code == "admin").AnyAsync(); + } + + private async Task> GetPermissionsAsync(long userId, bool isAdmin) + { + if (isAdmin) + return new System.Collections.Generic.HashSet( + await _fsql.Select().Where(p => !p.IsDeleted && !string.IsNullOrEmpty(p.Code)).ToListAsync(p => p.Code)); + var roleIds = await _fsql.Select().Where(ur => ur.UserId == userId).ToListAsync(x => x.RoleId); + if (roleIds.Count == 0) return new(); + var permIds = await _fsql.Select().Where(rp => roleIds.Contains(rp.RoleId) && !rp.IsDeleted).ToListAsync(x => x.PermissionId); + if (permIds.Count == 0) return new(); + var codes = await _fsql.Select().Where(p => permIds.Contains(p.Id) && !p.IsDeleted && !string.IsNullOrEmpty(p.Code)).ToListAsync(p => p.Code); + return new System.Collections.Generic.HashSet(codes); + } + + /// 用 query 中的 access_token 解析用户;优先尝试从 Authorization 头解析;解析失败返回 0。 + private async Task<(long UserId, bool IsAdmin)> ResolveCallerAsync(string? accessToken) + { + // 优先用当前 HTTP 请求上下文(JWT 中间件已解析) + if (User?.Identity?.IsAuthenticated == true) + return (GetUserId(), await IsAdminAsync(GetUserId())); + + if (string.IsNullOrEmpty(accessToken)) return (0, false); + try + { + var (uid, _) = _jwt.ValidateAccessToken(accessToken); + return (uid, await IsAdminAsync(uid)); + } + catch { return (0, false); } + } + + private async Task CanAccessReportAsync(long userId, bool isAdmin, Domain.Entities.SYS.Reports.Report r) + { + if (isAdmin) return true; + if (!r.IsActive) return false; + var perms = await GetPermissionsAsync(userId, isAdmin); + return perms.Contains($"report:{r.Code}:visble"); + } + + private IActionResult ForbidReport() => StatusCode(403, new { code = 403, message = "无权访问该报表" }); + + /// 模板文本 → .grf 字节(带 UTF-8 BOM,与锐浪设计器保存格式一致) + private static byte[] ToGrfBytes(string content) + { + var text = content.Length > 0 && content[0] == '\uFEFF' ? content.Substring(1) : content; + var enc = new System.Text.UTF8Encoding(true); + var pre = enc.GetPreamble(); + var body = enc.GetBytes(text); + var data = new byte[pre.Length + body.Length]; + Buffer.BlockCopy(pre, 0, data, 0, pre.Length); + Buffer.BlockCopy(body, 0, data, pre.Length, body.Length); + return data; + } +} diff --git a/backend/F9WebAiTest.Api/Program.cs b/backend/F9WebAiTest.Api/Program.cs index dbd2c5a..42bf33f 100644 --- a/backend/F9WebAiTest.Api/Program.cs +++ b/backend/F9WebAiTest.Api/Program.cs @@ -33,13 +33,16 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) ValidAudience = builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)) }; - // SignalR 通过 query string 传递 token,需从 access_token 读取 + // SignalR 与报表模板/数据下载通过 query string 传递 token options.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"]; - if (!string.IsNullOrEmpty(accessToken) && context.HttpContext.Request.Path.StartsWithSegments("/hubs")) + if (string.IsNullOrEmpty(accessToken)) return Task.CompletedTask; + var path = context.HttpContext.Request.Path; + if (path.StartsWithSegments("/hubs") + || path.StartsWithSegments("/api/sys/report")) { context.Token = accessToken; } diff --git a/backend/F9WebAiTest.Api/f9_web_ai_test_rbac.db b/backend/F9WebAiTest.Api/f9_web_ai_test_rbac.db index edf44f266a76a8f536c71185c51a8e504a4cbc3a..367ff92079876731f7a940e3d35353eba472bde5 100644 GIT binary patch delta 204461 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 notificationTypeRepository, + IRepository menuRepository, ISnowflakeIdGenerator snowflakeIdGenerator, IPublisher eventBus) : IRequestHandler { @@ -46,6 +48,15 @@ public class AssignRolePermissionCommandHandler( command.ResourceIdentity, command.ResourceId); + // 菜单/操作权限:Code 对齐菜单上声明的权限标识(如 sys:user:add), + // 否则 v-permission 之类按业务权限码判断的地方永远匹配不上 + if (command.ResourceType is Resource_Type_Enum.菜单 or Resource_Type_Enum.操作) + { + var menu = await menuRepository.GetByIdAsync(command.ResourceId); + if (menu != null && !string.IsNullOrEmpty(menu.Permission)) + dbPermission.SetCode(menu.Permission); + } + // 消息订阅权限:Code 对齐消息类型的 RequiredPermission(业务权限码),保证订阅校验一致 if (command.ResourceType == Resource_Type_Enum.消息订阅) { diff --git a/backend/F9WebAiTest.Application/CQRS/SYS/Reports/Queries/GetEmployeeDirectoryRepQuery.cs b/backend/F9WebAiTest.Application/CQRS/SYS/Reports/Queries/GetEmployeeDirectoryRepQuery.cs new file mode 100644 index 0000000..5777a18 --- /dev/null +++ b/backend/F9WebAiTest.Application/CQRS/SYS/Reports/Queries/GetEmployeeDirectoryRepQuery.cs @@ -0,0 +1,62 @@ +using F9WebAiTest.Application.Dtos.SYS.Reports; +using F9WebAiTest.Application.Interfaces.Repositories.SYS.Employees; +using MediatR; + +namespace F9WebAiTest.Application.CQRS.SYS.Reports.Queries; + +/// 职工花名册报表数据查询(示例,对应 DsName = RepDt_Sys_Users) +public record GetEmployeeDirectoryRepQuery(long DepId, string Like) : IRequest; + +public class GetEmployeeDirectoryRepQueryHandler : IRequestHandler +{ + private readonly IEmployeeReadRepository _readRepository; + + public GetEmployeeDirectoryRepQueryHandler(IEmployeeReadRepository readRepository) + { + _readRepository = readRepository; + } + + public async Task Handle(GetEmployeeDirectoryRepQuery query, CancellationToken cancellationToken) + { + var dto = new ReportDto_DataSource + { + Report_Data = new Dictionary() + }; + + // 1. 通过仓储查询职工数据(部门含子部门) + var page = await _readRepository.GetPagedAsync(query.DepId, 0, null, null, + string.IsNullOrWhiteSpace(query.Like) ? "" : query.Like, 1, 1000); + + // 2. 部门名称(查询条件展示用) + var depName = query.DepId > 0 ? await _readRepository.GetDepNameAsync(query.DepId) : ""; + + // 3. 明细数据:属性名须与 .grf 模板 DetailGrid.Recordset.Field 一致 + var rows = new List(); + foreach (var e in page.Items) + { + rows.Add(new + { + EmployeeCode = e.Code ?? "", + EmployeeName = e.Name ?? "", + Gender = e.Gender ?? "", + DepName = e.DepName ?? "", + WorktypeName = e.WorktypeName ?? "", + Phone = e.Phone ?? "", + RuzhiDate = e.RuzhiDate?.ToString("yyyy-MM-dd") ?? "" + }); + } + + // 4. 报表参数(模板以 StaticBox 的 Parameter 属性引用) + var mainData = new ReportDataDto_Data { Field_Data = rows }; + mainData.Parameter_Data = new + { + 部门 = string.IsNullOrWhiteSpace(depName) ? "(全部)" : depName, + 关键字 = string.IsNullOrWhiteSpace(query.Like) ? "(全部)" : query.Like, + 记录数 = page.Total, + 查询时间 = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + }; + + dto.Report_Data.Add("main", mainData); + return dto; + } +} diff --git a/backend/F9WebAiTest.Application/CQRS/SYS/Reports/Queries/GetLsjlRepQuery.cs b/backend/F9WebAiTest.Application/CQRS/SYS/Reports/Queries/GetLsjlRepQuery.cs new file mode 100644 index 0000000..9b2de93 --- /dev/null +++ b/backend/F9WebAiTest.Application/CQRS/SYS/Reports/Queries/GetLsjlRepQuery.cs @@ -0,0 +1,149 @@ +using F9WebAiTest.Application.Dtos.SYS.Reports; +using F9WebAiTest.Domain.Common; +using F9WebAiTest.Domain.Entities.Base; +using F9WebAiTest.Domain.Entities.Prod; +using MediatR; +using SysDept = F9WebAiTest.Domain.Entities.SYS.Departments; + +namespace F9WebAiTest.Application.CQRS.SYS.Reports.Queries; + +/// +/// 落丝记录报表查询(对应 DsName = RepDt_Prod_Lsjl)。 +/// 参数:日期范围 / 车间(含子车间)/ 机组 / 庄口 / 关键字(庄口、规格、颜色)。 +/// 输出字段名与 F9 落丝记录报表模板一致(中文列名)。 +/// +public record GetLsjlRepQuery( + DateTime? DateStart, + DateTime? DateEnd, + long DepId, + int Jizu, + long GyzkId, + string Like) : IRequest; + +public class GetLsjlRepQueryHandler : IRequestHandler +{ + private readonly IRepository _repo; + private readonly IRepository _deptRepo; + private readonly IRepository _zkRepo; + + public GetLsjlRepQueryHandler( + IRepository repo, + IRepository deptRepo, + IRepository zkRepo) + { + _repo = repo; + _deptRepo = deptRepo; + _zkRepo = zkRepo; + } + + public async Task Handle(GetLsjlRepQuery query, CancellationToken cancellationToken) + { + var all = await _repo.GetAllAsync(); + var depts = await _deptRepo.GetAllAsync(); + var zks = await _zkRepo.GetAllAsync(); + + var deptDict = depts.GroupBy(d => d.Id).ToDictionary(g => g.Key, g => g.First().Name); + var zkDict = zks.GroupBy(z => z.Id).ToDictionary(g => g.Key, g => g.First().ZhuangKouName); + + var depIds = ResolveDepIds(depts, query.DepId); + + IEnumerable rows = all; + if (query.DateStart.HasValue) + { + var start = query.DateStart.Value.Date; + rows = rows.Where(x => x.WorkDate.Date >= start); + } + if (query.DateEnd.HasValue) + { + var end = query.DateEnd.Value.Date; + rows = rows.Where(x => x.WorkDate.Date <= end); + } + if (depIds != null) + rows = rows.Where(x => depIds.Contains(x.DeptId)); + if (query.Jizu > 0) + rows = rows.Where(x => x.Jizu == query.Jizu); + if (query.GyzkId > 0) + rows = rows.Where(x => x.GyzkId == query.GyzkId); + if (!string.IsNullOrWhiteSpace(query.Like)) + { + var kw = query.Like.Trim(); + rows = rows.Where(x => + deptDict.GetValueOrDefault(x.DeptId, "").Contains(kw) || + zkDict.GetValueOrDefault(x.GyzkId, "").Contains(kw) || + (x.Guige ?? "").Contains(kw) || + (x.Yanse ?? "").Contains(kw)); + } + + var list = rows + .OrderBy(x => x.WorkDate) + .ThenBy(x => deptDict.GetValueOrDefault(x.DeptId, "")) + .ThenBy(x => x.Jizu) + .ThenBy(x => x.LuosiTime) + .ToList(); + + // 明细行:键名与 .grf 模板 DetailGrid.Recordset.Field 完全一致(中文列名) + var fieldRows = new List(list.Count); + foreach (var x in list) + { + var depName = deptDict.GetValueOrDefault(x.DeptId, ""); + var zkName = zkDict.GetValueOrDefault(x.GyzkId, ""); + fieldRows.Add(new Dictionary(StringComparer.Ordinal) + { + ["日期"] = x.WorkDate.ToString("yyyy-MM-dd"), + ["车间"] = depName, + ["机组"] = x.Jizu, + ["庄口"] = zkName, + ["回数"] = x.Huishu, + ["颜色"] = x.Yanse ?? "", + ["半车"] = x.Isbanche ? "半车" : "整车", + ["落丝时间"] = x.LuosiTime ?? "", + ["缫丝桶数"] = x.Chetaishu, + // 以下是备选列(模板未使用时不显示,可用于自定义模板) + ["规格"] = x.Guige ?? "", + ["车台范围"] = x.ChetaiStart > 0 ? $"{x.ChetaiStart}-{x.ChetaiEnd}" : "", + ["缫丝时长"] = Math.Round(x.Hours, 2), + ["车速"] = Math.Round(x.Chesu, 2), + }); + } + + var depText = query.DepId > 0 ? deptDict.GetValueOrDefault(query.DepId, "") : "(全部)"; + var zkText = query.GyzkId > 0 ? zkDict.GetValueOrDefault(query.GyzkId, "") : "(全部)"; + var paramData = new ReportDataDto_Data { Field_Data = fieldRows }; + paramData.Parameter_Data = new + { + 开始日期 = query.DateStart?.ToString("yyyy-MM-dd") ?? "", + 结束日期 = query.DateEnd?.ToString("yyyy-MM-dd") ?? "", + 车间 = string.IsNullOrWhiteSpace(depText) ? "(全部)" : depText, + 机组 = query.Jizu > 0 ? query.Jizu.ToString() : "(全部)", + 庄口 = string.IsNullOrWhiteSpace(zkText) ? "(全部)" : zkText, + 记录数 = fieldRows.Count, + 查询时间 = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + }; + + var dto = new ReportDto_DataSource(); + dto.Report_Data.Add("main", paramData); + return dto; + } + + /// 车间 Id → 自身 + 全部子孙车间 Id(null 表示不限) + private static HashSet? ResolveDepIds(List depts, long depId) + { + if (depId <= 0) return null; + + var children = depts + .GroupBy(d => d.ParentId) + .ToDictionary(g => g.Key, g => g.Select(d => d.Id).ToList()); + + var result = new HashSet { depId }; + var stack = new Stack(); + stack.Push(depId); + while (stack.Count > 0) + { + var cur = stack.Pop(); + if (!children.TryGetValue(cur, out var kids)) continue; + foreach (var k in kids) + if (result.Add(k)) stack.Push(k); + } + return result; + } +} diff --git a/backend/F9WebAiTest.Application/DependencyInjection.cs b/backend/F9WebAiTest.Application/DependencyInjection.cs index 8ad078a..d9f1e82 100644 --- a/backend/F9WebAiTest.Application/DependencyInjection.cs +++ b/backend/F9WebAiTest.Application/DependencyInjection.cs @@ -4,6 +4,9 @@ using F9WebAiTest.Application.Services.Auth; using F9WebAiTest.Application.Services.Base; using F9WebAiTest.Application.Services.Notice; using F9WebAiTest.Application.Services.Prod; +using F9WebAiTest.Application.Services.QC; +using F9WebAiTest.Application.Services.Reports; +using F9WebAiTest.Application.Interfaces.Reports; using MediatR; using Microsoft.Extensions.DependencyInjection; @@ -25,6 +28,18 @@ public static class DependencyInjection services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + + // 质量模块:工艺执行书 / 样茧试缫 + services.AddScoped(); + services.AddScoped(); + + // 报表数据提供者(按 Config.DsName 解析,自动注册所有实现) + foreach (var t in typeof(DependencyInjection).Assembly.GetTypes() + .Where(t => !t.IsAbstract && typeof(IReportDataProvider).IsAssignableFrom(t))) + { + services.AddScoped(typeof(IReportDataProvider), t); + } // MediatR:注册命令/查询 Handler services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(NotificationService).Assembly)); diff --git a/backend/F9WebAiTest.Application/Dtos/QC/GyzxsDtos.cs b/backend/F9WebAiTest.Application/Dtos/QC/GyzxsDtos.cs new file mode 100644 index 0000000..2eb6b56 --- /dev/null +++ b/backend/F9WebAiTest.Application/Dtos/QC/GyzxsDtos.cs @@ -0,0 +1,62 @@ +using F9WebAiTest.Domain.Entities.QC.Gyzxs; + +namespace F9WebAiTest.Application.Dtos.QC; + +/// 工艺执行书主列表行(庄口 + 各子单据完成标记) +public class GyzxsDocVersionDto +{ + public long Id { get; set; } + public long GyzkId { get; set; } + public string ZhuangKouName { get; set; } = ""; + public string ZhuangKouCode { get; set; } = ""; + public int Year { get; set; } + public int Sort { get; set; } + + public long ShichanId { get; set; } + public long FengMianId { get; set; } + public long XbjId { get; set; } + public long XbjSsdyId { get; set; } + public long ZhuJianId { get; set; } + public long ZdsId { get; set; } + public long ZdsDyId { get; set; } + public long HsId { get; set; } + + /// 该庄口已录入的规格(自动缫 / 后缫 / 试产 / 试缫大样 均按 庄口+规格 唯一) + public List Guiges { get; set; } = new(); +} + +/// 选剥茧(含混茧比例明细) +public class GyzxsXbjDto +{ + public GyzxsXbj Xbj { get; set; } = new(); + public List HunJian { get; set; } = new(); +} + +/// 煮茧(含分车间指标、副煮车间结果) +public class GyzxsZhuJianDto +{ + public GyzxsZhuJian ZhuJian { get; set; } = new(); + public List CheJian { get; set; } = new(); + public List FuZhu { get; set; } = new(); +} + +/// 自动缫(含大样资料) +public class GyzxsZdsDto +{ + public GyzxsZds Zds { get; set; } = new(); + public GyzxsZdsDyzl? Dyzl { get; set; } +} + +/// +/// 报表数据包:Parameters=报表参数区,Main/Sub1/Sub2=结果集。 +/// 结构与 Grid++Report 的 Report_Data 对齐,便于日后直接对接报表中心。 +/// +public class GyzxsPrintDto +{ + public string ReportName { get; set; } = ""; + public string Title { get; set; } = ""; + public Dictionary Parameters { get; set; } = new(); + public List> Main { get; set; } = new(); + public List> Sub1 { get; set; } = new(); + public List> Sub2 { get; set; } = new(); +} diff --git a/backend/F9WebAiTest.Application/Dtos/QC/YjssDtos.cs b/backend/F9WebAiTest.Application/Dtos/QC/YjssDtos.cs new file mode 100644 index 0000000..68933aa --- /dev/null +++ b/backend/F9WebAiTest.Application/Dtos/QC/YjssDtos.cs @@ -0,0 +1,60 @@ +using F9WebAiTest.Domain.Entities.QC.Yjss; + +namespace F9WebAiTest.Application.Dtos.QC; + +/// 样茧试缫主列表行(原料小样 / 综合样共用) +public class YjssListItemDto +{ + public long Id { get; set; } + public int Type { get; set; } // 1=原料小样 2=综合样 + public long ZhuangKouId { get; set; } // 原料庄口 / 工艺庄口 + public string ZhuangKouName { get; set; } = ""; + public DateTime Date { get; set; } + public int SampleType { get; set; } // 原料小样抽样类型:1定价 2入库 3鲜茧 + public string SampleTypeName { get; set; } = ""; + public decimal ZhongLiang { get; set; } + public decimal HuiChaoLv { get; set; } + public long XjdcId { get; set; } + public int JsdcCt { get; set; } + public long JfdcId { get; set; } + public long QpdcId { get; set; } + public long QjjjId { get; set; } + public int JsbhxdCt { get; set; } + public string Memo { get; set; } = ""; +} + +/// 茧幅调查(主表 + 明细) +public class YjssJfdcDto +{ + public YjssDcJfdcMain Main { get; set; } = new(); + public List Items { get; set; } = new(); +} + +/// 茧丝百回纤度(汇总 + 明细) +public class YjssJsbhxdDto +{ + public YjssDcJsbhxdMain Main { get; set; } = new(); + public List Items { get; set; } = new(); +} + +/// 样茧试缫整页数据(用于报表 / 打印) +public class YjssFullDto +{ + public int Type { get; set; } + public string ZhuangKouName { get; set; } = ""; + public DateTime Date { get; set; } + public decimal ZhongLiang { get; set; } + public decimal HuiChaoLv { get; set; } + public string SampleTypeName { get; set; } = ""; + public string Memo { get; set; } = ""; + + public YjssDcXjdc? Xjdc { get; set; } + public List Jsdc { get; set; } = new(); + public YjssJfdcDto? Jfdc { get; set; } + public YjssDcQpdc? Qpdc { get; set; } + public YjssDcQjjj? Qjjj { get; set; } + public YjssJsbhxdDto? Jsbhxd { get; set; } + + /// 报表参数区(中文键,与 F9 RepDt_Qc_Yjss_* 对齐) + public Dictionary Parameters { get; set; } = new(); +} diff --git a/backend/F9WebAiTest.Application/Dtos/SYS/Reports/ReportDataSourceDto.cs b/backend/F9WebAiTest.Application/Dtos/SYS/Reports/ReportDataSourceDto.cs new file mode 100644 index 0000000..bde4553 --- /dev/null +++ b/backend/F9WebAiTest.Application/Dtos/SYS/Reports/ReportDataSourceDto.cs @@ -0,0 +1,21 @@ +namespace F9WebAiTest.Application.Dtos.SYS.Reports; + +/// +/// 报表数据集(对应 F9 的 ReportDataDto_Data): +/// Field_Data 为记录集(每行一个匿名对象,属性名须与 .grf 模板 DetailGrid.Recordset.Field 一致); +/// Parameter_Data 为报表参数(账期 / 车间 / 查询时间等,供模板以 Parameter 方式引用)。 +/// +public class ReportDataDto_Data +{ + public List Field_Data { get; set; } = new(); + public object? Parameter_Data { get; set; } +} + +/// +/// 报表数据源(对应 F9 的 ReportDto_DataSource): +/// Report_Data 为「数据集名 → 数据集」的字典,如 "main" / "detail"。 +/// +public class ReportDto_DataSource +{ + public Dictionary Report_Data { get; set; } = new(); +} diff --git a/backend/F9WebAiTest.Application/Dtos/SYS/Reports/ReportDtos.cs b/backend/F9WebAiTest.Application/Dtos/SYS/Reports/ReportDtos.cs new file mode 100644 index 0000000..625bc57 --- /dev/null +++ b/backend/F9WebAiTest.Application/Dtos/SYS/Reports/ReportDtos.cs @@ -0,0 +1,82 @@ +namespace F9WebAiTest.Application.Dtos.SYS.Reports; + +public record ReportListDto( + long Id, + string Code, + string Name, + string Category, + string Description, + int SortOrder, + bool IsActive, + long ReportFileId, + bool HasTemplate, + DateTime CreatedAt, + DateTime? UpdatedAt); + +public record ReportDetailDto( + long Id, + string Code, + string Name, + string Category, + string Description, + string DataSql, + string DataParams, + string Config, + int SortOrder, + bool IsActive, + long ReportFileId, + bool HasTemplate, + DateTime CreatedAt, + DateTime? UpdatedAt); + +public record ReportViewDto( + long Id, + string Code, + string Name, + string Category, + string Description, + int SortOrder, + bool HasTemplate); + +public class ReportCreateDto +{ + public string Code { get; set; } = ""; + public string Name { get; set; } = ""; + public string? Category { get; set; } + public string? Description { get; set; } + public string? DataSql { get; set; } + public string? DataParams { get; set; } + public string? Config { get; set; } + public int SortOrder { get; set; } + + /// + /// 报表模板(.grf 的 JSON 文本)。与报表配置一起保存,无需再单独上传模板文件。 + /// 为空表示本次不改动模板。 + /// + public string? TemplateJson { get; set; } +} + +public class ReportUpdateDto +{ + public string Code { get; set; } = ""; + public string Name { get; set; } = ""; + public string? Category { get; set; } + public string? Description { get; set; } + public string? DataSql { get; set; } + public string? DataParams { get; set; } + public string? Config { get; set; } + public int SortOrder { get; set; } + + /// + /// 报表模板(.grf 的 JSON 文本)。与报表配置一起保存,无需再单独上传模板文件。 + /// 为空表示本次不改动模板(保留原模板)。 + /// + public string? TemplateJson { get; set; } +} + +/// 在线编辑模板源码后直接保存的内容(免下载 / 上传 .grf) +public class ReportTemplateContentDto +{ + /// 模板全文:GR6 时代的 UTF-8 JSON 文本,或 GR7 设计器的 "_WR" 压缩文本 + public string? Content { get; set; } +} diff --git a/backend/F9WebAiTest.Application/Dtos/SYS/Reports/ReportScaffoldDtos.cs b/backend/F9WebAiTest.Application/Dtos/SYS/Reports/ReportScaffoldDtos.cs new file mode 100644 index 0000000..618cd58 --- /dev/null +++ b/backend/F9WebAiTest.Application/Dtos/SYS/Reports/ReportScaffoldDtos.cs @@ -0,0 +1,55 @@ +namespace F9WebAiTest.Application.Dtos.SYS.Reports; + +/// 已注册的报表数据源(IReportDataProvider.ReportName) +public class ReportDataSourceInfoDto +{ + public string DsName { get; set; } = ""; +} + +/// 明细字段(.grf 模板 DetailGrid.Recordset.Field 的映射来源),全部由查询结果推断 +public class ReportFieldDefDto +{ + public string Name { get; set; } = ""; + public string Label { get; set; } = ""; + public string DataType { get; set; } = "String"; +} + +/// 探测请求:按 DsName(或 DataSql)执行一次查询,识别返回的列 +public class ReportProbeRequestDto +{ + /// 数据源名称(IReportDataProvider.ReportName),优先于 DataSql + public string? DsName { get; set; } + + /// 数据 SQL(无 DsName 时用于探测列) + public string? DataSql { get; set; } + + /// 执行探测时传入的参数值(键 = 参数名;报表上已配置的参数由前端带入) + public Dictionary Params { get; set; } = new(); +} + +/// +/// 报表参数(后端 DataSource.Parameter_Data 的键值)。 +/// 注意:这是「报表参数」——由查询执行后返回、供模板头展示的统计口径; +/// 与 Config.Parms 里的「查询条件」(q_*,用于查询面板传参)不是一回事。 +/// +public class ReportParamValueDto +{ + public string Name { get; set; } = ""; + public string Value { get; set; } = ""; +} + +/// 探测结果:查询返回的明细字段 + 报表参数 + 少量样本行 +public class ReportProbeResultDto +{ + public string DsName { get; set; } = ""; + public List Fields { get; set; } = new(); + + /// 报表参数(Parameter_Data 的键值),模板头/合计区直接引用 + public List Parameters { get; set; } = new(); + + public List> Rows { get; set; } = new(); + public int Total { get; set; } + + /// 探测失败原因 + public string? Error { get; set; } +} diff --git a/backend/F9WebAiTest.Application/F9WebAiTest.Application.csproj b/backend/F9WebAiTest.Application/F9WebAiTest.Application.csproj index 9b0f42c..4c9451f 100644 --- a/backend/F9WebAiTest.Application/F9WebAiTest.Application.csproj +++ b/backend/F9WebAiTest.Application/F9WebAiTest.Application.csproj @@ -16,6 +16,7 @@ + diff --git a/backend/F9WebAiTest.Application/Interfaces/Reports/IReportDataProvider.cs b/backend/F9WebAiTest.Application/Interfaces/Reports/IReportDataProvider.cs new file mode 100644 index 0000000..e81cdd5 --- /dev/null +++ b/backend/F9WebAiTest.Application/Interfaces/Reports/IReportDataProvider.cs @@ -0,0 +1,22 @@ +using F9WebAiTest.Application.Dtos.SYS.Reports; +using MediatR; + +namespace F9WebAiTest.Application.Interfaces.Reports; + +/// +/// 报表数据提供者(对应 F9 的 IReportService): +/// 报表配置 Config.DsName 指定查询名称,后端按名称解析到对应实现执行查询。 +/// 实现类注册到 DI 后自动被发现(静态 ReportName 属性作为解析键)。 +/// 查询实现应通过 MediatR 发送 CQRS Query,由 QueryHandler 组装 ReportDto_DataSource。 +/// +public interface IReportDataProvider +{ + /// 查询名称(对应报表配置中的 DsName,如 "RepDt_Sys_Users") + string ReportName { get; } + + /// + /// 执行查询。pairs 为前端查询控件收集的键值对(键 = Config.Parms[].Name); + /// 返回含记录集(Field_Data)与报表参数(Parameter_Data)的数据源。 + /// + Task GetData(Dictionary pairs, IMediator mediator); +} diff --git a/backend/F9WebAiTest.Application/Services/QC/GyzxsService.cs b/backend/F9WebAiTest.Application/Services/QC/GyzxsService.cs new file mode 100644 index 0000000..f4167d7 --- /dev/null +++ b/backend/F9WebAiTest.Application/Services/QC/GyzxsService.cs @@ -0,0 +1,760 @@ +using F9WebAiTest.Application.Common; +using F9WebAiTest.Application.Dtos.QC; +using F9WebAiTest.Domain.Common; +using F9WebAiTest.Domain.Entities.Base; +using F9WebAiTest.Domain.Entities.QC.Gyzxs; + +namespace F9WebAiTest.Application.Services.QC; + +public interface IGyzxsService +{ + Task> GetDocVersionPagedAsync(int pageIndex, int pageSize, int year, string keyword); + Task CreateDocVersionAsync(long gyzkId); + Task DeleteDocVersionAsync(long id); + + Task GetFengMianAsync(long gyzkId); + Task SaveFengMianAsync(GyzxsFengMian dto); + + Task GetShichanAsync(long gyzkId, string guige); + Task SaveShichanAsync(GyzxsShichan dto); + + Task GetXbjAsync(long gyzkId); + Task SaveXbjAsync(GyzxsXbjDto dto); + + Task GetXbjSsDyAsync(long gyzkId, string guige); + Task SaveXbjSsDyAsync(GyzxsXbjSsDy dto); + + Task GetZhuJianAsync(long gyzkId); + Task SaveZhuJianAsync(GyzxsZhuJianDto dto); + + Task GetZdsAsync(long gyzkId, string guige); + Task SaveZdsAsync(GyzxsZdsDto dto); + + Task GetHsAsync(long gyzkId, string guige); + Task SaveHsAsync(GyzxsHs dto); + + /// 报表数据(type: fengmian / gyzxs / xbj / shichan / ssdy / zhujian / zds / zdsdy) + Task GetPrintDataAsync(string type, long gyzkId, string guige); +} + +/// +/// 质量模块 —— 工艺执行书(对应 F9 生产\质量\工艺执行书)。 +/// 设计要点:以工艺庄口为主体,7 张子单据各自按「庄口(+规格)」唯一, +/// 任一子单据保存都会 upsert 主控表 qc_gyzxs_doc_version 的对应指针列。 +/// +public class GyzxsService : IGyzxsService +{ + private readonly IRepository _zkRepo; + private readonly IRepository _versionRepo; + private readonly IRepository _fengMianRepo; + private readonly IRepository _shichanRepo; + private readonly IRepository _xbjRepo; + private readonly IRepository _hunjianRepo; + private readonly IRepository _ssdyRepo; + private readonly IRepository _zhuJianRepo; + private readonly IRepository _zjCheJianRepo; + private readonly IRepository _zjFuZhuRepo; + private readonly IRepository _zdsRepo; + private readonly IRepository _zdsDyRepo; + private readonly IRepository _hsRepo; + + public GyzxsService( + IRepository zkRepo, + IRepository versionRepo, + IRepository fengMianRepo, + IRepository shichanRepo, + IRepository xbjRepo, + IRepository hunjianRepo, + IRepository ssdyRepo, + IRepository zhuJianRepo, + IRepository zjCheJianRepo, + IRepository zjFuZhuRepo, + IRepository zdsRepo, + IRepository zdsDyRepo, + IRepository hsRepo) + { + _zkRepo = zkRepo; + _versionRepo = versionRepo; + _fengMianRepo = fengMianRepo; + _shichanRepo = shichanRepo; + _xbjRepo = xbjRepo; + _hunjianRepo = hunjianRepo; + _ssdyRepo = ssdyRepo; + _zhuJianRepo = zhuJianRepo; + _zjCheJianRepo = zjCheJianRepo; + _zjFuZhuRepo = zjFuZhuRepo; + _zdsRepo = zdsRepo; + _zdsDyRepo = zdsDyRepo; + _hsRepo = hsRepo; + } + + // ==================== 主控(庄口年度列表) ==================== + + public async Task> GetDocVersionPagedAsync(int pageIndex, int pageSize, int year, string keyword) + { + if (year <= 0) year = DateTime.Today.Year; + + var zks = (await _zkRepo.FindAsync(x => !x.IsDeleted && x.BelongingYear == year)) + .OrderBy(x => x.Sort).ThenBy(x => x.ZhuangKouCode).ToList(); + if (!string.IsNullOrWhiteSpace(keyword)) + zks = zks.Where(x => x.ZhuangKouName.Contains(keyword) || x.ZhuangKouCode.Contains(keyword) + || x.ProductionDisplayName.Contains(keyword)).ToList(); + + var versions = (await _versionRepo.FindAsync(x => !x.IsDeleted)) + .GroupBy(x => x.GyzkId).ToDictionary(g => g.Key, g => g.OrderByDescending(v => v.Id).First()); + + // 规格集合:来自试产 / 自动缫 / 后缫 / 试缫大样 + var guigeMap = new Dictionary>(); + void Collect(IEnumerable<(long GyzkId, string Guige)> rows) + { + foreach (var (gk, gg) in rows) + { + if (string.IsNullOrWhiteSpace(gg)) continue; + if (!guigeMap.TryGetValue(gk, out var set)) guigeMap[gk] = set = new HashSet(); + set.Add(gg); + } + } + Collect((await _shichanRepo.FindAsync(x => !x.IsDeleted)).Select(x => (x.GyzkId, x.Guige))); + Collect((await _zdsRepo.FindAsync(x => !x.IsDeleted)).Select(x => (x.GyzkId, x.Guige))); + Collect((await _hsRepo.FindAsync(x => !x.IsDeleted)).Select(x => (x.GyzkId, x.Guige))); + Collect((await _ssdyRepo.FindAsync(x => !x.IsDeleted)).Select(x => (x.GyzkId, x.Guige))); + + var total = zks.Count; + var page = zks.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(); + + return new PageResult + { + Total = total, + PageIndex = pageIndex, + PageSize = pageSize, + Items = page.Select(zk => + { + versions.TryGetValue(zk.Id, out var v); + guigeMap.TryGetValue(zk.Id, out var gs); + return new GyzxsDocVersionDto + { + Id = v?.Id ?? 0, + GyzkId = zk.Id, + ZhuangKouName = zk.ZhuangKouName, + ZhuangKouCode = zk.ZhuangKouCode, + Year = zk.BelongingYear, + Sort = zk.Sort, + ShichanId = v?.ShichanId ?? 0, + FengMianId = v?.FengMianId ?? 0, + XbjId = v?.XbjId ?? 0, + XbjSsdyId = v?.XbjSsdyId ?? 0, + ZhuJianId = v?.ZhuJianId ?? 0, + ZdsId = v?.ZdsId ?? 0, + ZdsDyId = v?.ZdsDyId ?? 0, + HsId = v?.HsId ?? 0, + Guiges = gs?.OrderBy(x => x).ToList() ?? new List() + }; + }).ToList() + }; + } + + public async Task CreateDocVersionAsync(long gyzkId) + { + var zk = await _zkRepo.GetByIdAsync(gyzkId) ?? throw new ServiceException("工艺庄口不存在", 404); + var exist = await _versionRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.GyzkId == gyzkId); + if (exist == null) + { + exist = new GyzxsDocVersion { GyzkId = gyzkId }; + await _versionRepo.AddAsync(exist); + } + return new GyzxsDocVersionDto + { + Id = exist.Id, GyzkId = gyzkId, ZhuangKouName = zk.ZhuangKouName, + ZhuangKouCode = zk.ZhuangKouCode, Year = zk.BelongingYear, Sort = zk.Sort + }; + } + + public async Task DeleteDocVersionAsync(long id) + { + var v = await _versionRepo.GetByIdAsync(id) ?? throw new ServiceException("记录不存在", 404); + await _versionRepo.DeleteAsync(v); + } + + /// 主控表 upsert:任一子单据保存后回写指针列 + private async Task TouchVersionAsync(long gyzkId, Action apply) + { + var v = await _versionRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.GyzkId == gyzkId); + if (v == null) + { + v = new GyzxsDocVersion { GyzkId = gyzkId }; + apply(v); + await _versionRepo.AddAsync(v); + } + else + { + apply(v); + await _versionRepo.UpdateAsync(v); + } + } + + // ==================== 封面 ==================== + + public async Task GetFengMianAsync(long gyzkId) + { + var exist = await _fengMianRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.GyzkId == gyzkId); + if (exist != null) return exist; + + // 新封面:按 F9 规则预生成编号 SJZ-{年度}-工艺-{序号} + var zk = await _zkRepo.GetByIdAsync(gyzkId); + var year = zk?.BelongingYear ?? DateTime.Today.Year; + var prefix = $"SJZ-{year}-工艺-"; + var used = (await _fengMianRepo.FindAsync(x => !x.IsDeleted)) + .Where(x => x.BianHao != null && x.BianHao.StartsWith(prefix)); + var max = 0; + foreach (var f in used) + { + if (int.TryParse(f.BianHao.Substring(prefix.Length), out var n) && n > max) max = n; + } + return new GyzxsFengMian { GyzkId = gyzkId, BianHao = prefix + (max + 1), ShengChanCheJian = "" }; + } + + public async Task SaveFengMianAsync(GyzxsFengMian dto) + { + if (dto.GyzkId <= 0) throw new ServiceException("请选择工艺庄口"); + var exist = await _fengMianRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.GyzkId == dto.GyzkId); + var id = await UpsertAsync(_fengMianRepo, dto, exist); + await TouchVersionAsync(dto.GyzkId, v => v.FengMianId = id); + return true; + } + + // ==================== 试产(试缫工艺单) ==================== + + public async Task GetShichanAsync(long gyzkId, string guige) + => await _shichanRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.GyzkId == gyzkId && x.Guige == (guige ?? "")) + ?? new GyzxsShichan { GyzkId = gyzkId, Guige = guige ?? "" }; + + public async Task SaveShichanAsync(GyzxsShichan dto) + { + if (dto.GyzkId <= 0) throw new ServiceException("请选择工艺庄口"); + var exist = await _shichanRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.GyzkId == dto.GyzkId && x.Guige == (dto.Guige ?? "")); + var id = await UpsertAsync(_shichanRepo, dto, exist); + await TouchVersionAsync(dto.GyzkId, v => v.ShichanId = id); + return true; + } + + // ==================== 混剥选茧 ==================== + + public async Task GetXbjAsync(long gyzkId) + { + var e = await _xbjRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.GyzkId == gyzkId); + var dto = new GyzxsXbjDto { Xbj = e ?? new GyzxsXbj { GyzkId = gyzkId } }; + if (e != null) + dto.HunJian = (await _hunjianRepo.FindAsync(x => !x.IsDeleted && x.XbjId == e.Id)) + .OrderBy(x => x.Sort).ThenBy(x => x.Id).ToList(); + return dto; + } + + public async Task SaveXbjAsync(GyzxsXbjDto dto) + { + if (dto.Xbj.GyzkId <= 0) throw new ServiceException("请选择工艺庄口"); + var exist = await _xbjRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.GyzkId == dto.Xbj.GyzkId); + var id = await UpsertAsync(_xbjRepo, dto.Xbj, exist); + + var olds = await _hunjianRepo.FindAsync(x => x.XbjId == id); + if (olds.Count > 0) await _hunjianRepo.HardDeleteRangeAsync(olds); + var rows = dto.HunJian ?? new List(); + for (var i = 0; i < rows.Count; i++) + { + rows[i].Id = 0; + rows[i].XbjId = id; + rows[i].Sort = i + 1; + await _hunjianRepo.AddAsync(rows[i]); + } + await TouchVersionAsync(dto.Xbj.GyzkId, v => v.XbjId = id); + return true; + } + + // ==================== 试缫与大样 ==================== + + public async Task GetXbjSsDyAsync(long gyzkId, string guige) + => await _ssdyRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.GyzkId == gyzkId && x.Guige == (guige ?? "")) + ?? new GyzxsXbjSsDy { GyzkId = gyzkId, Guige = guige ?? "" }; + + public async Task SaveXbjSsDyAsync(GyzxsXbjSsDy dto) + { + if (dto.GyzkId <= 0) throw new ServiceException("请选择工艺庄口"); + var exist = await _ssdyRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.GyzkId == dto.GyzkId && x.Guige == (dto.Guige ?? "")); + var id = await UpsertAsync(_ssdyRepo, dto, exist); + await TouchVersionAsync(dto.GyzkId, v => v.XbjSsdyId = id); + return true; + } + + // ==================== 煮茧 ==================== + + public async Task GetZhuJianAsync(long gyzkId) + { + var e = await _zhuJianRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.GyzkId == gyzkId); + var dto = new GyzxsZhuJianDto { ZhuJian = e ?? new GyzxsZhuJian { GyzkId = gyzkId } }; + if (e != null) + { + dto.CheJian = (await _zjCheJianRepo.FindAsync(x => !x.IsDeleted && x.ZhuJianId == e.Id)) + .OrderBy(x => x.Sort).ThenBy(x => x.Id).ToList(); + dto.FuZhu = (await _zjFuZhuRepo.FindAsync(x => !x.IsDeleted && x.ZhuJianId == e.Id)) + .OrderBy(x => x.Sort).ThenBy(x => x.Id).ToList(); + } + return dto; + } + + public async Task SaveZhuJianAsync(GyzxsZhuJianDto dto) + { + if (dto.ZhuJian.GyzkId <= 0) throw new ServiceException("请选择工艺庄口"); + var exist = await _zhuJianRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.GyzkId == dto.ZhuJian.GyzkId); + var id = await UpsertAsync(_zhuJianRepo, dto.ZhuJian, exist); + + var oldChe = await _zjCheJianRepo.FindAsync(x => x.ZhuJianId == id); + if (oldChe.Count > 0) await _zjCheJianRepo.HardDeleteRangeAsync(oldChe); + var che = dto.CheJian ?? new List(); + for (var i = 0; i < che.Count; i++) + { + che[i].Id = 0; che[i].ZhuJianId = id; che[i].Sort = i + 1; + await _zjCheJianRepo.AddAsync(che[i]); + } + + var oldFz = await _zjFuZhuRepo.FindAsync(x => x.ZhuJianId == id); + if (oldFz.Count > 0) await _zjFuZhuRepo.HardDeleteRangeAsync(oldFz); + var fz = dto.FuZhu ?? new List(); + for (var i = 0; i < fz.Count; i++) + { + fz[i].Id = 0; fz[i].ZhuJianId = id; fz[i].Sort = i + 1; + await _zjFuZhuRepo.AddAsync(fz[i]); + } + + await TouchVersionAsync(dto.ZhuJian.GyzkId, v => v.ZhuJianId = id); + return true; + } + + // ==================== 自动缫(含大样) ==================== + + public async Task GetZdsAsync(long gyzkId, string guige) + { + var e = await _zdsRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.GyzkId == gyzkId && x.Guige == (guige ?? "")); + var dto = new GyzxsZdsDto { Zds = e ?? new GyzxsZds { GyzkId = gyzkId, Guige = guige ?? "" } }; + if (e != null) + dto.Dyzl = await _zdsDyRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.ZdsId == e.Id); + return dto; + } + + public async Task SaveZdsAsync(GyzxsZdsDto dto) + { + if (dto.Zds.GyzkId <= 0) throw new ServiceException("请选择工艺庄口"); + var exist = await _zdsRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.GyzkId == dto.Zds.GyzkId && x.Guige == (dto.Zds.Guige ?? "")); + var id = await UpsertAsync(_zdsRepo, dto.Zds, exist); + + var dy = dto.Dyzl; + if (dy != null) + { + var oldDy = await _zdsDyRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.ZdsId == id); + dy.ZdsId = id; + dy.GyzkId = dto.Zds.GyzkId; + if (string.IsNullOrWhiteSpace(dy.Guige)) dy.Guige = dto.Zds.Guige; + var dyId = await UpsertAsync(_zdsDyRepo, dy, oldDy); + await TouchVersionAsync(dto.Zds.GyzkId, v => v.ZdsDyId = dyId); + } + await TouchVersionAsync(dto.Zds.GyzkId, v => v.ZdsId = id); + return true; + } + + // ==================== 后缫(工艺执行书) ==================== + + public async Task GetHsAsync(long gyzkId, string guige) + => await _hsRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.GyzkId == gyzkId && x.Guige == (guige ?? "")) + ?? new GyzxsHs { GyzkId = gyzkId, Guige = guige ?? "", ZhiXingDate = DateTime.Today }; + + public async Task SaveHsAsync(GyzxsHs dto) + { + if (dto.GyzkId <= 0) throw new ServiceException("请选择工艺庄口"); + var exist = await _hsRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.GyzkId == dto.GyzkId && x.Guige == (dto.Guige ?? "")); + var id = await UpsertAsync(_hsRepo, dto, exist); + await TouchVersionAsync(dto.GyzkId, v => v.HsId = id); + return true; + } + + // ==================== 报表数据 ==================== + + public async Task GetPrintDataAsync(string type, long gyzkId, string guige) + { + var zk = await _zkRepo.GetByIdAsync(gyzkId); + var zkName = zk?.ZhuangKouName ?? ""; + var dto = new GyzxsPrintDto { ReportName = type }; + + switch ((type ?? "").ToLowerInvariant()) + { + case "fengmian": + { + var e = await GetFengMianAsync(gyzkId); + dto.Title = "桑蚕丝工艺设计书"; + dto.Parameters["庄口名称"] = zkName; + dto.Parameters["编号"] = string.IsNullOrWhiteSpace(e.BianHao) ? $"SJZ-{zk?.BelongingYear ?? DateTime.Today.Year}-工艺" : e.BianHao; + dto.Parameters["缫制规格"] = e.Guige; + dto.Parameters["原料总量"] = $"{e.YuanLiaoZhongLiang:0.##}吨"; + dto.Parameters["生产丝量"] = $"{e.ShengSiZhongLiang:0.##}吨"; + dto.Parameters["缫制车间"] = e.ShengChanCheJian; + dto.Parameters["生产时间"] = FormatRange(e.ShengChanStartDate, e.ShengChanEndDate); + dto.Parameters["发送部门"] = e.FaSongBuMen; + dto.Parameters["日期"] = DateTime.Today.ToString("yyyy年MM月dd日"); + break; + } + case "gyzxs": // 后缫 = 工艺执行书 + { + var e = await GetHsAsync(gyzkId, guige); + dto.Title = "工艺执行书"; + dto.Parameters["庄口名称"] = zkName; + dto.Parameters["执行时间"] = e.ZhiXingDate?.ToString("yyyy/M/d") ?? ""; + dto.Parameters["生丝规格"] = e.Guige; + dto.Parameters["前缫生产车间"] = e.QianSaoCheJian; + dto.Parameters["预缫天数"] = e.YuSaoTianShu; + dto.Parameters["整理规格"] = e.ZhengLiGuiGe; + // 生产指标 + dto.Parameters["单产"] = e.SczbDanChan; + dto.Parameters["日产"] = e.SczbRiChan; + dto.Parameters["编丝量"] = e.SczbBianSiLiang; + dto.Parameters["整检量"] = e.SczbZhengJianLiang; + dto.Parameters["生产正品率"] = e.SczbShengChanZhengPinLv; + dto.Parameters["商检正品率"] = e.SczbShangJianZhengPinLv; + dto.Parameters["切断"] = e.SczbQieDuan; + dto.Parameters["效率"] = e.SczbXiaoLv; + dto.Parameters["编检品质疵点"] = e.SczbPinZhiCiDian; + // 工艺条件及控制指标 + dto.Parameters["车速"] = e.GyzbCheSu; + dto.Parameters["小䈅待返时间"] = e.GyzbXiaoYuDaiFanShiJian; + dto.Parameters["车间温度"] = e.GyzbCheJianWenDu; + dto.Parameters["车间相对湿度"] = e.GyzbCheJianXiangDuiShiDu; + dto.Parameters["车厢温度"] = e.GyzbCheXiangWenDu; + dto.Parameters["车厢湿度"] = e.GyzbCheXiangShiDu; + dto.Parameters["平衡室温度"] = e.GyzbPingHengShiWenDu; + dto.Parameters["平衡室相对湿度"] = e.GyzbPingHengShiXiangDuiShiDu; + dto.Parameters["小䈅给湿温度"] = e.GyzbXiaoYuGeiShiWenDu; + dto.Parameters["小䈅丝片平衡时间"] = e.GyzbXiaoYuSiPianPingHengShiJian; + dto.Parameters["真空度"] = e.GyzbZhenKongDu; + dto.Parameters["抽气次数"] = e.GyzbChouQiCiShu; + dto.Parameters["大䈅丝片平衡时间"] = e.GyzbDaYuSiPianPingHengShiJian; + dto.Parameters["大䈅丝片回潮率"] = e.GyzbDaYuSiPianHuiChaoLv; + dto.Parameters["生丝回潮率"] = e.GyzbShengSiHuiChaoLv; + dto.Parameters["大䈅丝片宽度"] = e.GyzbDaYuSiPianKuanDu; + dto.Parameters["屑丝对丝率"] = e.GyzbXieSiDuiSiLv; + dto.Parameters["大䈅丝片疵点"] = e.GyzbDaYuSiPianCiDian; + dto.Parameters["主要技术措施"] = e.JiShuCuoShi; + dto.Parameters["设计"] = e.SheJiRen; + dto.Parameters["审核"] = e.ShenHeRen; + dto.Parameters["审批"] = e.PiFuRen; + break; + } + case "xbj": + { + var e = await GetXbjAsync(gyzkId); + var x = e.Xbj; + dto.Title = "混剥选茧工艺执行书"; + dto.Parameters["庄口名称"] = zkName; + dto.Parameters["换庄时间"] = x.HuanZhuangDate?.ToString("yyyy/M/d") ?? ""; + dto.Parameters["缫制规格"] = x.Guige; + dto.Parameters["缫制车间"] = x.CheJian; + dto.Parameters["日产量"] = x.ScRiChanLiang; + dto.Parameters["班产量"] = x.ScBanChanLiang; + dto.Parameters["上车率"] = x.ScShangCheLv; + dto.Parameters["联统茧率"] = x.ScLianTongLv; + dto.Parameters["五级茧率"] = x.ScWuJiLv; + dto.Parameters["下茧茧衣率"] = x.ScXiaJiaoLv; + dto.Parameters["茧衣率"] = x.ScJianYiLv; + dto.Parameters["技术措施要求"] = x.ScJiShuCuoShi; + dto.Parameters["统号茧五级茧率"] = x.QcTongHaoWuJiLv; + dto.Parameters["统号茧下茧率"] = x.QcTongHaoXiaJianLv; + dto.Parameters["五级茧联统茧率"] = x.QcWuJiLianTongLv; + dto.Parameters["五级茧下脚率"] = x.QcWuJiXiaJiaoLv; + dto.Parameters["下脚茧联统率"] = x.QcXiaJiaoLianTongLv; + dto.Parameters["下脚茧次茧率"] = x.QcXiaJiaoCiJianLv; + dto.Parameters["好五茧组产量"] = x.QcZbHaoWuZuChanLiang; + dto.Parameters["六级组产量"] = x.QcZbLiuJiZuChanLiang; + dto.Parameters["棉茧组产量"] = x.QcZbMianJianZuChanLiang; + dto.Parameters["下脚组产量"] = x.QcZbXiaJiaoZuChanLiang; + dto.Parameters["双宫组产量"] = x.QcZbShuangGongZuChanLiang; + dto.Parameters["公斤粒数"] = x.QcZbGongJinLiShu; + dto.Parameters["剥光率"] = x.QcZbBaoGuangLv; + dto.Parameters["质量控制茧衣率"] = x.QcZbJianYiLv; + dto.Parameters["合计包数"] = e.HunJian.Sum(i => i.BaoShu); + dto.Parameters["合计比例"] = e.HunJian.Sum(i => i.BiLi); + dto.Parameters["合计混茧包数"] = e.HunJian.Sum(i => i.HunJianBaoShu); + dto.Sub1 = e.HunJian.Select((i, idx) => new Dictionary + { + ["序号"] = idx + 1, + ["庄别"] = i.ZhuangBie, + ["总包数"] = i.BaoShu, + ["比例"] = i.BiLi, + ["混茧包数"] = i.HunJianBaoShu + }).ToList(); + break; + } + case "shichan": + { + var e = await GetShichanAsync(gyzkId, guige); + dto.Title = "自动缫试缫工艺单"; + dto.Parameters["庄口名称"] = zkName; + dto.Parameters["试缫日期"] = e.ScDate?.ToString("yyyy/M/d") ?? ""; + dto.Parameters["换庄方式"] = e.HuanZhuangFangShi; + dto.Parameters["规格"] = e.Guige; + dto.Parameters["生产车间"] = e.ScCheJian; + dto.Parameters["一茧丝长"] = e.YiJianSiChang; + dto.Parameters["解舒率"] = e.JieShuLv; + dto.Parameters["解舒丝长"] = e.JieShuSiChang; + dto.Parameters["茧层率"] = e.JianCengLv; + dto.Parameters["茧丝纤度"] = e.JianSiXianDu; + dto.Parameters["解舒光折"] = e.JieShuGuangZhe; + dto.Parameters["万吊"] = e.WanDiao; + dto.Parameters["车速"] = e.ZdsCheSu; + dto.Parameters["允许粒数"] = e.ZdsYunXuLiShu; + dto.Parameters["缫折"] = e.ZdsSaoZhe; + dto.Parameters["中心纤度"] = e.ZdsZhongXinXianDu; + dto.Parameters["中心粒符率"] = e.ZdsZhongXinLiFuLv; + dto.Parameters["缫丝温度"] = e.ZdsSaoSiWenDu; + dto.Parameters["计算纤度"] = e.ZdsJiSuanXianDu; + dto.Parameters["允许粒符率"] = e.ZdsYunXuLiFuLv; + dto.Parameters["索绪温度"] = e.ZdsSuoXuWenDu; + dto.Parameters["平均粒数"] = e.ZdsPingJunLiShu; + dto.Parameters["白茧引绪"] = e.ZdsBaiJianYinXu; + dto.Parameters["理绪温度"] = e.ZdsLiXuWenDu; + dto.Parameters["中心粒数"] = e.ZdsZhongXinLiShu; + dto.Parameters["给茧口宽度"] = e.ZdsGeiJianKouKuanDu; + dto.Parameters["落丝笼数"] = e.ZdsLuoSiLongShu; + dto.Parameters["笼茧粒数"] = e.ZdsLongJianLiShu; + dto.Parameters["煮茧车速"] = e.ZjCheSu; + dto.Parameters["蒸煮室温度"] = $"{e.ZjZhengZhuShi1}、{e.ZjZhengZhuShi2}、{e.ZjZhengZhuShi3}"; + dto.Parameters["外层中层内层掉绪"] = $"{e.ZjLuoXuWai} {e.ZjLuoXuZhong} {e.ZjLuoXuNei}"; + dto.Parameters["调整部温度"] = e.ZjTiaoZhengBuWenDu; + dto.Parameters["出口温度"] = e.ZjChuKouWenDu; + dto.Parameters["附煮车间"] = e.ZjFuZhuCheJian; + dto.Parameters["附煮庄口"] = e.ZjFuZhuGyzkName; + dto.Parameters["设计"] = e.SheJiRen; + dto.Parameters["审核"] = e.ShenHeRen; + dto.Parameters["审批"] = e.PiFuRen; + break; + } + case "ssdy": + { + var e = await GetXbjSsDyAsync(gyzkId, guige); + dto.Title = "(三)试缫与大样资料"; + dto.Parameters["庄口名称"] = zkName; + dto.Parameters["规格"] = e.Guige; + dto.Parameters["主要技术措施"] = e.JiShuCuoShi; + dto.Parameters["设计"] = e.SheJiRen; + dto.Parameters["审核"] = e.ShenHeRen; + dto.Parameters["审批"] = e.PiFuRen; + dto.Main = new List> + { + new() + { + ["项目"] = "试缫", + ["上车率"] = e.SsShangCheLv, ["联统率"] = e.SsLianTongLv, ["次茧率"] = e.SsCiJianLv, + ["次六级"] = e.SsCiLiuJiLv, ["棉茧率"] = e.SsMianJianLv, ["下脚率"] = e.SsXiaJiaoLv, + ["双宫率"] = e.SsShuangGongLv, ["茧衣率"] = e.SsJianYiLv, + ["400粒重"] = e.SsLiJianZhong, ["公斤粒数"] = e.SsGongJinLiShu + }, + new() + { + ["项目"] = "大样", + ["上车率"] = e.DyShangCheLv, ["联统率"] = e.DyLianTongLv, ["次茧率"] = e.DyCiJianLv, + ["次六级"] = e.DyCiLiuJiLv, ["棉茧率"] = e.DyMianJianLv, ["下脚率"] = e.DyXiaJiaoLv, + ["双宫率"] = e.DyShuangGongLv, ["茧衣率"] = e.DyJianYiLv, + ["400粒重"] = e.DyLiJianZhong, ["公斤粒数"] = e.DyGongJinLiShu + } + }; + break; + } + case "zhujian": + { + var e = await GetZhuJianAsync(gyzkId); + var z = e.ZhuJian; + dto.Title = "煮茧工艺执行书"; + dto.Parameters["庄口名称"] = zkName; + dto.Parameters["缫制规格"] = z.Guige; + dto.Parameters["换庄时间"] = z.HuanZhuangDate?.ToString("yyyy/M/d") ?? ""; + dto.Parameters["接缫方式"] = z.HuanZhuangFangShi; + dto.Parameters["生产车间"] = z.CheJian; + dto.Parameters["煮茧机号"] = z.ZhuJianJi; + // 庄口原料性能 + dto.Parameters["公斤粒数"] = z.YlGongJinLiShu; + dto.Parameters["缩绉"] = z.YlSuoZhou; + dto.Parameters["茧层率"] = z.YlJianCengLv; + dto.Parameters["清洁"] = z.YlQingJie; + dto.Parameters["净度"] = z.YlJingDu; + dto.Parameters["粒茧丝长"] = z.YlLiJianSiChang; + dto.Parameters["解舒率"] = z.YlJieShuLv; + dto.Parameters["解舒丝长"] = z.YlJieShuSiChang; + dto.Parameters["茧丝纤度"] = z.YlJianSiXianDu; + dto.Parameters["万米吊糙"] = z.YlWanMiDiaoCao; + dto.Parameters["解舒光折"] = z.YlJieShuGuangZhe; + // 工艺技术指标 + dto.Parameters["实缫解舒"] = z.GyzbJieShuLv; + dto.Parameters["实缫万吊"] = z.GyzbWanDiao; + dto.Parameters["工艺清洁"] = z.GyzbQingJie; + dto.Parameters["工艺净度"] = z.GyzbJingDu; + dto.Parameters["丝胶溶失率"] = z.GyzbSiJiaoRongShiLv; + dto.Parameters["组落丝笼数"] = z.GyzbZuLuoSiLongShu; + dto.Parameters["缫折"] = z.GyzbSaoZhe; + dto.Parameters["笼茧粒数"] = z.GyzbLongJianLiShu; + dto.Parameters["每桶茧量"] = z.GyzbMeiTongJianLiang; + dto.Parameters["每包桶数"] = z.GyzbMeiBaoTongShu; + dto.Parameters["每小时用茧笼数"] = z.GyzbMeiXiaoShiYongJianLong; + dto.Parameters["条吐率"] = z.GyzbTiaoTuLv; + // 真空渗透 + dto.Parameters["真空度"] = z.ZkstZhenKongDu; + dto.Parameters["吸水温度"] = z.ZkstXiShuiWenDu; + dto.Parameters["达到最高真空度时长"] = z.ZkstDaZhenKongShiChang; + dto.Parameters["液面到最高位时长"] = z.ZkstZuiGaoWeiShiChang; + dto.Parameters["关水后继续真空时长"] = z.ZkstGuanShuiZhenKongShiChang; + dto.Parameters["出茧温度"] = z.ZkstChuJianWenDu; + dto.Parameters["全机回转时间"] = z.ZkstQuanJiHuiZhuanShiJian; + dto.Parameters["主要技术措施"] = z.JiShuCuoShi; + dto.Parameters["设计"] = z.SheJiRen; + dto.Parameters["审核"] = z.ShenHeRen; + dto.Parameters["审批"] = z.PiFuRen; + dto.Sub1 = e.CheJian.Select(c => new Dictionary + { + ["车间"] = c.CheJian, ["低温渗透"] = c.DiWenShenTou, + ["蒸煮室1"] = c.ZhengZhuShi1, ["蒸煮室2"] = c.ZhengZhuShi2, ["蒸煮室3"] = c.ZhengZhuShi3, + ["调整部1"] = c.TiaoZhengBu1, ["调整部2"] = c.TiaoZhengBu2, ["调整部3"] = c.TiaoZhengBu3, + ["调整部4"] = c.TiaoZhengBu4, ["调整部5"] = c.TiaoZhengBu5, + ["出茧温度"] = c.ChuJianWenDu, ["全机回转时间"] = c.HuiZhuanShiJian + }).ToList(); + dto.Sub2 = e.FuZhu.Select(c => new Dictionary + { + ["附煮车间"] = c.CheJian, ["解舒"] = c.JieShuLv, ["万吊"] = c.WanDiao, ["缫折"] = c.SaoZhe, + ["温度1"] = c.WenDu1, ["温度2"] = c.WenDu2, ["温度3"] = c.WenDu3, + ["温度4"] = c.WenDu4, ["温度5"] = c.WenDu5 + }).ToList(); + break; + } + case "zds": + { + var e = await GetZdsAsync(gyzkId, guige); + var z = e.Zds; + dto.Title = "自动缫车间生产工艺执行书(表一)"; + dto.Parameters["庄口名称"] = zkName; + dto.Parameters["接缫日期"] = z.HuanZhuangDate?.ToString("yyyy/M/d") ?? ""; + dto.Parameters["换庄方式"] = z.HuanZhuangFangShi; + dto.Parameters["规格"] = z.Guige; + dto.Parameters["生产车间"] = z.CheJian; + dto.Parameters["预缫天数"] = z.YuSaoTianShu; + dto.Parameters["预缫丝量"] = z.YuSaoSiLiang; + // 茧质概况 + dto.Parameters["一茧丝长"] = z.ZgYiJianSiChang; + dto.Parameters["解舒丝长"] = z.ZgJieShuSiChang; + dto.Parameters["解舒率"] = z.ZgJieShuLv; + dto.Parameters["茧丝纤度"] = z.ZgJianSiXianDu; + dto.Parameters["解舒光折"] = z.ZgJieShuGuangZhe; + dto.Parameters["万米吊糙"] = z.ZgWanMiDiaoCao; + dto.Parameters["清洁分"] = z.ZgQingJie; + dto.Parameters["洁净分"] = z.ZgJingDu; + dto.Parameters["茧层率"] = z.ZgJianCengLv; + dto.Parameters["茧丝纤度均方差"] = z.ZgXianDuJunFangCha; + dto.Parameters["外三百回"] = z.ZgWaiSanBaiHui; + dto.Parameters["中三百回"] = z.ZgZhongSanBaiHui; + dto.Parameters["内三百回"] = z.ZgNeiSanBaiHui; + dto.Parameters["最大开差"] = z.ZgZuiDaKaiCha; + dto.Parameters["内外开差"] = z.ZgNeiWaiKaiCha; + dto.Parameters["白色茧单纤"] = z.ZgBaiSeJianDanXian; + dto.Parameters["灰色茧单纤"] = z.ZgHuiSeJianDanXian; + dto.Parameters["红色茧单纤"] = z.ZgHongSeJianDanXian; + dto.Parameters["落绪外"] = z.ZgLuoXuWai; + dto.Parameters["落绪中"] = z.ZgLuoXuZhong; + dto.Parameters["落绪内"] = z.ZgLuoXuNei; + dto.Parameters["平均茧幅"] = z.ZgPingJunJianFu; + dto.Parameters["茧幅整齐率"] = z.ZgJianFuZhengQiLv; + dto.Parameters["茧丝百回纤度"] = z.ZgJianSiBaiHuiXianDu; + // 生产指标 + dto.Parameters["品味"] = z.SczbPinWei; + dto.Parameters["缫折"] = z.SczbSaoZhe; + dto.Parameters["台产"] = z.SczbTaiChan; + dto.Parameters["偏差"] = z.SczbPianCha; + dto.Parameters["总差"] = z.SczbZongCha; + dto.Parameters["清洁"] = z.SczbQingJie; + dto.Parameters["洁净"] = z.SczbJingJie; + dto.Parameters["匀二变化"] = z.SczbYunEr; + dto.Parameters["生产正品率"] = z.SczbZhengPinLv; + // 工艺技术指标 + dto.Parameters["车速"] = z.GyzbCheSu; + dto.Parameters["添绪次数"] = z.GyzbTianXuCiShu; + dto.Parameters["运转率"] = z.GyzbYunZhuanLv; + dto.Parameters["中心纤度"] = z.GyzbZhongXinXianDu; + dto.Parameters["设计单纤度"] = z.GyzbSheJiDanXianDu; + dto.Parameters["平均粒数"] = z.GyzbPingJunLiShu; + dto.Parameters["中心粒数"] = z.GyzbZhongXinLiShu; + dto.Parameters["允许粒数"] = z.GyzbYunXuLiShu; + dto.Parameters["引绪"] = z.GyzbYinXu; + dto.Parameters["中心粒符率"] = z.GyzbZhongXinLiFuLv; + dto.Parameters["允许粒符率"] = z.GyzbYunXuLiFuLv; + dto.Parameters["给茧口宽度"] = z.GyzbGeiJianKouKuanDu; + dto.Parameters["条吐率"] = z.GyzbTiaoTuLv; + dto.Parameters["丝鞘长度"] = z.GyzbSiQiaoChangDu; + dto.Parameters["车头汽压"] = z.GyzbCheTouQiYa; + dto.Parameters["索绪温度"] = z.GyzbSuoXuWenDu; + dto.Parameters["理绪温度"] = z.GyzbLiXuWenDu; + dto.Parameters["偏心盘茧量"] = z.GyzbPianXinPanJianLiang; + dto.Parameters["缫汤温度"] = z.GyzbSaoTangWenDu; + dto.Parameters["加茧口茧量"] = z.GyzbJiaJianKouJianLiang; + dto.Parameters["无绪率"] = z.GyzbWuXuLv; + dto.Parameters["蓬糙茧率"] = z.GyzbPengCaoJianLv; + dto.Parameters["加茧均匀率"] = z.GyzbJiaJianJunYunLv; + dto.Parameters["正确添绪率"] = z.GyzbZhengQueTianXuLv; + dto.Parameters["桶茧量"] = z.GyzbTongJianLiang; + dto.Parameters["落丝笼数"] = z.GyzbLuoSiLongShu; + dto.Parameters["设计"] = z.SheJiRen; + dto.Parameters["审核"] = z.ShenHeRen; + dto.Parameters["审批"] = z.PiFuRen; + break; + } + case "zdsdy": + { + var e = await GetZdsAsync(gyzkId, guige); + var d = e.Dyzl ?? new GyzxsZdsDyzl { GyzkId = gyzkId, Guige = guige ?? "" }; + dto.Title = "自动缫大样(表二)"; + dto.Parameters["庄口名称"] = zkName; + dto.Parameters["规格"] = d.Guige; + dto.Parameters["偏差"] = d.PianCha; + dto.Parameters["清洁"] = d.QingJie; + dto.Parameters["净度"] = d.JingDu; + dto.Parameters["解舒率"] = d.JieShuLv; + dto.Parameters["万米吊糙"] = d.WanMiDiaoCao; + dto.Parameters["缫折"] = d.SaoZhe; + dto.Parameters["技术措施及要求"] = d.JiShuCuoShi; + dto.Parameters["设计"] = d.SheJiRen; + dto.Parameters["审核"] = d.ShenHeRen; + dto.Parameters["审批"] = d.PiFuRen; + break; + } + default: + throw new ServiceException($"未知的报表类型:{type}"); + } + + return dto; + } + + // ==================== 内部工具 ==================== + + private static async Task UpsertAsync(IRepository repo, T dto, T? exist) where T : class, IEntity + { + if (exist == null) + { + dto.Id = 0; + dto.IsDeleted = false; + await repo.AddAsync(dto); + return dto.Id; + } + dto.Id = exist.Id; + dto.CreatedAt = exist.CreatedAt; + dto.IsDeleted = false; + await repo.UpdateAsync(dto); + return dto.Id; + } + + private static string FormatRange(DateTime? start, DateTime? end) + { + if (start == null && end == null) return ""; + var s = start?.ToString("yy年M月d日") ?? ""; + var e2 = end?.ToString("yy年M月d日") ?? ""; + return string.IsNullOrEmpty(e2) ? s : $"{s}-{e2}"; + } +} diff --git a/backend/F9WebAiTest.Application/Services/QC/YjssService.cs b/backend/F9WebAiTest.Application/Services/QC/YjssService.cs new file mode 100644 index 0000000..a7345b2 --- /dev/null +++ b/backend/F9WebAiTest.Application/Services/QC/YjssService.cs @@ -0,0 +1,551 @@ +using F9WebAiTest.Application.Common; +using F9WebAiTest.Application.Dtos.QC; +using F9WebAiTest.Domain.Common; +using F9WebAiTest.Domain.Entities.Base; +using F9WebAiTest.Domain.Entities.QC.Yjss; +using F9WebAiTest.Domain.Entities.SYS.YuanliaoZhuangkous; + +namespace F9WebAiTest.Application.Services.QC; + +public interface IYjssService +{ + Task> GetPagedAsync(int pageIndex, int pageSize, int type, int year, string keyword); + Task SaveYlxyAsync(YjssMainYlxy dto); + Task SaveZhyAsync(YjssMainZhy dto); + Task DeleteAsync(int type, long id); + + Task GetXjdcAsync(int type, long yjssId); + Task SaveXjdcAsync(int type, YjssDcXjdc dto); + + Task> GetJsdcAsync(int type, long yjssId); + Task SaveJsdcAsync(int type, YjssDcJsdc dto); + Task DeleteJsdcAsync(int type, long id); + + Task GetJfdcAsync(int type, long yjssId); + Task SaveJfdcAsync(int type, YjssJfdcDto dto); + + Task GetQpdcAsync(int type, long yjssId); + Task SaveQpdcAsync(int type, YjssDcQpdc dto); + + Task GetQjjjAsync(int type, long yjssId); + Task SaveQjjjAsync(int type, YjssDcQjjj dto); + + Task GetJsbhxdAsync(int type, long yjssId); + Task SaveJsbhxdAsync(int type, YjssJsbhxdDto dto); + + /// 整页数据(含报表参数区):用于「综合样报表 / 原料小样报表 / 解舒调查报表」 + Task GetFullAsync(int type, long yjssId); + /// 解舒调查平均值汇总(跨多条记录,口径对齐 F9 GetDataByYjssCommand) + Task> GetJsdcSummaryAsync(int type, long yjssId); +} + +/// +/// 质量模块 —— 样茧试缫(对应 F9 生产\质量\样茧试缫)。 +/// 两类样品:原料小样(选茧 / 解舒 / 清洁洁净)、综合样(加 茧幅 / 切剖 / 茧丝百回纤度)。 +/// 子表统一外键 yjss_id;一对一子表保存后回写主表指针,解舒/百回纤度回写记录数。 +/// +public class YjssService : IYjssService +{ + private readonly IRepository _ylxyRepo; + private readonly IRepository _zhyRepo; + private readonly IRepository _xjdcRepo; + private readonly IRepository _jsdcRepo; + private readonly IRepository _jfdcRepo; + private readonly IRepository _jfdcItemRepo; + private readonly IRepository _qpdcRepo; + private readonly IRepository _qjjjRepo; + private readonly IRepository _jsbhxdMainRepo; + private readonly IRepository _jsbhxdRepo; + private readonly IRepository _ylzkRepo; + private readonly IRepository _gyzkRepo; + + public YjssService( + IRepository ylxyRepo, + IRepository zhyRepo, + IRepository xjdcRepo, + IRepository jsdcRepo, + IRepository jfdcRepo, + IRepository jfdcItemRepo, + IRepository qpdcRepo, + IRepository qjjjRepo, + IRepository jsbhxdMainRepo, + IRepository jsbhxdRepo, + IRepository ylzkRepo, + IRepository gyzkRepo) + { + _ylxyRepo = ylxyRepo; + _zhyRepo = zhyRepo; + _xjdcRepo = xjdcRepo; + _jsdcRepo = jsdcRepo; + _jfdcRepo = jfdcRepo; + _jfdcItemRepo = jfdcItemRepo; + _qpdcRepo = qpdcRepo; + _qjjjRepo = qjjjRepo; + _jsbhxdMainRepo = jsbhxdMainRepo; + _jsbhxdRepo = jsbhxdRepo; + _ylzkRepo = ylzkRepo; + _gyzkRepo = gyzkRepo; + } + + private static string SampleTypeName(int t) => t switch + { + 1 => "定价抽样", + 2 => "入库抽样", + 3 => "鲜茧抽样", + _ => "" + }; + + // ==================== 主表 ==================== + + public async Task> GetPagedAsync(int pageIndex, int pageSize, int type, int year, string keyword) + { + if (year <= 0) year = DateTime.Today.Year; + var items = new List(); + + var ylzks = (await _ylzkRepo.FindAsync(x => !x.IsDeleted)).ToDictionary(x => x.Id, x => x.Name); + var gyzks = (await _gyzkRepo.FindAsync(x => !x.IsDeleted)).ToDictionary(x => x.Id, x => x.ZhuangKouName); + + if (type == 1) + { + var rows = await _ylxyRepo.FindAsync(x => !x.IsDeleted && x.Date.Year == year); + items = rows.Select(x => new YjssListItemDto + { + Id = x.Id, Type = 1, ZhuangKouId = x.YlzkId, + ZhuangKouName = ylzks.GetValueOrDefault(x.YlzkId, ""), + Date = x.Date, SampleType = x.Type, SampleTypeName = SampleTypeName(x.Type), + ZhongLiang = x.ZhongLiang, HuiChaoLv = x.HuiChaoLv, + XjdcId = x.XjdcId, JsdcCt = x.JsdcCt, QjjjId = x.QjjjId, Memo = x.Memo + }).ToList(); + } + else + { + var rows = await _zhyRepo.FindAsync(x => !x.IsDeleted && x.Date.Year == year); + items = rows.Select(x => new YjssListItemDto + { + Id = x.Id, Type = 2, ZhuangKouId = x.GyzkId, + ZhuangKouName = gyzks.GetValueOrDefault(x.GyzkId, ""), + Date = x.Date, SampleType = 2, SampleTypeName = "综合样", + ZhongLiang = x.ZhongLiang, XjdcId = x.XjdcId, JsdcCt = x.JsdcCt, + JfdcId = x.JfdcId, QpdcId = x.QpdcId, QjjjId = x.QjjjId, + JsbhxdCt = x.JsbhxdCt, Memo = x.Memo + }).ToList(); + } + + if (!string.IsNullOrWhiteSpace(keyword)) + items = items.Where(x => x.ZhuangKouName.Contains(keyword)).ToList(); + + items = items.OrderByDescending(x => x.Date).ThenByDescending(x => x.Id).ToList(); + var total = items.Count; + return new PageResult + { + Total = total, PageIndex = pageIndex, PageSize = pageSize, + Items = items.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList() + }; + } + + public async Task SaveYlxyAsync(YjssMainYlxy dto) + { + if (dto.YlzkId <= 0) throw new ServiceException("请选择原料庄口"); + if (dto.Date == default) dto.Date = DateTime.Today; + + var exist = dto.Id > 0 ? await _ylxyRepo.GetByIdAsync(dto.Id) : null; + if (exist == null) + { + dto.Id = 0; dto.IsDeleted = false; + await _ylxyRepo.AddAsync(dto); + } + else + { + dto.CreatedAt = exist.CreatedAt; dto.IsDeleted = false; + await _ylxyRepo.UpdateAsync(dto); + } + var name = (await _ylzkRepo.GetByIdAsync(dto.YlzkId))?.Name ?? ""; + return new YjssListItemDto + { + Id = dto.Id, Type = 1, ZhuangKouId = dto.YlzkId, ZhuangKouName = name, + Date = dto.Date, SampleType = dto.Type, SampleTypeName = SampleTypeName(dto.Type), + ZhongLiang = dto.ZhongLiang, HuiChaoLv = dto.HuiChaoLv, + XjdcId = dto.XjdcId, JsdcCt = dto.JsdcCt, QjjjId = dto.QjjjId, Memo = dto.Memo + }; + } + + public async Task SaveZhyAsync(YjssMainZhy dto) + { + if (dto.GyzkId <= 0) throw new ServiceException("请选择工艺庄口"); + if (dto.Date == default) dto.Date = DateTime.Today; + + var exist = dto.Id > 0 ? await _zhyRepo.GetByIdAsync(dto.Id) : null; + if (exist == null) + { + dto.Id = 0; dto.IsDeleted = false; + await _zhyRepo.AddAsync(dto); + } + else + { + dto.CreatedAt = exist.CreatedAt; dto.IsDeleted = false; + await _zhyRepo.UpdateAsync(dto); + } + var name = (await _gyzkRepo.GetByIdAsync(dto.GyzkId))?.ZhuangKouName ?? ""; + return new YjssListItemDto + { + Id = dto.Id, Type = 2, ZhuangKouId = dto.GyzkId, ZhuangKouName = name, + Date = dto.Date, SampleType = 2, SampleTypeName = "综合样", + ZhongLiang = dto.ZhongLiang, XjdcId = dto.XjdcId, JsdcCt = dto.JsdcCt, + JfdcId = dto.JfdcId, QpdcId = dto.QpdcId, QjjjId = dto.QjjjId, + JsbhxdCt = dto.JsbhxdCt, Memo = dto.Memo + }; + } + + public async Task DeleteAsync(int type, long id) + { + if (type == 1) + { + var e = await _ylxyRepo.GetByIdAsync(id) ?? throw new ServiceException("记录不存在", 404); + await _ylxyRepo.DeleteAsync(e); + } + else + { + var e = await _zhyRepo.GetByIdAsync(id) ?? throw new ServiceException("记录不存在", 404); + await _zhyRepo.DeleteAsync(e); + } + // 连带软删除子单据(按 主表 Id + 样品类型 精确定位) + foreach (var x in await _xjdcRepo.FindAsync(x => !x.IsDeleted && x.YjssId == id && x.YjssType == type)) await _xjdcRepo.DeleteAsync(x); + foreach (var x in await _jsdcRepo.FindAsync(x => !x.IsDeleted && x.YjssId == id && x.YjssType == type)) await _jsdcRepo.DeleteAsync(x); + foreach (var x in await _jfdcItemRepo.FindAsync(x => !x.IsDeleted && x.YjssId == id && x.YjssType == type)) await _jfdcItemRepo.DeleteAsync(x); + foreach (var x in await _jfdcRepo.FindAsync(x => !x.IsDeleted && x.YjssId == id && x.YjssType == type)) await _jfdcRepo.DeleteAsync(x); + foreach (var x in await _qpdcRepo.FindAsync(x => !x.IsDeleted && x.YjssId == id && x.YjssType == type)) await _qpdcRepo.DeleteAsync(x); + foreach (var x in await _qjjjRepo.FindAsync(x => !x.IsDeleted && x.YjssId == id && x.YjssType == type)) await _qjjjRepo.DeleteAsync(x); + foreach (var x in await _jsbhxdRepo.FindAsync(x => !x.IsDeleted && x.YjssId == id && x.YjssType == type)) await _jsbhxdRepo.DeleteAsync(x); + foreach (var x in await _jsbhxdMainRepo.FindAsync(x => !x.IsDeleted && x.YjssId == id && x.YjssType == type)) await _jsbhxdMainRepo.DeleteAsync(x); + } + + // ==================== 选茧调查 ==================== + + public async Task GetXjdcAsync(int type, long yjssId) + => await _xjdcRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.YjssId == yjssId && x.YjssType == type) + ?? new YjssDcXjdc { YjssId = yjssId, YjssType = type }; + + public async Task SaveXjdcAsync(int type, YjssDcXjdc dto) + { + dto.YjssType = type; + var exist = await _xjdcRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.YjssId == dto.YjssId && x.YjssType == type); + var id = await UpsertAsync(_xjdcRepo, dto, exist); + await SetParentPointerAsync(type, dto.YjssId, xjdcId: id, qjjjId: null, jfdcId: null, qpdcId: null, jsdcCt: null, jsbhxdCt: null); + return true; + } + + // ==================== 解舒调查(多条) ==================== + + public async Task> GetJsdcAsync(int type, long yjssId) + => (await _jsdcRepo.FindAsync(x => !x.IsDeleted && x.YjssId == yjssId && x.YjssType == type)) + .OrderBy(x => x.XuHao).ThenBy(x => x.Id).ToList(); + + public async Task SaveJsdcAsync(int type, YjssDcJsdc dto) + { + dto.YjssType = type; + var exist = dto.Id > 0 ? await _jsdcRepo.GetByIdAsync(dto.Id) : null; + if (exist == null) + { + dto.Id = 0; dto.IsDeleted = false; + if (dto.XuHao <= 0) dto.XuHao = (await GetJsdcAsync(type, dto.YjssId)).Count + 1; + await _jsdcRepo.AddAsync(dto); + } + else + { + dto.CreatedAt = exist.CreatedAt; dto.IsDeleted = false; + await _jsdcRepo.UpdateAsync(dto); + } + var ct = (await _jsdcRepo.FindAsync(x => !x.IsDeleted && x.YjssId == dto.YjssId && x.YjssType == type)).Count; + await SetParentPointerAsync(type, dto.YjssId, null, null, null, null, ct, null); + return true; + } + + public async Task DeleteJsdcAsync(int type, long id) + { + var e = await _jsdcRepo.GetByIdAsync(id) ?? throw new ServiceException("记录不存在", 404); + await _jsdcRepo.DeleteAsync(e); + var ct = (await _jsdcRepo.FindAsync(x => !x.IsDeleted && x.YjssId == e.YjssId && x.YjssType == e.YjssType)).Count; + await SetParentPointerAsync(type, e.YjssId, null, null, null, null, ct, null); + } + + // ==================== 茧幅调查 ==================== + + public async Task GetJfdcAsync(int type, long yjssId) + { + var main = await _jfdcRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.YjssId == yjssId && x.YjssType == type); + var dto = new YjssJfdcDto { Main = main ?? new YjssDcJfdcMain { YjssId = yjssId, YjssType = type } }; + if (main != null) + dto.Items = (await _jfdcItemRepo.FindAsync(x => !x.IsDeleted && x.JfdcId == main.Id)) + .OrderBy(x => x.Sort).ThenBy(x => x.JianFu).ToList(); + return dto; + } + + public async Task SaveJfdcAsync(int type, YjssJfdcDto dto) + { + dto.Main.YjssType = type; + var exist = await _jfdcRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.YjssId == dto.Main.YjssId && x.YjssType == type); + var id = await UpsertAsync(_jfdcRepo, dto.Main, exist); + + var olds = await _jfdcItemRepo.FindAsync(x => x.JfdcId == id); + if (olds.Count > 0) await _jfdcItemRepo.HardDeleteRangeAsync(olds); + var rows = dto.Items ?? new List(); + for (var i = 0; i < rows.Count; i++) + { + rows[i].Id = 0; rows[i].JfdcId = id; rows[i].YjssId = dto.Main.YjssId; + rows[i].YjssType = type; rows[i].Sort = i + 1; + await _jfdcItemRepo.AddAsync(rows[i]); + } + await SetParentPointerAsync(type, dto.Main.YjssId, null, null, id, null, null, null); + return true; + } + + // ==================== 切剖调查 ==================== + + public async Task GetQpdcAsync(int type, long yjssId) + => await _qpdcRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.YjssId == yjssId && x.YjssType == type) + ?? new YjssDcQpdc { YjssId = yjssId, YjssType = type }; + + public async Task SaveQpdcAsync(int type, YjssDcQpdc dto) + { + dto.YjssType = type; + var exist = await _qpdcRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.YjssId == dto.YjssId && x.YjssType == type); + var id = await UpsertAsync(_qpdcRepo, dto, exist); + await SetParentPointerAsync(type, dto.YjssId, null, null, null, id, null, null); + return true; + } + + // ==================== 清洁洁净 ==================== + + public async Task GetQjjjAsync(int type, long yjssId) + => await _qjjjRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.YjssId == yjssId && x.YjssType == type) + ?? new YjssDcQjjj { YjssId = yjssId, YjssType = type }; + + public async Task SaveQjjjAsync(int type, YjssDcQjjj dto) + { + dto.YjssType = type; + var exist = await _qjjjRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.YjssId == dto.YjssId && x.YjssType == type); + var id = await UpsertAsync(_qjjjRepo, dto, exist); + await SetParentPointerAsync(type, dto.YjssId, null, id, null, null, null, null); + return true; + } + + // ==================== 茧丝百回纤度 ==================== + + public async Task GetJsbhxdAsync(int type, long yjssId) + { + var main = await _jsbhxdMainRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.YjssId == yjssId && x.YjssType == type); + var dto = new YjssJsbhxdDto { Main = main ?? new YjssDcJsbhxdMain { YjssId = yjssId, YjssType = type } }; + if (main != null) + dto.Items = (await _jsbhxdRepo.FindAsync(x => !x.IsDeleted && x.JsbhxdMainId == main.Id)) + .OrderBy(x => x.HuiShu).ToList(); + return dto; + } + + public async Task SaveJsbhxdAsync(int type, YjssJsbhxdDto dto) + { + dto.Main.YjssType = type; + var exist = await _jsbhxdMainRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.YjssId == dto.Main.YjssId && x.YjssType == type); + var id = await UpsertAsync(_jsbhxdMainRepo, dto.Main, exist); + + var olds = await _jsbhxdRepo.FindAsync(x => x.JsbhxdMainId == id); + if (olds.Count > 0) await _jsbhxdRepo.HardDeleteRangeAsync(olds); + var rows = dto.Items ?? new List(); + for (var i = 0; i < rows.Count; i++) + { + rows[i].Id = 0; rows[i].JsbhxdMainId = id; rows[i].YjssId = dto.Main.YjssId; + rows[i].YjssType = type; rows[i].Sort = i + 1; + await _jsbhxdRepo.AddAsync(rows[i]); + } + await SetParentPointerAsync(type, dto.Main.YjssId, null, null, null, null, null, rows.Count); + return true; + } + + // ==================== 整页 / 报表 ==================== + + public async Task GetFullAsync(int type, long yjssId) + { + var dto = new YjssFullDto { Type = type }; + if (type == 1) + { + var m = await _ylxyRepo.GetByIdAsync(yjssId) ?? throw new ServiceException("原料小样不存在", 404); + dto.ZhuangKouName = (await _ylzkRepo.GetByIdAsync(m.YlzkId))?.Name ?? ""; + dto.Date = m.Date; dto.ZhongLiang = m.ZhongLiang; dto.HuiChaoLv = m.HuiChaoLv; + dto.SampleTypeName = SampleTypeName(m.Type); dto.Memo = m.Memo; + } + else + { + var m = await _zhyRepo.GetByIdAsync(yjssId) ?? throw new ServiceException("综合样不存在", 404); + dto.ZhuangKouName = (await _gyzkRepo.GetByIdAsync(m.GyzkId))?.ZhuangKouName ?? ""; + dto.Date = m.Date; dto.ZhongLiang = m.ZhongLiang; dto.SampleTypeName = "综合样"; dto.Memo = m.Memo; + } + + var xjdc = await _xjdcRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.YjssId == yjssId && x.YjssType == type); + if (xjdc != null) dto.Xjdc = xjdc; + + dto.Jsdc = await GetJsdcAsync(type, yjssId); + + var jfdcMain = await _jfdcRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.YjssId == yjssId && x.YjssType == type); + if (jfdcMain != null) + dto.Jfdc = new YjssJfdcDto + { + Main = jfdcMain, + Items = (await _jfdcItemRepo.FindAsync(x => !x.IsDeleted && x.JfdcId == jfdcMain.Id)).OrderBy(x => x.JianFu).ToList() + }; + + dto.Qpdc = await _qpdcRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.YjssId == yjssId && x.YjssType == type); + dto.Qjjj = await _qjjjRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.YjssId == yjssId && x.YjssType == type); + + var bhMain = await _jsbhxdMainRepo.FirstOrDefaultAsync(x => !x.IsDeleted && x.YjssId == yjssId && x.YjssType == type); + if (bhMain != null) + dto.Jsbhxd = new YjssJsbhxdDto + { + Main = bhMain, + Items = (await _jsbhxdRepo.FindAsync(x => !x.IsDeleted && x.JsbhxdMainId == bhMain.Id)).OrderBy(x => x.HuiShu).ToList() + }; + + BuildParameters(dto); + return dto; + } + + public async Task> GetJsdcSummaryAsync(int type, long yjssId) + { + var list = await GetJsdcAsync(type, yjssId); + var valid = list.Where(x => x.YiJianSiChang > 0).ToList(); + decimal Avg(Func f) + { + var v = list.Where(x => f(x) > 0).Select(f).ToList(); + return v.Count == 0 ? 0 : Math.Round(v.Average(), 2, MidpointRounding.AwayFromZero); + } + return new Dictionary + { + ["解舒样茧粒数"] = valid.Select(x => x.JieShuLiShu).DefaultIfEmpty(0).Max(), + ["解舒样茧重量"] = valid.Select(x => x.JieShuZhongLiang).DefaultIfEmpty(0).Max(), + ["一茧丝长"] = Avg(x => x.YiJianSiChang), + ["解舒率"] = Avg(x => x.JieShuLv), + ["解舒丝长"] = Avg(x => x.JieShuSiChang), + ["茧丝纤度"] = Avg(x => x.JianSiXianDu), + ["解舒光折"] = Avg(x => x.JieShuGuangZhe), + ["万米吊糙"] = Avg(x => x.XwWanMiDiaoCao), + ["落绪外"] = Math.Round(list.Where(x => x.LdfbWai > 0).Select(x => (decimal)x.LdfbWai).DefaultIfEmpty(0).Average(), 0, MidpointRounding.AwayFromZero), + ["落绪中"] = Math.Round(list.Where(x => x.LdfbZhong > 0).Select(x => (decimal)x.LdfbZhong).DefaultIfEmpty(0).Average(), 0, MidpointRounding.AwayFromZero), + ["落绪内"] = Math.Round(list.Where(x => x.LdfbNei > 0).Select(x => (decimal)x.LdfbNei).DefaultIfEmpty(0).Average(), 0, MidpointRounding.AwayFromZero), + ["记录数"] = list.Count + }; + } + + /// 按 F9 RepDt_Qc_Yjss_* 的中文键组装报表参数区 + private static void BuildParameters(YjssFullDto dto) + { + var p = dto.Parameters; + p["查询时间"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm"); + p["庄口名称"] = dto.ZhuangKouName; + p["日期"] = dto.Date.ToString("yyyy/M/d"); + p["样茧重量"] = dto.ZhongLiang; + p["类型"] = dto.SampleTypeName; + p["回潮率"] = dto.HuiChaoLv; + p["备注"] = dto.Memo; + + var x = dto.Xjdc; + if (x != null) + { + p["公斤茧粒"] = x.GongJinLiShu; + p["解舒样茧粒数"] = x.JieShuLiShu; + p["解舒样茧重量"] = x.JieShuZhongLiang; + p["茧重联统茧"] = x.ZlLtj; p["茧重好五级茧"] = x.ZlHwj; p["茧重五级茧"] = x.ZlWj; + p["茧重棉茧"] = x.ZlMianJian; p["茧重黄斑"] = x.ZlHuangBan; p["茧重蛆茧"] = x.ZlQuJian; + p["茧重双宫"] = x.ZlShuangGong; p["茧重血茧"] = x.ZlXueJian; p["茧重薄皮"] = x.ZlBaoPi; + p["茧重茧衣"] = x.ZlJianYi; p["茧重霉茧"] = x.ZlMeiJian; + p["比例联统茧"] = x.BlLtj; p["比例好五级茧"] = x.BlHwj; p["比例五级茧"] = x.BlWj; + p["比例棉茧"] = x.BlMianJian; p["比例黄斑"] = x.BlHuangBan; p["比例蛆茧"] = x.BlQuJian; + p["比例双宫"] = x.BlShuangGong; p["比例血茧"] = x.BlXueJian; p["比例薄皮"] = x.BlBaoPi; + p["比例茧衣"] = x.BlJianYi; p["比例霉茧"] = x.BlMeiJian; + } + + var q = dto.Qpdc; + if (q != null) + { + p["茧层率"] = q.JianCengLv; p["公量茧层率"] = q.JianCengLvGongLiang; p["脱胶率"] = q.TuoJiaoLv; + p["原量全茧"] = q.YlQuanJian; p["原量茧层"] = q.YlJianCeng; p["原量蛹体"] = q.YlYongTi; + p["干量煮茧后"] = q.GlZhuJian; p["干量茧层"] = q.GlJianCeng; p["干量蛹体"] = q.GlYongTi; + p["粒数僵蚕"] = q.LsJiangCan; p["粒数内染"] = q.LsNeiRan; p["粒数霉蛹"] = q.LsMeiYong; p["粒数毛脚"] = q.LsMaoJiao; + p["比例僵蚕"] = q.BlJiangCan; p["比例内染"] = q.BlNeiRan; p["比例霉蛹"] = q.BlMeiYong; p["比例毛脚"] = q.BlMaoJiao; + } + + var jj = dto.Qjjj; + if (jj != null) + { + p["清洁"] = jj.QingJie; p["洁净"] = jj.JingJie; p["片数"] = jj.PianShu; + p["一度"] = jj.YdYiDu; p["二度"] = jj.YdErDu; p["三度"] = jj.YdSanDu; + p["洁净100"] = jj.Jj100; p["洁净95"] = jj.Jj95; p["洁净90"] = jj.Jj90; p["洁净85"] = jj.Jj85; + p["洁净80"] = jj.Jj80; p["洁净75"] = jj.Jj75; p["洁净70"] = jj.Jj70; p["洁净65"] = jj.Jj65; p["洁净60"] = jj.Jj60; + p["特大糙"] = jj.QjTdc; p["废丝"] = jj.QjFs; p["大糙"] = jj.QjDc; p["黏附糙"] = jj.QjNfc; + p["大长结"] = jj.QjDcj; p["重螺旋"] = jj.QjZlx; + p["小糙"] = jj.QjXc; p["长结"] = jj.QjCj; p["螺旋"] = jj.QjLx; p["环结及裂丝"] = jj.QjHjls; + } + + if (dto.Jfdc != null) + { + p["平均茧幅"] = dto.Jfdc.Main.PingJunJianFu; + p["最大茧幅"] = dto.Jfdc.Main.ZuiDaJianFu; + p["茧幅整齐率"] = dto.Jfdc.Main.ZhengQiLv; + p["茧幅均方差"] = dto.Jfdc.Main.JunFangCha; + } + + if (dto.Jsbhxd != null) + { + p["茧丝纤度均方差"] = dto.Jsbhxd.Main.XianDuJunFangCha; + p["最大开差"] = dto.Jsbhxd.Main.ZuiDaKaiCha; + p["内外开差"] = dto.Jsbhxd.Main.NeiWaiKaiCha; + p["外三百回"] = dto.Jsbhxd.Main.WaiSanBaiHui; + p["中三百回"] = dto.Jsbhxd.Main.ZhongSanBaiHui; + p["内三百回"] = dto.Jsbhxd.Main.NeiSanBaiHui; + p["白色茧单纤"] = dto.Jsbhxd.Main.BaiSeJianDanXian; + p["灰色茧单纤"] = dto.Jsbhxd.Main.HuiSeJianDanXian; + p["红色茧单纤"] = dto.Jsbhxd.Main.HongSeJianDanXian; + } + } + + // ==================== 内部工具 ==================== + + private async Task SetParentPointerAsync(int type, long yjssId, + long? xjdcId, long? qjjjId, long? jfdcId, long? qpdcId, int? jsdcCt, int? jsbhxdCt) + { + if (type == 1) + { + var m = await _ylxyRepo.GetByIdAsync(yjssId); + if (m == null) return; + if (xjdcId.HasValue) m.XjdcId = xjdcId.Value; + if (qjjjId.HasValue) m.QjjjId = qjjjId.Value; + if (jsdcCt.HasValue) m.JsdcCt = jsdcCt.Value; + await _ylxyRepo.UpdateAsync(m); + } + else + { + var m = await _zhyRepo.GetByIdAsync(yjssId); + if (m == null) return; + if (xjdcId.HasValue) m.XjdcId = xjdcId.Value; + if (qjjjId.HasValue) m.QjjjId = qjjjId.Value; + if (jfdcId.HasValue) m.JfdcId = jfdcId.Value; + if (qpdcId.HasValue) m.QpdcId = qpdcId.Value; + if (jsdcCt.HasValue) m.JsdcCt = jsdcCt.Value; + if (jsbhxdCt.HasValue) m.JsbhxdCt = jsbhxdCt.Value; + await _zhyRepo.UpdateAsync(m); + } + } + + private static async Task UpsertAsync(IRepository repo, T dto, T? exist) where T : class, IEntity + { + if (exist == null) + { + dto.Id = 0; + dto.IsDeleted = false; + await repo.AddAsync(dto); + return dto.Id; + } + dto.Id = exist.Id; + dto.CreatedAt = exist.CreatedAt; + dto.IsDeleted = false; + await repo.UpdateAsync(dto); + return dto.Id; + } +} diff --git a/backend/F9WebAiTest.Application/Services/Reports/IReportService.cs b/backend/F9WebAiTest.Application/Services/Reports/IReportService.cs new file mode 100644 index 0000000..8a2404b --- /dev/null +++ b/backend/F9WebAiTest.Application/Services/Reports/IReportService.cs @@ -0,0 +1,41 @@ +using F9WebAiTest.Application.Common; +using F9WebAiTest.Application.Dtos.SYS.Reports; +using F9WebAiTest.Domain.Entities.SYS.Reports; + +namespace F9WebAiTest.Application.Services.Reports; + +public interface IReportService +{ + Task> GetPagedAsync(string like, string category, bool? isActive, int pageIndex, int pageSize); + Task GetByIdAsync(long id); + Task CreateAsync(ReportCreateDto dto); + Task UpdateAsync(long id, ReportUpdateDto dto); + Task DeleteAsync(long id); + + /// 上传 .grf 模板文件(f9designer:// 本机设计器协议回传用);常规保存走 Create/Update 的 templateJson + Task UploadTemplateAsync(long id, byte[] data, string fileName); + + /// 直接保存模板文本(前端在线编辑模板源码用,免下载 / 上传),返回保存后的字符数 + Task SaveTemplateContentAsync(long id, string content); + + /// 读取报表模板内容(ReportFile.DesignContent,.grf 的 JSON 文本);无模板返回 null + Task GetTemplateContentAsync(Report report); + + /// 用户可见报表(按权限码过滤,admin 返回全部启用报表) + Task> GetVisibleReportsAsync(long userId, bool isAdmin, IReadOnlyCollection permissions); + + /// 校验用户是否拥有某报表的查看权限(admin 视为通过) + Task EnsureReportAccessAsync(long userId, bool isAdmin, IReadOnlyCollection permissions, long reportId); + + /// 执行报表数据查询 SQL,返回 Grid++Report 约定的 JSON 形态 + Task> ExecuteDataAsync(Report report, IReadOnlyDictionary parameters); + + /// 列出所有启用的报表(角色管理-报表权限分配时使用) + Task> GetAllActiveAsync(); + + /// 数据源清单(IReportDataProvider 注册表 + 各自声明的查询参数),新增报表自动配置用 + Task> GetDataSourcesAsync(); + + /// 探测数据源结构:按 DsName / DataSql 执行一次查询,识别明细列与参数 + Task ProbeAsync(ReportProbeRequestDto request); +} diff --git a/backend/F9WebAiTest.Application/Services/Reports/Providers/RepDt_Prod_Lsjl.cs b/backend/F9WebAiTest.Application/Services/Reports/Providers/RepDt_Prod_Lsjl.cs new file mode 100644 index 0000000..6cd9090 --- /dev/null +++ b/backend/F9WebAiTest.Application/Services/Reports/Providers/RepDt_Prod_Lsjl.cs @@ -0,0 +1,47 @@ +using F9WebAiTest.Application.CQRS.SYS.Reports.Queries; +using F9WebAiTest.Application.Dtos.SYS.Reports; +using F9WebAiTest.Application.Interfaces.Reports; +using MediatR; + +namespace F9WebAiTest.Application.Services.Reports.Providers; + +/// +/// 落丝记录报表数据提供者(生产-前缫)。 +/// DsName = "RepDt_Prod_Lsjl" +/// 参数:q_date_start / q_date_end(日期范围)、q_dep(车间,含子车间)、q_jizu(机组)、 +/// q_zk(庄口 Id,选择页取值)、q_like(关键字:庄口 / 规格 / 颜色)。 +/// 参数与列完全由查询结果/报表配置决定,Provider 只负责取数。 +/// +public class RepDt_Prod_Lsjl : IReportDataProvider +{ + public string ReportName => "RepDt_Prod_Lsjl"; + + public async Task GetData(Dictionary pairs, IMediator mediator) + { + DateTime? dateStart = null; + if (pairs.TryGetValue("q_date_start", out var ds) && ds != null && ds.ToString() is { Length: > 0 } s1 + && DateTime.TryParse(s1, out var d1)) + dateStart = d1; + + DateTime? dateEnd = null; + if (pairs.TryGetValue("q_date_end", out var de) && de != null && de.ToString() is { Length: > 0 } s2 + && DateTime.TryParse(s2, out var d2)) + dateEnd = d2; + + long depId = 0; + if (pairs.TryGetValue("q_dep", out var dep) && dep != null && long.TryParse(dep.ToString(), out var d)) + depId = d; + + int jizu = 0; + if (pairs.TryGetValue("q_jizu", out var jz) && jz != null && int.TryParse(jz.ToString(), out var j)) + jizu = j; + + long gyzkId = 0; + if (pairs.TryGetValue("q_zk", out var zk) && zk != null && long.TryParse(zk.ToString(), out var z)) + gyzkId = z; + + var like = pairs.TryGetValue("q_like", out var lk) && lk != null ? lk.ToString() ?? "" : ""; + + return await mediator.Send(new GetLsjlRepQuery(dateStart, dateEnd, depId, jizu, gyzkId, like)); + } +} diff --git a/backend/F9WebAiTest.Application/Services/Reports/Providers/RepDt_Sys_Users.cs b/backend/F9WebAiTest.Application/Services/Reports/Providers/RepDt_Sys_Users.cs new file mode 100644 index 0000000..838741d --- /dev/null +++ b/backend/F9WebAiTest.Application/Services/Reports/Providers/RepDt_Sys_Users.cs @@ -0,0 +1,31 @@ +using F9WebAiTest.Application.CQRS.SYS.Reports.Queries; +using F9WebAiTest.Application.Dtos.SYS.Reports; +using F9WebAiTest.Application.Interfaces.Reports; +using MediatR; + +namespace F9WebAiTest.Application.Services.Reports.Providers; + +/// +/// 职工花名册数据提供者(对应 F9 的 IReportService 实现): +/// 解析查询参数 → 通过 MediatR 发送 CQRS 查询 → 返回含记录集与报表参数的数据源。 +/// DsName = "RepDt_Sys_Users" +/// +public class RepDt_Sys_Users : IReportDataProvider +{ + public string ReportName => "RepDt_Sys_Users"; + + public async Task GetData(Dictionary pairs, IMediator mediator) + { + // 1. 解析输入参数(部门 q_dep、关键字 q_like) + long depId = 0; + if (pairs.TryGetValue("q_dep", out var depObj) && depObj != null && long.TryParse(depObj.ToString(), out var d)) + depId = d; + + string like = ""; + if (pairs.TryGetValue("q_like", out var likeObj) && likeObj != null) + like = likeObj.ToString() ?? ""; + + // 2. 发送 CQRS 查询获取报表数据 + return await mediator.Send(new GetEmployeeDirectoryRepQuery(depId, like)); + } +} diff --git a/backend/F9WebAiTest.Application/Services/Reports/ReportService.cs b/backend/F9WebAiTest.Application/Services/Reports/ReportService.cs new file mode 100644 index 0000000..4c74ffe --- /dev/null +++ b/backend/F9WebAiTest.Application/Services/Reports/ReportService.cs @@ -0,0 +1,652 @@ +using System.Collections.Concurrent; +using System.Data; +using System.Data.Common; +using System.Reflection; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; +using FreeSql; +using F9WebAiTest.Application.Common; +using F9WebAiTest.Application.Common.Exceptions; +using F9WebAiTest.Application.Dtos.SYS.Reports; +using F9WebAiTest.Application.Interfaces.Reports; +using F9WebAiTest.Application.Interfaces.SnowflakeID; +using F9WebAiTest.Domain.Common; +using F9WebAiTest.Domain.Entities.SYS.RBAC; +using F9WebAiTest.Domain.Entities.SYS.Reports; +using F9WebAiTest.Domain.Enums.SYS.RBAC; +using MediatR; +using System.Data.SQLite; + +namespace F9WebAiTest.Application.Services.Reports; + +public class ReportService : IReportService +{ + private readonly IRepository _reportRepo; + private readonly IRepository _reportFileRepo; + private readonly IRepository _permissionRepo; + private readonly IRepository _rolePermissionRepo; + private readonly IFreeSql _fsql; + private readonly ISnowflakeIdGenerator _snowflake; + private readonly IMediator _mediator; + private readonly Dictionary _dataProviders; + + public ReportService( + IRepository reportRepo, + IRepository reportFileRepo, + IRepository permissionRepo, + IRepository rolePermissionRepo, + IFreeSql fsql, + ISnowflakeIdGenerator snowflake, + IMediator mediator, + IEnumerable dataProviders) + { + _reportRepo = reportRepo; + _reportFileRepo = reportFileRepo; + _permissionRepo = permissionRepo; + _rolePermissionRepo = rolePermissionRepo; + _fsql = fsql; + _snowflake = snowflake; + _mediator = mediator; + _dataProviders = dataProviders.ToDictionary(p => p.ReportName, StringComparer.OrdinalIgnoreCase); + } + + // ===== 报表管理 ===== + + public async Task> GetPagedAsync(string like, string category, bool? isActive, int pageIndex, int pageSize) + { + var query = _fsql.Select().Where(r => !r.IsDeleted); + if (!string.IsNullOrWhiteSpace(like)) + query = query.Where(r => r.Name.Contains(like) || r.Code.Contains(like)); + if (!string.IsNullOrWhiteSpace(category)) + query = query.Where(r => r.Category == category); + if (isActive.HasValue) + query = query.Where(r => r.IsActive == isActive.Value); + + var total = await query.CountAsync(); + var rows = await query + .OrderBy(r => r.SortOrder) + .OrderByDescending(r => r.CreatedAt) + .Page(pageIndex, pageSize) + .ToListAsync(); + + var items = rows.Select(r => new ReportListDto( + r.Id, r.Code, r.Name, r.Category, r.Description, r.SortOrder, r.IsActive, + r.ReportFileId, HasTemplate(r), r.CreatedAt, r.UpdatedAt)).ToList(); + return new PageResult { Items = items, Total = total, PageIndex = pageIndex, PageSize = pageSize }; + } + + public async Task GetByIdAsync(long id) + { + var r = await _reportRepo.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted) + ?? throw new F9NotFoundException("报表不存在"); + return ToDetail(r); + } + + public async Task CreateAsync(ReportCreateDto dto) + { + if (string.IsNullOrWhiteSpace(dto.Code)) throw new F9OperationException("报表编码不能为空"); + if (string.IsNullOrWhiteSpace(dto.Name)) throw new F9OperationException("报表名称不能为空"); + ValidateCode(dto.Code); + ValidateConfig(dto.Config); + // 有 DsName 时允许不填 SQL;否则必须为合法 SELECT + if (GetConfigDsName(dto.Config) == null) ValidateSql(dto.DataSql); + + if (await _reportRepo.AnyAsync(r => r.Code == dto.Code && !r.IsDeleted)) + throw new F9OperationException($"报表编码已存在:{dto.Code}"); + + var entity = new Report(_snowflake.CreateId(), dto.Code.Trim(), dto.Name.Trim(), + dto.Category ?? "通用", dto.Description ?? "", dto.DataSql ?? "", + dto.DataParams ?? "[]", dto.SortOrder, dto.Config ?? ""); + + // 模板与配置一起保存(模板实例写入 ReportFile,Report 持有 ReportFileId) + if (!string.IsNullOrWhiteSpace(dto.TemplateJson)) + await SaveTemplateAsync(entity, dto.TemplateJson); + + await _reportRepo.AddAsync(entity); + await _reportRepo.SaveChangesAsync(); + + // 同步创建权限单元:report:{code}:visble + var perm = new Permission(_snowflake.CreateId(), Permission_Type_Enum.Visble, + Resource_Type_Enum.报表, entity.Code, entity.Id); + perm.SetCode($"report:{entity.Code}:visble"); + await _permissionRepo.AddAsync(perm); + await _permissionRepo.SaveChangesAsync(); + + return entity.Id; + } + + public async Task UpdateAsync(long id, ReportUpdateDto dto) + { + if (string.IsNullOrWhiteSpace(dto.Code)) throw new F9OperationException("报表编码不能为空"); + if (string.IsNullOrWhiteSpace(dto.Name)) throw new F9OperationException("报表名称不能为空"); + ValidateCode(dto.Code); + ValidateConfig(dto.Config); + if (GetConfigDsName(dto.Config) == null) ValidateSql(dto.DataSql); + + var entity = await _reportRepo.FirstOrDefaultAsync(r => r.Id == id && !r.IsDeleted) + ?? throw new F9NotFoundException("报表不存在"); + + if (await _reportRepo.AnyAsync(r => r.Code == dto.Code && r.Id != id && !r.IsDeleted)) + throw new F9OperationException($"报表编码已存在:{dto.Code}"); + + var oldCode = entity.Code; + entity.UpdateBasic(dto.Code.Trim(), dto.Name.Trim(), dto.Category ?? "通用", + dto.Description ?? "", dto.DataSql ?? "", dto.DataParams ?? "[]", dto.SortOrder, dto.Config ?? ""); + entity.Activate(); + + // 模板与配置一起保存;不传模板时保留原模板 + if (!string.IsNullOrWhiteSpace(dto.TemplateJson)) + await SaveTemplateAsync(entity, dto.TemplateJson); + + await _reportRepo.UpdateAsync(entity); + await _reportRepo.SaveChangesAsync(); + + // 同步更新权限 Code(编码变更时) + if (!string.Equals(oldCode, entity.Code, StringComparison.Ordinal)) + { + var perm = await _permissionRepo.FirstOrDefaultAsync(p => + p.ResourceType == Resource_Type_Enum.报表 && p.ResourceId == entity.Id && !p.IsDeleted); + if (perm != null) + { + perm.SetCode($"report:{entity.Code}:visble"); + await _permissionRepo.UpdateAsync(perm); + await _permissionRepo.SaveChangesAsync(); + } + } + } + + public async Task DeleteAsync(long id) + { + var entity = await _reportRepo.FirstOrDefaultAsync(r => r.Id == id && !r.IsDeleted) + ?? throw new F9NotFoundException("报表不存在"); + await _reportRepo.DeleteAsync(entity); + await _reportRepo.SaveChangesAsync(); + + // 级联删除模板文件实例 + var files = await _reportFileRepo.FindAsync(f => f.ReportId == id && !f.IsDeleted); + if (files.Count > 0) + { + await _reportFileRepo.DeleteRangeAsync(files); + await _reportFileRepo.SaveChangesAsync(); + } + + // 同步删除报表关联的权限单元与角色授权 + var perms = await _permissionRepo.FindAsync(p => p.ResourceType == Resource_Type_Enum.报表 && p.ResourceId == id && !p.IsDeleted); + if (perms.Count > 0) + { + var permIds = perms.Select(p => p.Id).ToList(); + await _rolePermissionRepo.HardDeleteRangeAsync( + (await _rolePermissionRepo.FindAsync(rp => permIds.Contains(rp.PermissionId) && !rp.IsDeleted))); + await _permissionRepo.HardDeleteRangeAsync(perms); + await _permissionRepo.SaveChangesAsync(); + } + } + + public async Task UploadTemplateAsync(long id, byte[] data, string fileName) + { + if (data == null || data.Length == 0) throw new F9OperationException("模板内容为空"); + var entity = await _reportRepo.FirstOrDefaultAsync(r => r.Id == id && !r.IsDeleted) + ?? throw new F9NotFoundException("报表不存在"); + + // .grf 可能是 GR6 时代的 UTF-8 JSON,也可能是 GR7 的 "_WR" 压缩文本,按 BOM 自动识别编码 + var text = DecodeTemplateBytes(data); + await SaveTemplateAsync(entity, text); + await _reportRepo.UpdateAsync(entity); + await _reportRepo.SaveChangesAsync(); + } + + /// + /// 直接保存模板文本(前端「模板源码」在线编辑后保存)。 + /// JSON 格式模板做一次语法校验,避免保存后在设计器 / 渲染端打不开; + /// "_WR" 压缩模板无法在服务端校验,原样保存。 + /// + public async Task SaveTemplateContentAsync(long id, string content) + { + var entity = await _reportRepo.FirstOrDefaultAsync(r => r.Id == id && !r.IsDeleted) + ?? throw new F9NotFoundException("报表不存在"); + + var text = NormalizeTemplateContent(content); + var head = text.TrimStart(); + if (head.StartsWith('{') || head.StartsWith('[')) + { + try { using var _ = JsonDocument.Parse(text); } + catch (JsonException ex) { throw new F9OperationException($"模板 JSON 不合法:{ex.Message}"); } + } + + await SaveTemplateAsync(entity, text); + await _reportRepo.UpdateAsync(entity); + await _reportRepo.SaveChangesAsync(); + return text.Length; + } + + public async Task GetTemplateContentAsync(Report report) + { + var file = await FindTemplateFileAsync(report); + return file?.DesignContent; + } + + /// 查找报表的模板文件实例(优先 ReportFileId,兼容按 ReportId 查找) + private async Task FindTemplateFileAsync(Report report) + { + if (report.ReportFileId != 0) + { + var byId = await _reportFileRepo.FirstOrDefaultAsync(f => f.Id == report.ReportFileId && !f.IsDeleted); + if (byId != null) return byId; + } + return await _reportFileRepo.FirstOrDefaultAsync(f => f.ReportId == report.Id && !f.IsDeleted); + } + + /// + /// 保存模板:新建或更新 ReportFile.DesignContent,并把 Report.ReportFileId 绑定到该实例。 + /// 模板与报表配置一起保存,前端无需再单独上传 .grf 文件。 + /// 注意:只修改内存中的 report,不落库(由调用方统一保存)。 + /// + private async Task SaveTemplateAsync(Report report, string templateContent) + { + var content = NormalizeTemplateContent(templateContent); + var file = await FindTemplateFileAsync(report); + if (file != null) + { + file.UpdateContent(content); + await _reportFileRepo.UpdateAsync(file); + await _reportFileRepo.SaveChangesAsync(); + } + else + { + file = new ReportFile(_snowflake.CreateId(), report.Id, content); + await _reportFileRepo.AddAsync(file); + await _reportFileRepo.SaveChangesAsync(); + } + + if (report.ReportFileId != file.Id) report.BindReportFile(file.Id); + } + + /// + /// 规范化模板内容:仅去 BOM / 去掉首尾空白,不做格式校验。 + /// .grf 有两种格式:GR6 的 UTF-8 JSON 文本,以及 GR7 的 "_WR" 前缀压缩文本; + /// 两者都由 Grid++Report 的 HTML5 渲染库自行识别(内部 decodeWR),服务端原样保存即可。 + /// + private static string NormalizeTemplateContent(string? content) + { + if (string.IsNullOrWhiteSpace(content)) throw new F9OperationException("报表模板内容为空"); + + var text = content.Trim(); + if (text.Length > 0 && text[0] == '\uFEFF') text = text.Substring(1); + if (string.IsNullOrWhiteSpace(text)) throw new F9OperationException("报表模板内容为空"); + return text; + } + + /// 按 BOM / 内容特征解码 .grf 字节:UTF-8(BOM) / UTF-16LE(BOM) / UTF-16BE(BOM) / 无 BOM 按 UTF-8 + private static string DecodeTemplateBytes(byte[] data) + { + if (data.Length >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF) + return Encoding.UTF8.GetString(data, 3, data.Length - 3); + if (data.Length >= 2 && data[0] == 0xFF && data[1] == 0xFE) + return Encoding.Unicode.GetString(data, 2, data.Length - 2); + if (data.Length >= 2 && data[0] == 0xFE && data[1] == 0xFF) + return Encoding.BigEndianUnicode.GetString(data, 2, data.Length - 2); + + // 无 BOM:先按 UTF-8 严格解码,失败则按 UTF-16LE 尝试 + try + { + var strict = new UTF8Encoding(false, true); + return strict.GetString(data); + } + catch (DecoderFallbackException) + { + try { return Encoding.Unicode.GetString(data); } + catch { return Encoding.UTF8.GetString(data); } + } + } + + // ===== 用户可见报表 ===== + + public async Task> GetVisibleReportsAsync(long userId, bool isAdmin, IReadOnlyCollection permissions) + { + var all = await _reportRepo.FindAsync(r => r.IsActive && !r.IsDeleted); + IEnumerable visible; + if (isAdmin) + { + visible = all; + } + else + { + visible = all.Where(r => permissions.Contains($"report:{r.Code}:visble")); + } + return visible + .OrderBy(r => r.SortOrder).ThenBy(r => r.Name) + .Select(r => new ReportViewDto(r.Id, r.Code, r.Name, r.Category, r.Description, r.SortOrder, HasTemplate(r))) + .ToList(); + + } + + public async Task EnsureReportAccessAsync(long userId, bool isAdmin, IReadOnlyCollection permissions, long reportId) + { + if (isAdmin) return; + var report = await _reportRepo.FirstOrDefaultAsync(r => r.Id == reportId && !r.IsDeleted) + ?? throw new F9NotFoundException("报表不存在", 404); + if (!report.IsActive) throw new F9NotFoundException("报表已停用", 404); + if (!permissions.Contains($"report:{report.Code}:visble")) + throw new F9OperationException("无权访问该报表"); + } + + // ===== 数据查询 ===== + + public async Task> ExecuteDataAsync(Report report, IReadOnlyDictionary parameters) + { + // 1. 优先按 Config.DsName 解析到数据提供者(F9 IReportService 模式) + var dsName = GetConfigDsName(report.Config); + if (!string.IsNullOrWhiteSpace(dsName)) + { + if (!_dataProviders.TryGetValue(dsName, out var provider)) + throw new F9OperationException($"未找到数据源查询实现:{dsName}"); + + var pairs = new Dictionary(StringComparer.Ordinal); + foreach (var kv in parameters) pairs[kv.Key] = kv.Value!; + + var dataSource = await provider.GetData(pairs, _mediator); + return ToGridReportJson(dataSource); + } + + // 2. 回退:DataSql + @参数名(仅绑定 SQL 中实际出现的参数,防注入) + if (string.IsNullOrWhiteSpace(report.DataSql)) + throw new F9OperationException("报表未配置数据源(Config.DsName 或 DataSql)"); + + try + { + var sqlTokens = Regex.Matches(report.DataSql, @"@([A-Za-z_][A-Za-z0-9_]*)") + .Select(m => m.Groups[1].Value).Distinct().ToHashSet(StringComparer.Ordinal); + + var dbParams = new List(); + foreach (var kv in parameters) + { + if (kv.Key.Length > 0 && sqlTokens.Contains(kv.Key) && kv.Value != null) + dbParams.Add(new SQLiteParameter("@" + kv.Key, kv.Value)); + } + + var dt = dbParams.Count > 0 + ? _fsql.Ado.ExecuteDataTable(report.DataSql, dbParams.ToArray()) + : _fsql.Ado.ExecuteDataTable(report.DataSql); + + var recordset = new List>(); + foreach (DataRow row in dt.Rows) + { + var d = new Dictionary(dt.Columns.Count, StringComparer.Ordinal); + foreach (DataColumn col in dt.Columns) + { + var v = row[col]; + d[col.ColumnName] = v == DBNull.Value ? null : v; + } + recordset.Add(d); + } + return new Dictionary { ["recordset"] = recordset }; + } + catch (F9OperationException) { throw; } + catch (Exception ex) + { + throw new F9OperationException("报表数据查询失败:" + ex.Message); + } + } + + /// + /// 将 F9 的 ReportDto_DataSource 转换为 Grid++Report 约定的 JSON 数据: + /// 单数据集 → { "recordset": [ {...} ] }(兼容无名记录集的模板) + /// 多数据集 → { "main": [ {...} ], "detail": [ {...} ] }(顶层键为数据集名) + /// 报表参数 → 附加 { "parameter": [ { 账期: "...", 车间: "..." } ] }(模板以 Parameter 方式引用) + /// + private static Dictionary ToGridReportJson(ReportDto_DataSource dataSource) + { + var result = new Dictionary(StringComparer.Ordinal); + + var datasets = dataSource.Report_Data + .Where(kv => (kv.Value.Field_Data?.Count ?? 0) > 0) + .ToList(); + + if (datasets.Count <= 1) + { + // 0 行时也必须输出空 recordset:否则渲染库找不到主记录集, + // 报表参数(Parameter)会被错位渲染到明细区(实测 0 行报表参数值错乱) + result["recordset"] = datasets.Count == 0 + ? new List() + : datasets[0].Value.Field_Data.Select(ToNameValueDict).Cast().ToList(); + } + else + { + foreach (var kv in datasets) + result[kv.Key] = kv.Value.Field_Data.Select(ToNameValueDict).Cast().ToList(); + } + + // 报表参数:以 Parameter 数据集输出(键名须与 .grf 模板的 Parameter 节点一致,HTML5 渲染器取数组第一项) + var paramData = dataSource.Report_Data.Values + .FirstOrDefault(d => d.Parameter_Data != null)?.Parameter_Data; + if (paramData != null) + result["Parameter"] = new List { ToNameValueDict(paramData) }; + + return result; + } + + /// + /// 对象 → 字段名/值的字典。 + /// 用字典承载是为了保证属性名原样输出(不受 ASP.NET Core 默认 camelCase 序列化策略影响), + /// 从而与 .grf 模板 DetailGrid.Recordset.Field 定义的字段名严格一致。 + /// + private static readonly ConcurrentDictionary _rowPropsCache = new(); + + private static Dictionary ToNameValueDict(object row) + { + if (row is IDictionary exists) return exists.ToDictionary(k => k.Key, v => (object?)v.Value); + + var props = _rowPropsCache.GetOrAdd(row.GetType(), t => t.GetProperties()); + var dict = new Dictionary(props.Length, StringComparer.Ordinal); + foreach (var p in props) + { + if (!p.CanRead) continue; + dict[p.Name] = p.GetValue(row); + } + return dict!; + } + + private static string? GetConfigDsName(string configJson) + { + if (string.IsNullOrWhiteSpace(configJson)) return null; + try + { + using var doc = JsonDocument.Parse(configJson); + if (doc.RootElement.TryGetProperty("DsName", out var n) && n.ValueKind == JsonValueKind.String) + return n.GetString(); + } + catch { /* 配置 JSON 非法时忽略 */ } + return null; + } + + public async Task> GetAllActiveAsync() + { + var list = await _reportRepo.FindAsync(r => r.IsActive && !r.IsDeleted); + return list + .OrderBy(r => r.SortOrder).ThenBy(r => r.Name) + .Select(r => new ReportListDto( + r.Id, r.Code, r.Name, r.Category, r.Description, r.SortOrder, r.IsActive, + r.ReportFileId, HasTemplate(r), r.CreatedAt, r.UpdatedAt)) + .ToList(); + } + + // ===== 数据源清单 / 结构探测(新增报表时按查询结果自动识别明细列) ===== + + public Task> GetDataSourcesAsync() + { + var list = _dataProviders.Values + .OrderBy(p => p.ReportName, StringComparer.OrdinalIgnoreCase) + .Select(p => new ReportDataSourceInfoDto { DsName = p.ReportName }) + .ToList(); + return Task.FromResult(list); + } + + public async Task ProbeAsync(ReportProbeRequestDto request) + { + var dsName = (request.DsName ?? "").Trim(); + var result = new ReportProbeResultDto { DsName = dsName }; + + _dataProviders.TryGetValue(dsName, out var provider); + + // 探测参数只用前端带入的取值(参数本身由用户在报表配置里维护,不在这里生成控件) + var pairs = new Dictionary(StringComparer.Ordinal); + foreach (var kv in request.Params) + if (kv.Value != null) pairs[kv.Key] = kv.Value; + + try + { + if (provider != null) + await FillFromProviderAsync(result, provider, pairs); + else if (!string.IsNullOrWhiteSpace(request.DataSql)) + FillFromSql(result, request.DataSql!, pairs); + else if (!string.IsNullOrWhiteSpace(dsName)) + result.Error = $"未找到数据源实现:{dsName}"; + else + result.Error = "请先选择数据源(DsName)或填写数据 SQL"; + } + catch (F9OperationException ex) { result.Error = ex.Message; } + catch (Exception ex) { result.Error = "数据源探测失败:" + ex.Message; } + + return result; + } + + /// 通过 IReportDataProvider 执行一次查询,取返回记录集的字段名作为明细列 + private async Task FillFromProviderAsync(ReportProbeResultDto result, IReportDataProvider provider, Dictionary pairs) + { + var dataSource = await provider.GetData(pairs, _mediator); + + // 报表参数:来自 Provider 返回的 Parameter_Data(不是查询条件,也不是硬编码声明) + var paramDatum = dataSource.Report_Data.FirstOrDefault(kv => kv.Value?.Parameter_Data != null); + if (paramDatum.Value?.Parameter_Data != null) + { + result.Parameters = ToNameValueDict(paramDatum.Value.Parameter_Data) + .Select(kv => new ReportParamValueDto + { + Name = kv.Key, + Value = kv.Value?.ToString() ?? "", + }) + .ToList(); + } + + var datums = dataSource.Report_Data + .Where(kv => kv.Value?.Field_Data is { Count: > 0 }) + .ToList(); + result.Total = datums.Sum(kv => kv.Value.Field_Data.Count); + + if (datums.Count == 0) + { + result.Error = "查询未返回数据,无法识别明细列(可手工添加列)"; + return; + } + + var rows = datums[0].Value.Field_Data.Take(3).Select(ToNameValueDict).ToList(); + result.Rows = rows; + result.Fields = rows[0].Select(kv => new ReportFieldDefDto + { + Name = kv.Key, + Label = kv.Key, + DataType = InferDataType(kv.Value), + }).ToList(); + } + + /// 直接用 DataSql 执行一次查询(带 @参数),取 DataTable 列作为明细列 + private void FillFromSql(ReportProbeResultDto result, string sql, Dictionary pairs) + { + ValidateSql(sql); + + var sqlTokens = Regex.Matches(sql, @"@([A-Za-z_][A-Za-z0-9_]*)") + .Select(m => m.Groups[1].Value).Distinct().ToHashSet(StringComparer.Ordinal); + var dbParams = new List(); + foreach (var kv in pairs) + if (sqlTokens.Contains(kv.Key) && kv.Value != null) + dbParams.Add(new SQLiteParameter("@" + kv.Key, kv.Value)); + + var dt = dbParams.Count > 0 + ? _fsql.Ado.ExecuteDataTable(sql, dbParams.ToArray()) + : _fsql.Ado.ExecuteDataTable(sql); + + result.Fields = dt.Columns.Cast().Select(c => new ReportFieldDefDto + { + Name = c.ColumnName, + Label = c.ColumnName, + DataType = c.DataType.Name, + }).ToList(); + + result.Total = dt.Rows.Count; + result.Rows = dt.Rows.Cast().Take(3).Select(row => + { + var d = new Dictionary(dt.Columns.Count, StringComparer.Ordinal); + foreach (DataColumn col in dt.Columns) + { + var v = row[col]; + d[col.ColumnName] = v == DBNull.Value ? null : v; + } + return d; + }).ToList(); + } + + private static string InferDataType(object? value) + { + if (value == null) return "String"; + if (value is JsonElement je) + { + return je.ValueKind switch + { + JsonValueKind.Number => "Number", + JsonValueKind.True or JsonValueKind.False => "Boolean", + _ => "String", + }; + } + return value switch + { + int or long or short or byte or uint or ulong or ushort or sbyte => "Integer", + decimal or double or float => "Number", + DateTime or DateTimeOffset => "DateTime", + bool => "Boolean", + _ => "String", + }; + } + + // ===== 工具 ===== + + private static bool HasTemplate(Report r) => r.ReportFileId != 0; + + private static ReportDetailDto ToDetail(Report r) => new( + r.Id, r.Code, r.Name, r.Category, r.Description, r.DataSql, r.DataParams, r.Config, + r.SortOrder, r.IsActive, r.ReportFileId, HasTemplate(r), + r.CreatedAt, r.UpdatedAt); + + private static void ValidateCode(string code) + { + if (!Regex.IsMatch(code ?? "", "^[a-zA-Z0-9_]{2,40}$")) + throw new F9OperationException("报表编码仅允许字母/数字/下划线,长度 2-40"); + } + + private static void ValidateSql(string sql) + { + if (string.IsNullOrWhiteSpace(sql)) throw new F9OperationException("数据集 SQL 不能为空"); + var trimmed = sql.Trim(); + if (!trimmed.StartsWith("select", StringComparison.OrdinalIgnoreCase)) + throw new F9OperationException("数据集 SQL 必须以 SELECT 开头(仅允许查询语句)"); + // 禁止分号后跟其他语句 + if (trimmed.IndexOf(';') >= 0 && trimmed.IndexOf(';') < trimmed.Length - 1) + throw new F9OperationException("数据集 SQL 不允许包含多条语句"); + } + + private static void ValidateConfig(string? configJson) + { + if (string.IsNullOrWhiteSpace(configJson)) return; + try + { + using var doc = JsonDocument.Parse(configJson); + if (doc.RootElement.ValueKind != JsonValueKind.Object) + throw new F9OperationException("查询配置(Config)必须是 JSON 对象"); + } + catch (JsonException) + { + throw new F9OperationException("查询配置(Config)不是合法 JSON"); + } + catch (F9OperationException) { throw; } + } +} diff --git a/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsDocVersion.cs b/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsDocVersion.cs new file mode 100644 index 0000000..5637a32 --- /dev/null +++ b/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsDocVersion.cs @@ -0,0 +1,25 @@ +using FreeSql.DataAnnotations; +using F9WebAiTest.Domain.Common; + +namespace F9WebAiTest.Domain.Entities.QC.Gyzxs; + +/// +/// 工艺执行书主控(对应 F9 biz_prod_qc_gyzxs_version): +/// 以「工艺庄口」为主键的一条汇总指针记录,记录该庄口下每张子单据是否已录入。 +/// 列表上的 √ 标记全部来自本表各 *Id > 0。 +/// +[Table(Name = "qc_gyzxs_doc_version")] +public class GyzxsDocVersion : BaseEntity +{ + public long GyzkId { get; set; } // 工艺庄口 Id + public long ZongheyangId { get; set; } // 综合样茧质 Id(本系统暂未建综合样茧质,保留指针) + public long ShichanId { get; set; } // 试产(试缫工艺单)Id + public long FengMianId { get; set; } // 封面 Id + public long XbjSsdyId { get; set; } // 试缫与大样 Id + public long XbjId { get; set; } // 混剥选茧 Id + public long ZhuJianId { get; set; } // 煮茧 Id + public long ZdsId { get; set; } // 自动缫 Id + public long ZdsDyId { get; set; } // 自动缫大样 Id + public long HsId { get; set; } // 后缫(工艺执行书)Id + public string Memo { get; set; } = ""; // 备注 +} diff --git a/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsFengMian.cs b/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsFengMian.cs new file mode 100644 index 0000000..a87d907 --- /dev/null +++ b/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsFengMian.cs @@ -0,0 +1,23 @@ +using FreeSql.DataAnnotations; +using F9WebAiTest.Domain.Common; + +namespace F9WebAiTest.Domain.Entities.QC.Gyzxs; + +/// +/// 封面(对应 F9 biz_prod_qc_gyzxs_fengmian): +/// 报表「封面」= 桑蚕丝工艺设计书,含编号、庄口、规格、原料总量、生产丝量、缫制车间、生产时间。 +/// +[Table(Name = "qc_gyzxs_fengmian")] +public class GyzxsFengMian : BaseEntity +{ + public long GyzkId { get; set; } // 工艺庄口 Id + public string BianHao { get; set; } = ""; // 编号(如 SJZ-2025-武工艺-58) + public string Guige { get; set; } = ""; // 缫制规格 + public decimal YuanLiaoZhongLiang { get; set; } // 原料总量(吨) + public decimal ShengSiZhongLiang { get; set; } // 生产丝量(吨) + public string ShengChanCheJian { get; set; } = "";// 缫制车间 + public DateTime? ShengChanStartDate { get; set; } // 开始生产日期 + public DateTime? ShengChanEndDate { get; set; } // 结束生产日期 + public string FaSongBuMen { get; set; } = ""; // 发送部门 + public string Memo { get; set; } = ""; // 备注 +} diff --git a/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsHs.cs b/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsHs.cs new file mode 100644 index 0000000..9eddf30 --- /dev/null +++ b/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsHs.cs @@ -0,0 +1,57 @@ +using FreeSql.DataAnnotations; +using F9WebAiTest.Domain.Common; + +namespace F9WebAiTest.Domain.Entities.QC.Gyzxs; + +/// +/// 后缫(编丝整理)工艺执行书(对应 F9 biz_prod_qc_gyzxs_hs)。 +/// 报表「工艺执行书」即本单据:生产指标 9 项 + 工艺条件及控制指标 18 项 + 主要技术措施 + 设计/审核/审批。 +/// 指标按 F9 约定全部为字符串,可存 "3100"、"99.8"、"20~38"、"185±5" 这类文本。 +/// +[Table(Name = "qc_gyzxs_hs")] +public class GyzxsHs : BaseEntity +{ + public long GyzkId { get; set; } // 工艺庄口 + public string Guige { get; set; } = ""; // 生丝规格 + public DateTime? ZhiXingDate { get; set; } // 执行时间 + public string QianSaoCheJian { get; set; } = ""; // 前缫生产车间 + public int YuSaoTianShu { get; set; } // 预缫天数 + public string ZhengLiGuiGe { get; set; } = "GB1798-2008 白厂丝"; // 整理规格 + + // ---- 生产指标 ---- + public string SczbDanChan { get; set; } = ""; // 单产(片/人、班) + public string SczbRiChan { get; set; } = ""; // 日产(片/班) + public string SczbBianSiLiang { get; set; } = ""; // 编丝量(片/人、班) + public string SczbZhengJianLiang { get; set; } = ""; // 整检量(片/日) + public string SczbShengChanZhengPinLv { get; set; } = ""; // 生产正品率(%) + public string SczbShangJianZhengPinLv { get; set; } = ""; // 商检正品率(%) + public string SczbQieDuan { get; set; } = ""; // 切断(次/批) + public string SczbXiaoLv { get; set; } = ""; // 效率(%) + public string SczbPinZhiCiDian { get; set; } = ""; // 编检品质疵点(%) + + // ---- 工艺条件及控制指标 ---- + public string GyzbCheSu { get; set; } = ""; // 车速(转/分) + public string GyzbXiaoYuDaiFanShiJian { get; set; } = ""; // 小䈅待返时间(分) + public string GyzbCheJianWenDu { get; set; } = ""; // 车间温度(℃) + public string GyzbCheJianXiangDuiShiDu { get; set; } = ""; // 车间相对湿度(%) + public string GyzbCheXiangWenDu { get; set; } = ""; // 车厢温度(℃) + public string GyzbCheXiangShiDu { get; set; } = ""; // 车厢湿度(%) + public string GyzbPingHengShiWenDu { get; set; } = ""; // 平衡室温度(℃) + public string GyzbPingHengShiXiangDuiShiDu { get; set; } = ""; // 平衡室相对湿度(%) + public string GyzbXiaoYuGeiShiWenDu { get; set; } = ""; // 小䈅给湿温度(℃) + public string GyzbXiaoYuSiPianPingHengShiJian { get; set; } = ""; // 小䈅丝片平衡时间(分) + public string GyzbZhenKongDu { get; set; } = ""; // 真空度(mp) + public string GyzbChouQiCiShu { get; set; } = ""; // 抽气次数(次) + public string GyzbDaYuSiPianPingHengShiJian { get; set; } = ""; // 大䈅丝片平衡时间(分) + public string GyzbDaYuSiPianHuiChaoLv { get; set; } = ""; // 大䈅丝片回潮率(%) + public string GyzbShengSiHuiChaoLv { get; set; } = ""; // 生丝回潮率(%) + public string GyzbDaYuSiPianKuanDu { get; set; } = ""; // 大䈅丝片宽度(cm) + public string GyzbXieSiDuiSiLv { get; set; } = ""; // 屑丝对丝率(%) + public string GyzbDaYuSiPianCiDian { get; set; } = ""; // 大䈅丝片疵点(%) + + public string JiShuCuoShi { get; set; } = ""; // 主要技术措施 + public string SheJiRen { get; set; } = ""; // 设计 + public string ShenHeRen { get; set; } = ""; // 审核 + public string PiFuRen { get; set; } = ""; // 审批 + public string Memo { get; set; } = ""; // 备注 +} diff --git a/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsShichan.cs b/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsShichan.cs new file mode 100644 index 0000000..dea299e --- /dev/null +++ b/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsShichan.cs @@ -0,0 +1,65 @@ +using FreeSql.DataAnnotations; +using F9WebAiTest.Domain.Common; + +namespace F9WebAiTest.Domain.Entities.QC.Gyzxs; + +/// +/// 试产(试缫工艺单,对应 F9 biz_prod_qc_gyzxs_shichan): +/// 两个分组——「自动缫试缫工艺」与「试缫煮茧工艺」。 +/// 工艺指标大量是区间文本(如 190±5、9、10、11、≥99),因此车速/温度类用 string。 +/// +[Table(Name = "qc_gyzxs_shichan")] +public class GyzxsShichan : BaseEntity +{ + public long GyzkId { get; set; } // 工艺庄口 + public string Guige { get; set; } = ""; // 规格 + public string HuanZhuangFangShi { get; set; } = ""; // 换庄方式 + public DateTime? ScDate { get; set; } // 试缫日期 + public string ScCheJian { get; set; } = ""; // 生产车间 + + // ---- 茧质调查(来源:综合样茧质 / 手工填写)---- + public decimal YiJianSiChang { get; set; } // 一茧丝长(m) + public decimal JieShuLv { get; set; } // 解舒率(%) + public decimal JieShuSiChang { get; set; } // 解舒丝长(m) + public decimal JianCengLv { get; set; } // 茧层率(%) + public decimal JianSiXianDu { get; set; } // 茧丝纤度(D) + public decimal JieShuGuangZhe { get; set; } // 解舒光折(kg) + public decimal WanDiao { get; set; } // 万吊(次) + + // ---- 自动缫试缫工艺指标 ---- + public string ZdsCheSu { get; set; } = ""; // 车速(转) + public string ZdsYunXuLiShu { get; set; } = ""; // 允许粒数(粒) + public decimal ZdsSaoZhe { get; set; } // 缫折(kg) + public decimal ZdsZhongXinXianDu { get; set; } // 中心纤度(D) + public string ZdsZhongXinLiFuLv { get; set; } = ""; // 中心粒符率(%) + public string ZdsSaoSiWenDu { get; set; } = ""; // 缫丝温度(℃) + public decimal ZdsJiSuanXianDu { get; set; } // 计算纤度(D) + public string ZdsYunXuLiFuLv { get; set; } = ""; // 允许粒符率(%) + public string ZdsSuoXuWenDu { get; set; } = ""; // 索绪温度(℃) + public decimal ZdsPingJunLiShu { get; set; } // 平均粒数(粒) + public decimal ZdsBaiJianYinXu { get; set; } // 白茧引绪(粒) + public string ZdsLiXuWenDu { get; set; } = ""; // 理绪温度(℃) + public decimal ZdsZhongXinLiShu { get; set; } // 中心粒数(粒) + public string ZdsGeiJianKouKuanDu { get; set; } = ""; // 给茧口宽度(mm) + public decimal ZdsLuoSiLongShu { get; set; } // 落丝笼数(笼) + public decimal ZdsLongJianLiShu { get; set; } // 笼茧粒数(粒) + + // ---- 试缫煮茧工艺 ---- + public string ZjCheSu { get; set; } = ""; // 煮茧车速(转) + public string ZjZhengZhuShi1 { get; set; } = ""; // 蒸煮室温度1 + public string ZjZhengZhuShi2 { get; set; } = ""; // 蒸煮室温度2 + public string ZjZhengZhuShi3 { get; set; } = ""; // 蒸煮室温度3 + public decimal ZjLuoXuWai { get; set; } // 外层掉绪 + public decimal ZjLuoXuZhong { get; set; } // 中层掉绪 + public decimal ZjLuoXuNei { get; set; } // 内层掉绪 + public string ZjTiaoZhengBuWenDu { get; set; } = ""; // 调整部温度 + public string ZjChuKouWenDu { get; set; } = ""; // 出口温度 + public string ZjFuZhuCheJian { get; set; } = ""; // 附煮车间 + public string ZjFuZhuGyzkName { get; set; } = ""; // 附煮庄口 + + // ---- 签署 ---- + public string SheJiRen { get; set; } = ""; // 设计人 + public string ShenHeRen { get; set; } = ""; // 审核 + public string PiFuRen { get; set; } = ""; // 审批 + public string Memo { get; set; } = ""; // 备注 +} diff --git a/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsXbj.cs b/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsXbj.cs new file mode 100644 index 0000000..44c248d --- /dev/null +++ b/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsXbj.cs @@ -0,0 +1,61 @@ +using FreeSql.DataAnnotations; +using F9WebAiTest.Domain.Common; + +namespace F9WebAiTest.Domain.Entities.QC.Gyzxs; + +/// +/// 混剥选茧工艺执行书(对应 F9 biz_prod_qc_gyzxs_xbj)。 +/// 含生产指标 / 质量指标 / 质量控制指标 三组,指标多为百分数或区间文本。 +/// +[Table(Name = "qc_gyzxs_xbj")] +public class GyzxsXbj : BaseEntity +{ + public long GyzkId { get; set; } // 工艺庄口 + public string Guige { get; set; } = ""; // 缫制规格 + public string CheJian { get; set; } = ""; // 缫制车间 + public DateTime? HuanZhuangDate { get; set; } // 换庄时间 + + // ---- 生产指标 ---- + public decimal ScRiChanLiang { get; set; } // 日产量(包/人) + public decimal ScBanChanLiang { get; set; } // 班产量(包) + public string ScShangCheLv { get; set; } = ""; // 上车率 + public string ScLianTongLv { get; set; } = ""; // 联统茧率 + public string ScWuJiLv { get; set; } = ""; // 五级茧率 + public string ScXiaJiaoLv { get; set; } = ""; // 下茧/茧衣率 + public string ScJianYiLv { get; set; } = ""; // 茧衣率 + public string ScJiShuCuoShi { get; set; } = ""; // 技术措施要求 + + // ---- 质量指标(统号茧 / 五级茧 / 下脚茧 交叉率)---- + public string QcTongHaoWuJiLv { get; set; } = ""; // 统号茧-五级茧率 + public string QcTongHaoXiaJianLv { get; set; } = ""; // 统号茧-下茧率 + public string QcWuJiLianTongLv { get; set; } = ""; // 五级茧-联统茧率 + public string QcWuJiXiaJiaoLv { get; set; } = ""; // 五级茧-下脚率 + public string QcXiaJiaoLianTongLv { get; set; } = ""; // 下脚茧-联统率 + public string QcXiaJiaoCiJianLv { get; set; } = ""; // 下脚茧-次茧率 + + // ---- 质量控制指标(其它级茧日产量 + 共性指标)---- + public string QcZbHaoWuZuChanLiang { get; set; } = ""; // 好五茧(kg/组/3人) + public string QcZbLiuJiZuChanLiang { get; set; } = ""; // 六级茧(kg/组/3人) + public string QcZbMianJianZuChanLiang { get; set; } = ""; // 棉茧(kg/组/3人) + public string QcZbXiaJiaoZuChanLiang { get; set; } = ""; // 下脚茧(kg/组/3人) + public string QcZbShuangGongZuChanLiang { get; set; } = ""; // 双宫茧(kg/组/3人) + public string QcZbGongJinLiShu { get; set; } = ""; // 公斤粒数(粒) + public string QcZbBaoGuangLv { get; set; } = ""; // 剥光率(%) + public string QcZbJianYiLv { get; set; } = ""; // 茧衣率(%) + + public string Memo { get; set; } = ""; // 备注 +} + +/// 混茧比例明细(对应 F9 biz_prod_qc_gyzxs_xbj_hunjian) +[Table(Name = "qc_gyzxs_xbj_hunjian")] +public class GyzxsXbjHunJian : BaseEntity +{ + public long XbjId { get; set; } // 选剥茧主表 Id + public long YlzkId { get; set; } // 原料庄口 Id + public string ZhuangBie { get; set; } = ""; // 庄别(冗余名称,便于打印) + public decimal ZhongLiang { get; set; } // 重量(kg) + public int BaoShu { get; set; } // 总包数 + public decimal BiLi { get; set; } // 比例(%) + public int HunJianBaoShu { get; set; } // 混茧包数 + public int Sort { get; set; } // 排序 +} diff --git a/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsXbjSsDy.cs b/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsXbjSsDy.cs new file mode 100644 index 0000000..c92680e --- /dev/null +++ b/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsXbjSsDy.cs @@ -0,0 +1,45 @@ +using FreeSql.DataAnnotations; +using F9WebAiTest.Domain.Common; + +namespace F9WebAiTest.Domain.Entities.QC.Gyzxs; + +/// +/// 试缫与大样资料(对应 F9 biz_prod_qc_gyzxs_xbj_ssdy): +/// 「试缫」与「大样」两组同构指标,报表为两行对比表。 +/// +[Table(Name = "qc_gyzxs_xbj_ssdy")] +public class GyzxsXbjSsDy : BaseEntity +{ + public long GyzkId { get; set; } // 工艺庄口 + public string Guige { get; set; } = ""; // 缫制规格 + + // ---- 试缫 ---- + public string SsShangCheLv { get; set; } = ""; // 上车率(%) + public string SsLianTongLv { get; set; } = ""; // 联统率(%) + public string SsCiJianLv { get; set; } = ""; // 次茧率(%) + public string SsCiLiuJiLv { get; set; } = ""; // 次六级(%) + public string SsMianJianLv { get; set; } = ""; // 棉茧率(%) + public string SsXiaJiaoLv { get; set; } = ""; // 下脚率(%) + public string SsShuangGongLv { get; set; } = ""; // 双宫率(%) + public string SsJianYiLv { get; set; } = ""; // 茧衣率(%) + public decimal SsLiJianZhong { get; set; } // 400粒重(克) + public decimal SsGongJinLiShu { get; set; } // 公斤粒数 + + // ---- 大样 ---- + public string DyShangCheLv { get; set; } = ""; // 上车率(%) + public string DyLianTongLv { get; set; } = ""; // 联统率(%) + public string DyCiJianLv { get; set; } = ""; // 次茧率(%) + public string DyCiLiuJiLv { get; set; } = ""; // 次六级(%) + public string DyMianJianLv { get; set; } = ""; // 棉茧率(%) + public string DyXiaJiaoLv { get; set; } = ""; // 下脚率(%) + public string DyShuangGongLv { get; set; } = ""; // 双宫率(%) + public string DyJianYiLv { get; set; } = ""; // 茧衣率(%) + public decimal DyLiJianZhong { get; set; } // 400粒重(克) + public decimal DyGongJinLiShu { get; set; } // 公斤粒数 + + public string JiShuCuoShi { get; set; } = ""; // 主要技术措施及要求 + public string SheJiRen { get; set; } = ""; // 设计 + public string ShenHeRen { get; set; } = ""; // 审核 + public string PiFuRen { get; set; } = ""; // 审批 + public string Memo { get; set; } = ""; // 备注 +} diff --git a/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsZds.cs b/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsZds.cs new file mode 100644 index 0000000..8865b00 --- /dev/null +++ b/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsZds.cs @@ -0,0 +1,112 @@ +using FreeSql.DataAnnotations; +using F9WebAiTest.Domain.Common; + +namespace F9WebAiTest.Domain.Entities.QC.Gyzxs; + +/// +/// 自动缫车间生产工艺执行书(表一,对应 F9 biz_prod_qc_gyzxs_zds)。 +/// 含「茧质概况」(冗余自综合样茧质)、「生产指标」、「工艺技术指标」三块。 +/// +[Table(Name = "qc_gyzxs_zds")] +public class GyzxsZds : BaseEntity +{ + public long GyzkId { get; set; } // 工艺庄口 + public string Guige { get; set; } = ""; // 生丝规格 + public DateTime? HuanZhuangDate { get; set; } // 接缫日期 + public string HuanZhuangFangShi { get; set; } = ""; // 换庄方式 + public string CheJian { get; set; } = ""; // 生产车间 + public int YuSaoTianShu { get; set; } // 预缫天数(天) + public decimal YuSaoSiLiang { get; set; } // 预缫丝量(吨) + + // ---- 茧质概况 ---- + public decimal ZgYiJianSiChang { get; set; } // 一茧丝长(米) + public decimal ZgJieShuSiChang { get; set; } // 解舒丝长 + public decimal ZgJieShuLv { get; set; } // 解舒率(%) + public decimal ZgJianSiXianDu { get; set; } // 茧丝纤度(D) + public decimal ZgJieShuGuangZhe { get; set; } // 解舒光折(kg) + public decimal ZgWanMiDiaoCao { get; set; } // 万米吊糙(次) + public decimal ZgQingJie { get; set; } // 清洁(分) + public decimal ZgJingDu { get; set; } // 洁净(分) + public decimal ZgJianCengLv { get; set; } // 茧层率(%) + public decimal ZgXianDuJunFangCha { get; set; } // 茧丝纤度均方差 + public decimal ZgWaiSanBaiHui { get; set; } // 外三百回纤度 + public decimal ZgZhongSanBaiHui { get; set; } // 中三百回纤度 + public decimal ZgNeiSanBaiHui { get; set; } // 内三百回纤度 + public decimal ZgZuiDaKaiCha { get; set; } // 最大开差 + public decimal ZgNeiWaiKaiCha { get; set; } // 内外开差 + public decimal ZgBaiSeJianDanXian { get; set; } // 白色茧单纤 + public decimal ZgHuiSeJianDanXian { get; set; } // 灰色茧单纤 + public decimal ZgHongSeJianDanXian { get; set; } // 红色茧单纤 + public decimal ZgLuoXuWai { get; set; } // 落绪分布-外 + public decimal ZgLuoXuZhong { get; set; } // 落绪分布-中 + public decimal ZgLuoXuNei { get; set; } // 落绪分布-内 + public decimal ZgPingJunJianFu { get; set; } // 平均茧幅(mm) + public decimal ZgJianFuZhengQiLv { get; set; } // 茧幅整齐率(%) + public string ZgJianSiBaiHuiXianDu { get; set; } = ""; // 茧丝百回纤度(明细串) + + // ---- 生产指标 ---- + public string SczbPinWei { get; set; } = ""; // 品味 + public string SczbSaoZhe { get; set; } = ""; // 缫折(kg) + public string SczbTaiChan { get; set; } = ""; // 台产 + public string SczbPianCha { get; set; } = ""; // 偏差 + public string SczbZongCha { get; set; } = ""; // 总差 + public string SczbQingJie { get; set; } = ""; // 清洁 + public string SczbJingJie { get; set; } = ""; // 洁净 + public string SczbYunEr { get; set; } = ""; // 匀Ⅱ + public string SczbZhengPinLv { get; set; } = ""; // 生产正品率 + + // ---- 工艺技术指标(第一行)---- + public string GyzbCheSu { get; set; } = ""; // 车速(转/分) + public string GyzbTianXuCiShu { get; set; } = ""; // 添绪次数(次/分) + public string GyzbYunZhuanLv { get; set; } = ""; // 运转率(%) + public string GyzbZhongXinXianDu { get; set; } = ""; // 中心纤度(D) + public string GyzbSheJiDanXianDu { get; set; } = ""; // 设计单纤度(D) + public string GyzbPingJunLiShu { get; set; } = ""; // 平均粒数(粒) + public string GyzbZhongXinLiShu { get; set; } = ""; // 中心粒数(粒) + public string GyzbYunXuLiShu { get; set; } = ""; // 允许粒数(粒) + public string GyzbYinXu { get; set; } = ""; // 引绪(粒) + public string GyzbZhongXinLiFuLv { get; set; } = ""; // 中心粒符率 + public string GyzbYunXuLiFuLv { get; set; } = ""; // 允许粒符率 + public string GyzbGeiJianKouKuanDu { get; set; } = ""; // 给茧口宽度 + public string GyzbTiaoTuLv { get; set; } = ""; // 条吐率(%) + public string GyzbSiQiaoChangDu { get; set; } = ""; // 丝鞘长度(cm) + + // ---- 工艺技术指标(第二行)---- + public string GyzbCheTouQiYa { get; set; } = ""; // 车头汽压(MP) + public string GyzbSuoXuWenDu { get; set; } = ""; // 索绪温度(℃) + public string GyzbLiXuWenDu { get; set; } = ""; // 理绪温度(℃) + public string GyzbPianXinPanJianLiang { get; set; } = ""; // 偏心盘茧量 + public string GyzbSaoTangWenDu { get; set; } = ""; // 缫汤温度(℃) + public string GyzbJiaJianKouJianLiang { get; set; } = ""; // 加茧口茧量(粒) + public string GyzbWuXuLv { get; set; } = ""; // 无绪率(%) + public string GyzbPengCaoJianLv { get; set; } = ""; // 蓬糙茧率(%) + public string GyzbJiaJianJunYunLv { get; set; } = ""; // 加茧均匀率(%) + public string GyzbZhengQueTianXuLv { get; set; } = ""; // 正确添绪率(%) + public string GyzbTongJianLiang { get; set; } = ""; // 桶茧量(g) + public string GyzbLuoSiLongShu { get; set; } = ""; // 落丝笼数 + + public string SheJiRen { get; set; } = ""; // 设计 + public string ShenHeRen { get; set; } = ""; // 审核 + public string PiFuRen { get; set; } = ""; // 审批 + public string Memo { get; set; } = ""; // 备注 +} + +/// 自动缫大样资料(表二,对应 F9 biz_prod_qc_gyzxs_zds_dyzl) +[Table(Name = "qc_gyzxs_zds_dyzl")] +public class GyzxsZdsDyzl : BaseEntity +{ + public long GyzkId { get; set; } // 工艺庄口 + public long ZdsId { get; set; } // 自动缫主表 Id + public string Guige { get; set; } = ""; // 生丝规格 + public string PianCha { get; set; } = ""; // 偏差 + public string QingJie { get; set; } = ""; // 清洁 + public string JingDu { get; set; } = ""; // 净度 + public string JieShuLv { get; set; } = ""; // 解舒率 + public string WanMiDiaoCao { get; set; } = ""; // 万米吊糙 + public string SaoZhe { get; set; } = ""; // 缫折 + public string JiShuCuoShi { get; set; } = ""; // 技术措施及要求 + public string SheJiRen { get; set; } = ""; // 设计 + public string ShenHeRen { get; set; } = ""; // 审核 + public string PiFuRen { get; set; } = ""; // 审批 + public string Memo { get; set; } = ""; // 备注 +} diff --git a/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsZhuJian.cs b/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsZhuJian.cs new file mode 100644 index 0000000..99aa1e7 --- /dev/null +++ b/backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/GyzxsZhuJian.cs @@ -0,0 +1,98 @@ +using FreeSql.DataAnnotations; +using F9WebAiTest.Domain.Common; + +namespace F9WebAiTest.Domain.Entities.QC.Gyzxs; + +/// +/// 煮茧工艺执行书主表(对应 F9 biz_prod_qc_gyzxs_zhujian)。 +/// 「庄口原料性能」区在本系统内冗余存储在表上,避免强依赖综合样茧质。 +/// +[Table(Name = "qc_gyzxs_zhujian")] +public class GyzxsZhuJian : BaseEntity +{ + public long GyzkId { get; set; } // 工艺庄口 + public string Guige { get; set; } = ""; // 缫制规格 + public DateTime? HuanZhuangDate { get; set; } // 换庄时间 + public string HuanZhuangFangShi { get; set; } = ""; // 接缫方式 + public string CheJian { get; set; } = ""; // 生产车间 + public string ZhuJianJi { get; set; } = ""; // 煮茧机号 + + // ---- 庄口原料性能 ---- + public decimal YlGongJinLiShu { get; set; } // 公斤粒数(粒) + public string YlSuoZhou { get; set; } = ""; // 缩绉 + public decimal YlJianCengLv { get; set; } // 茧层率 + public decimal YlQingJie { get; set; } // 清洁 + public decimal YlJingDu { get; set; } // 净度 + public decimal YlLiJianSiChang { get; set; } // 粒茧丝长 + public decimal YlJieShuLv { get; set; } // 解舒率 + public decimal YlJieShuSiChang { get; set; } // 解舒丝长 + public decimal YlJianSiXianDu { get; set; } // 茧丝纤度 + public decimal YlWanMiDiaoCao { get; set; } // 万米吊糙 + public decimal YlJieShuGuangZhe { get; set; } // 解舒光折 + + // ---- 工艺技术指标 ---- + public string GyzbJieShuLv { get; set; } = ""; // 实缫解舒 + public string GyzbWanDiao { get; set; } = ""; // 实缫万吊 + public string GyzbQingJie { get; set; } = ""; // 清洁 + public string GyzbJingDu { get; set; } = ""; // 净度 + public string GyzbSiJiaoRongShiLv { get; set; } = ""; // 丝胶溶失率 + public string GyzbZuLuoSiLongShu { get; set; } = ""; // 组落丝笼数 + public string GyzbSaoZhe { get; set; } = ""; // 缫折(kg) + public string GyzbLongJianLiShu { get; set; } = ""; // 笼茧粒数 + public string GyzbMeiTongJianLiang { get; set; } = ""; // 每桶茧量(g) + public string GyzbMeiBaoTongShu { get; set; } = ""; // 每包桶数(笼) + public string GyzbMeiXiaoShiYongJianLong { get; set; } = ""; // 每小时用茧笼数 + public string GyzbTiaoTuLv { get; set; } = ""; // 条吐率(%) + + // ---- 真空渗透 ---- + public string ZkstZhenKongDu { get; set; } = ""; // 真空度 + public string ZkstXiShuiWenDu { get; set; } = ""; // 吸水温度 + public string ZkstDaZhenKongShiChang { get; set; } = ""; // 没进水时达到最高真空度时长 + public string ZkstZuiGaoWeiShiChang { get; set; } = ""; // 液面到最高位时长 + public string ZkstGuanShuiZhenKongShiChang { get; set; } = ""; // 关水后继续真空时长 + public string ZkstChuJianWenDu { get; set; } = ""; // 出茧温度 + public string ZkstQuanJiHuiZhuanShiJian { get; set; } = ""; // 全机回转时间(分) + + public string JiShuCuoShi { get; set; } = ""; // 主要技术措施 + public string SheJiRen { get; set; } = ""; // 设计 + public string ShenHeRen { get; set; } = ""; // 审核 + public string PiFuRen { get; set; } = ""; // 审批 + public string Memo { get; set; } = ""; // 备注 +} + +/// 分车间煮茧指标(对应 F9 biz_prod_qc_gyzxs_zhujian_chejian_zhujianduan) +[Table(Name = "qc_gyzxs_zhujian_chejian")] +public class GyzxsZhuJianCheJian : BaseEntity +{ + public long ZhuJianId { get; set; } // 煮茧主表 Id + public string CheJian { get; set; } = ""; // 车间 + public string DiWenShenTou { get; set; } = ""; // 低温渗透 + public string ZhengZhuShi1 { get; set; } = ""; // 蒸煮室1 + public string ZhengZhuShi2 { get; set; } = ""; // 蒸煮室2 + public string ZhengZhuShi3 { get; set; } = ""; // 蒸煮室3 + public string TiaoZhengBu1 { get; set; } = ""; // 调整部1 + public string TiaoZhengBu2 { get; set; } = ""; // 调整部2 + public string TiaoZhengBu3 { get; set; } = ""; // 调整部3 + public string TiaoZhengBu4 { get; set; } = ""; // 调整部4 + public string TiaoZhengBu5 { get; set; } = ""; // 调整部5 + public string ChuJianWenDu { get; set; } = ""; // 出茧温度 + public string HuiZhuanShiJian { get; set; } = ""; // 全机回转时间 + public int Sort { get; set; } +} + +/// 副煮车间结果(对应 F9 biz_prod_qc_gyzxs_zhujian_chejian_fuzhu) +[Table(Name = "qc_gyzxs_zhujian_fuzhu")] +public class GyzxsZhuJianFuZhu : BaseEntity +{ + public long ZhuJianId { get; set; } // 煮茧主表 Id + public string CheJian { get; set; } = ""; // 附煮车间 + public string JieShuLv { get; set; } = ""; // 解舒 + public string WanDiao { get; set; } = ""; // 万吊 + public string SaoZhe { get; set; } = ""; // 缫折(kg) + public string WenDu1 { get; set; } = ""; // 温度1 + public string WenDu2 { get; set; } = ""; // 温度2 + public string WenDu3 { get; set; } = ""; // 温度3 + public string WenDu4 { get; set; } = ""; // 温度4 + public string WenDu5 { get; set; } = ""; // 温度5 + public int Sort { get; set; } +} diff --git a/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcJfdc.cs b/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcJfdc.cs new file mode 100644 index 0000000..831389a --- /dev/null +++ b/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcJfdc.cs @@ -0,0 +1,28 @@ +using FreeSql.DataAnnotations; +using F9WebAiTest.Domain.Common; + +namespace F9WebAiTest.Domain.Entities.QC.Yjss; + +/// 茧幅调查主表(对应 F9 biz_prod_qc_yjss_dc_jfdc_main) +[Table(Name = "qc_yjss_dc_jfdc_main")] +public class YjssDcJfdcMain : BaseEntity +{ + public long YjssId { get; set; } // 主表 Id + public int YjssType { get; set; } = 1; // 样品类型:1=原料小样 2=综合样 + public decimal PingJunJianFu { get; set; } // 平均茧幅(mm) + public decimal ZuiDaJianFu { get; set; } // 最大茧幅(mm) + public decimal ZhengQiLv { get; set; } // 茧幅整齐率(%) + public decimal JunFangCha { get; set; } // 茧幅均方差 +} + +/// 茧幅调查明细(对应 F9 biz_prod_qc_yjss_dc_jfdc_item) +[Table(Name = "qc_yjss_dc_jfdc_item")] +public class YjssDcJfdcItem : BaseEntity +{ + public long JfdcId { get; set; } // 茧幅调查主表 Id + public long YjssId { get; set; } // 主表 Id(冗余,便于整表删除) + public int YjssType { get; set; } = 1; // 样品类型:1=原料小样 2=综合样 + public decimal JianFu { get; set; } // 茧幅(mm) + public decimal ShuLiang { get; set; } // 数量(粒) + public int Sort { get; set; } +} diff --git a/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcJsbhxd.cs b/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcJsbhxd.cs new file mode 100644 index 0000000..94849f2 --- /dev/null +++ b/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcJsbhxd.cs @@ -0,0 +1,33 @@ +using FreeSql.DataAnnotations; +using F9WebAiTest.Domain.Common; + +namespace F9WebAiTest.Domain.Entities.QC.Yjss; + +/// 茧丝百回纤度汇总(对应 F9 biz_prod_qc_yjss_dc_jsbhxdmain) +[Table(Name = "qc_yjss_dc_jsbhxd_main")] +public class YjssDcJsbhxdMain : BaseEntity +{ + public long YjssId { get; set; } // 主表 Id + public int YjssType { get; set; } = 1; // 样品类型:1=原料小样 2=综合样 + public decimal XianDuJunFangCha { get; set; } // 茧丝纤度均方差 + public decimal ZuiDaKaiCha { get; set; } // 最大开差 + public decimal NeiWaiKaiCha { get; set; } // 内外开差 + public decimal WaiSanBaiHui { get; set; } // 外三百回 + public decimal ZhongSanBaiHui { get; set; } // 中三百回 + public decimal NeiSanBaiHui { get; set; } // 内三百回 + public decimal BaiSeJianDanXian { get; set; } // 白色茧单纤 + public decimal HuiSeJianDanXian { get; set; } // 灰色茧单纤 + public decimal HongSeJianDanXian { get; set; } // 红色茧单纤 +} + +/// 茧丝百回纤度明细(对应 F9 biz_prod_qc_yjss_dc_jsbhxd) +[Table(Name = "qc_yjss_dc_jsbhxd")] +public class YjssDcJsbhxd : BaseEntity +{ + public long JsbhxdMainId { get; set; } // 汇总表 Id + public long YjssId { get; set; } // 主表 Id(冗余) + public int YjssType { get; set; } = 1; // 样品类型:1=原料小样 2=综合样 + public int HuiShu { get; set; } // 回数 + public decimal XianDu { get; set; } // 纤度(D) + public int Sort { get; set; } +} diff --git a/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcJsdc.cs b/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcJsdc.cs new file mode 100644 index 0000000..67c3f40 --- /dev/null +++ b/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcJsdc.cs @@ -0,0 +1,72 @@ +using FreeSql.DataAnnotations; +using F9WebAiTest.Domain.Common; + +namespace F9WebAiTest.Domain.Entities.QC.Yjss; + +/// +/// 解舒调查(试缫,对应 F9 biz_prod_qc_yjss_dc_jsdc):一条主表可有多条本记录。 +/// +[Table(Name = "qc_yjss_dc_jsdc")] +public class YjssDcJsdc : BaseEntity +{ + public long YjssId { get; set; } // 主表 Id + public int YjssType { get; set; } = 1; // 样品类型:1=原料小样 2=综合样 + public int XuHao { get; set; } // 序号 + + // ---- 缫丝工艺参数 ---- + public int XuShu { get; set; } // 绪数 + public int DingLi { get; set; } // 定粒 + public int ZhuJianTangWen { get; set; } // 煮茧汤温(℃) + public int SaoSiTangWen { get; set; } // 缫丝汤温(℃) + public int CheSu { get; set; } // 车速(转/分) + public decimal XiaoSiGanLiang { get; set; } // 小丝干量(g) + public decimal XiaoSiGongLiang { get; set; } // 小丝公量(g) + + // ---- 落绪分布 ---- + public int LdfbWai { get; set; } // 外 + public int LdfbZhong { get; set; } // 中 + public int LdfbNei { get; set; } // 内 + public int LdfbSum { get; set; } // 总粒数 + + // ---- 解舒核心 ---- + public string XianJianYouXuLv { get; set; } = ""; // 鲜茧有绪率(区间文本) + public int JiaoShu { get; set; } // 绞数 + public int LingHuiShu { get; set; } // 零回数 + public decimal ShengSiZongChang { get; set; } // 生丝总长(回) + public decimal JianSiZongChang { get; set; } // 茧丝总长(回) + public decimal YiJianSiChang { get; set; } // 一茧丝长(m) + public decimal JieShuSiChang { get; set; } // 解舒丝长(m) + public decimal JieShuLv { get; set; } // 解舒率(%) + public decimal JieShuGuangZhe { get; set; } // 解舒光折(kg) + public decimal ShengSiXianDu { get; set; } // 生丝纤度(D) + public decimal JianSiXianDu { get; set; } // 茧丝纤度(D) + + // ---- 蛹体粒数 / 比例 ---- + public int YtLsNeiYin { get; set; } // 内印 + public int YtLsMaoZu { get; set; } // 毛足 + public int YtLsChuanTou { get; set; } // 穿头 + public int YtLsShuangGong { get; set; } // 双宫 + public decimal YtBlNeiYin { get; set; } // 内印比例 + public decimal YtBlMaoZu { get; set; } // 毛足比例 + public decimal YtBlChuanTou { get; set; } // 穿头比例 + public decimal YtBlShuangGong { get; set; } // 双宫比例 + + // ---- 附煮 ---- + public string FzCheJian { get; set; } = ""; // 附煮车间 + public string FzGyzkName { get; set; } = ""; // 附煮工艺庄口 + public string FzZhengZhuWenDu { get; set; } = ""; // 蒸煮室温度 + public string FzTiaoZhengBuWenDu { get; set; } = ""; // 调整部温度 + public string FzCheSu { get; set; } = ""; // 附煮车速 + + // ---- 屑物 ---- + public decimal XwJianBaLiang { get; set; } // 总茧巴量 + public decimal XwYongYiZhongLiang { get; set; } // 蛹衣重量 + public decimal XwYongYiLv { get; set; } // 蛹衣率 + public decimal XwLeiJie { get; set; } // 类节 + public decimal XwWanMiDiaoCao { get; set; } // 万米吊糙 + public decimal XwJianBaBiLi { get; set; } // 茧巴比例 + + // ---- 样茧 ---- + public int JieShuLiShu { get; set; } // 解舒样茧粒数 + public decimal JieShuZhongLiang { get; set; } // 解舒样茧重量(g) +} diff --git a/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcQjjj.cs b/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcQjjj.cs new file mode 100644 index 0000000..9c300b7 --- /dev/null +++ b/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcQjjj.cs @@ -0,0 +1,50 @@ +using FreeSql.DataAnnotations; +using F9WebAiTest.Domain.Common; + +namespace F9WebAiTest.Domain.Entities.QC.Yjss; + +/// +/// 清洁洁净(对应 F9 biz_prod_qc_yjss_dc_qjjj): +/// 匀度(一/二/三度) + 洁净档位片数(100~60) + 疵点粒数(次要/普通) + 计算出的清洁、洁净、片数。 +/// +[Table(Name = "qc_yjss_dc_qjjj")] +public class YjssDcQjjj : BaseEntity +{ + public long YjssId { get; set; } // 主表 Id + public int YjssType { get; set; } = 1; // 样品类型:1=原料小样 2=综合样 + + // ---- 匀度 ---- + public int YdYiDu { get; set; } // 一度 + public int YdErDu { get; set; } // 二度 + public int YdSanDu { get; set; } // 三度 + + // ---- 洁净档位(片数)---- + public int Jj100 { get; set; } + public int Jj95 { get; set; } + public int Jj90 { get; set; } + public int Jj85 { get; set; } + public int Jj80 { get; set; } + public int Jj75 { get; set; } + public int Jj70 { get; set; } + public int Jj65 { get; set; } + public int Jj60 { get; set; } + + // ---- 次要疵点 ---- + public int QjTdc { get; set; } // 特大糙 + public int QjFs { get; set; } // 废丝 + public int QjDc { get; set; } // 大糙 + public int QjNfc { get; set; } // 黏附糙 + public int QjDcj { get; set; } // 大长结 + public int QjZlx { get; set; } // 重螺旋 + + // ---- 普通疵点 ---- + public int QjXc { get; set; } // 小糙 + public int QjCj { get; set; } // 长结 + public int QjLx { get; set; } // 螺旋 + public int QjHjls { get; set; } // 环结及裂丝 + + // ---- 结果 ---- + public int PianShu { get; set; } // 片数 + public decimal QingJie { get; set; } // 清洁 + public decimal JingJie { get; set; } // 洁净 +} diff --git a/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcQpdc.cs b/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcQpdc.cs new file mode 100644 index 0000000..5f0a8b5 --- /dev/null +++ b/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcQpdc.cs @@ -0,0 +1,29 @@ +using FreeSql.DataAnnotations; +using F9WebAiTest.Domain.Common; + +namespace F9WebAiTest.Domain.Entities.QC.Yjss; + +/// 切剖调查(对应 F9 biz_prod_qc_yjss_dc_qpdc) +[Table(Name = "qc_yjss_dc_qpdc")] +public class YjssDcQpdc : BaseEntity +{ + public long YjssId { get; set; } // 主表 Id + public int YjssType { get; set; } = 1; // 样品类型:1=原料小样 2=综合样 + public decimal YlQuanJian { get; set; } // 原量-全茧 + public decimal YlJianCeng { get; set; } // 原量-茧层 + public decimal YlYongTi { get; set; } // 原量-蛹体 + public decimal JianCengLv { get; set; } // 茧层率(%) + public decimal GlYongTi { get; set; } // 蛹体干量 + public decimal GlJianCeng { get; set; } // 茧层干量 + public decimal GlZhuJian { get; set; } // 煮茧后干量 + public decimal JianCengLvGongLiang { get; set; } // 公量茧层率(%) + public decimal TuoJiaoLv { get; set; } // 脱胶率(%) + public int LsJiangCan { get; set; } // 僵蚕粒数 + public int LsNeiRan { get; set; } // 内染粒数 + public int LsMeiYong { get; set; } // 霉蛹粒数 + public int LsMaoJiao { get; set; } // 毛脚粒数 + public decimal BlJiangCan { get; set; } // 僵蚕比例(%) + public decimal BlNeiRan { get; set; } // 内染比例(%) + public decimal BlMeiYong { get; set; } // 霉蛹比例(%) + public decimal BlMaoJiao { get; set; } // 毛脚比例(%) +} diff --git a/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcXjdc.cs b/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcXjdc.cs new file mode 100644 index 0000000..9f26cb5 --- /dev/null +++ b/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssDcXjdc.cs @@ -0,0 +1,45 @@ +using FreeSql.DataAnnotations; +using F9WebAiTest.Domain.Common; + +namespace F9WebAiTest.Domain.Entities.QC.Yjss; + +/// +/// 选茧调查(对应 F9 biz_prod_qc_yjss_dc_xjdc): +/// 11 个茧别重量 + 11 个比例 + 公斤茧粒数 + 解舒样茧粒数/重量。 +/// +[Table(Name = "qc_yjss_dc_xjdc")] +public class YjssDcXjdc : BaseEntity +{ + public long YjssId { get; set; } // 主表 Id(原料小样 / 综合样) + public int YjssType { get; set; } = 1; // 样品类型:1=原料小样 2=综合样(两张主表 Id 各自自增,需判别) + + // ---- 茧重(g)---- + public decimal ZlLtj { get; set; } // 联统茧 + public decimal ZlHwj { get; set; } // 好五级茧 + public decimal ZlWj { get; set; } // 五级茧 + public decimal ZlMianJian { get; set; } // 绵茧 + public decimal ZlHuangBan { get; set; } // 黄斑 + public decimal ZlQuJian { get; set; } // 蛆茧 + public decimal ZlShuangGong { get; set; } // 双宫 + public decimal ZlXueJian { get; set; } // 血茧 + public decimal ZlBaoPi { get; set; } // 薄皮 + public decimal ZlJianYi { get; set; } // 茧衣 + public decimal ZlMeiJian { get; set; } // 霉茧 + + // ---- 比例(%)---- + public decimal BlLtj { get; set; } + public decimal BlHwj { get; set; } + public decimal BlWj { get; set; } + public decimal BlMianJian { get; set; } + public decimal BlHuangBan { get; set; } + public decimal BlQuJian { get; set; } + public decimal BlShuangGong { get; set; } + public decimal BlXueJian { get; set; } + public decimal BlBaoPi { get; set; } + public decimal BlJianYi { get; set; } + public decimal BlMeiJian { get; set; } + + public int GongJinLiShu { get; set; } // 公斤茧粒数 + public int JieShuLiShu { get; set; } // 解舒样茧粒数 + public decimal JieShuZhongLiang { get; set; } // 解舒样茧重量(g) +} diff --git a/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssMain.cs b/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssMain.cs new file mode 100644 index 0000000..cdb3161 --- /dev/null +++ b/backend/F9WebAiTest.Domain/Entities/QC/Yjss/YjssMain.cs @@ -0,0 +1,41 @@ +using FreeSql.DataAnnotations; +using F9WebAiTest.Domain.Common; + +namespace F9WebAiTest.Domain.Entities.QC.Yjss; + +/// +/// 样茧试缫——原料小样主表(对应 F9 biz_prod_qc_yjss_main_ylxy)。 +/// 能力范围:选茧调查 / 解舒调查 / 清洁洁净。 +/// +[Table(Name = "qc_yjss_main_ylxy")] +public class YjssMainYlxy : BaseEntity +{ + public long YlzkId { get; set; } // 原料庄口 Id + public DateTime Date { get; set; } = DateTime.Today; // 日期 + public int Type { get; set; } = 1; // 类型:1定价抽样 2入库抽样 3鲜茧抽样 + public decimal ZhongLiang { get; set; } // 样茧重量(g) + public decimal HuiChaoLv { get; set; } // 回潮率(%) + public string Memo { get; set; } = ""; // 备注 + public long XjdcId { get; set; } // 选茧调查 Id + public int JsdcCt { get; set; } // 解舒调查记录数 + public long QjjjId { get; set; } // 清洁洁净 Id +} + +/// +/// 样茧试缫——综合样主表(对应 F9 biz_prod_qc_yjss_main_zhy)。 +/// 能力范围:选茧 / 解舒 / 茧幅 / 切剖 / 清洁洁净 / 茧丝百回纤度。 +/// +[Table(Name = "qc_yjss_main_zhy")] +public class YjssMainZhy : BaseEntity +{ + public long GyzkId { get; set; } // 工艺庄口 Id + public DateTime Date { get; set; } = DateTime.Today; // 日期 + public decimal ZhongLiang { get; set; } // 样茧重量(g) + public string Memo { get; set; } = ""; // 备注 + public long XjdcId { get; set; } // 选茧调查 Id + public int JsdcCt { get; set; } // 解舒调查记录数 + public long JfdcId { get; set; } // 茧幅调查 Id + public long QpdcId { get; set; } // 切剖调查 Id + public long QjjjId { get; set; } // 清洁洁净 Id + public int JsbhxdCt { get; set; } // 茧丝百回纤度记录数 +} diff --git a/backend/F9WebAiTest.Domain/Entities/SYS/Reports/Report.cs b/backend/F9WebAiTest.Domain/Entities/SYS/Reports/Report.cs new file mode 100644 index 0000000..0ac4043 --- /dev/null +++ b/backend/F9WebAiTest.Domain/Entities/SYS/Reports/Report.cs @@ -0,0 +1,130 @@ +using FreeSql.DataAnnotations; +using F9WebAiTest.Domain.Events.SYS.Reports; + +namespace F9WebAiTest.Domain.Entities.SYS.Reports; + +/// +/// 报表定义(Grid++Report 模板)。每张报表对应一个 .grf 模板与一段数据查询 SQL。 +/// 权限模型:创建/更新报表时自动同步一条 Permission(ResourceType=报表), +/// 角色在「角色管理 - 报表权限」中分配给用户后即可在报表中心查看。 +/// +[Table(Name = "sys_reports")] +public class Report : AggregateRoot +{ + [Column(Name = "code")] + public string Code { get; private set; } = ""; // 唯一标识,作为权限码的 resourceIdentity + + [Column(Name = "name")] + public string Name { get; private set; } = ""; // 显示名称 + + [Column(Name = "category")] + public string Category { get; private set; } = "通用"; // 分类 + + [Column(Name = "description")] + public string Description { get; private set; } = ""; // 描述 + + /// 数据集 SQL(必须以 SELECT 开头)。FreeSql ExecuteDataTable 执行。 + [Column(Name = "data_sql")] + public string DataSql { get; private set; } = ""; + + /// 数据集查询参数定义 JSON:[{"name":"deptId","label":"部门","type":"int","default":0}] + [Column(Name = "data_params")] + public string DataParams { get; private set; } = "[]"; + + /// + /// 查询配置 JSON(F9 ReportConfig 约定): + /// { "DsName":"查询名(后端 IReportDataProvider 解析)", "ViewerType":0, + /// "Parms":[{ "Name":"q_yearmonth","Label":"账期","Prompt":"","IsRequired":false, + /// "ControlType":"AntdUI.DatePickerYearMonth","Ds_Static":null,"Ds_Service":null,"DefaultValue":"" }] } + /// DsName 优先于 DataSql。 + /// + [Column(Name = "config")] + public string Config { get; private set; } = ""; + + /// + /// 报表模板文件 Id(对齐 F9 Report.ReportFileId)。 + /// 模板实例存储在 ReportFile(sys_report_files.DesignContent),与报表定义分离。 + /// + [Column(Name = "report_file_id")] + public long ReportFileId { get; private set; } + + // ---------- 以下三个字段已废弃(历史遗留列,仅供启动时把旧模板迁移到 ReportFile 读取) ---------- + // 新代码一律通过 ReportFile.DesignContent 读写模板。 + + /// [已废弃] 旧版模板二进制,迁移到 ReportFile 后不再写入 + [Column(Name = "template_data", MapType = typeof(byte[]))] + public byte[]? TemplateData { get; private set; } + + [Column(Name = "template_name")] + public string TemplateName { get; private set; } = ""; + + [Column(Name = "template_size")] + public int TemplateSize { get; private set; } + + [Column(Name = "sort_order")] + public int SortOrder { get; private set; } + + [Column(Name = "is_active")] + public bool IsActive { get; private set; } = true; + + public Report() { } + + public Report(long id, string code, string name, string category, string description, string dataSql, string dataParams, int sortOrder, string config = "") + { + Id = id; + Code = code ?? ""; + Name = name ?? ""; + Category = string.IsNullOrWhiteSpace(category) ? "通用" : category.Trim(); + Description = description ?? ""; + DataSql = dataSql ?? ""; + DataParams = string.IsNullOrWhiteSpace(dataParams) ? "[]" : dataParams; + Config = config ?? ""; + SortOrder = sortOrder; + IsActive = true; + CreatedAt = DateTime.Now; + AddDomainEvent(new Report_ChangedDomainEvent(Id)); + } + + public void UpdateBasic(string code, string name, string category, string description, string dataSql, string dataParams, int sortOrder, string? config = null) + { + Code = code ?? Code; + Name = name ?? Name; + Category = string.IsNullOrWhiteSpace(category) ? Category : category.Trim(); + Description = description ?? Description; + DataSql = dataSql ?? DataSql; + DataParams = string.IsNullOrWhiteSpace(dataParams) ? DataParams : dataParams; + Config = config ?? Config; + SortOrder = sortOrder; + UpdatedAt = DateTime.Now; + AddDomainEvent(new Report_ChangedDomainEvent(Id)); + } + + /// [已废弃] 旧版把模板二进制直接写在报表上;现模板存于 ReportFile.DesignContent + public void UploadTemplate(byte[] data, string fileName) + { + TemplateData = data ?? Array.Empty(); + TemplateName = fileName ?? ""; + TemplateSize = TemplateData.Length; + UpdatedAt = DateTime.Now; + AddDomainEvent(new Report_ChangedDomainEvent(Id)); + } + + /// 绑定模板文件实例(对齐 F9:Report.ReportFileId → ReportFile) + public void BindReportFile(long reportFileId) + { + ReportFileId = reportFileId; + UpdatedAt = DateTime.Now; + AddDomainEvent(new Report_ChangedDomainEvent(Id)); + } + + /// 迁移完成后清空废弃的旧模板字段 + public void ClearLegacyTemplate() + { + TemplateData = null; + TemplateName = ""; + TemplateSize = 0; + } + + public void Activate() { IsActive = true; UpdatedAt = DateTime.Now; AddDomainEvent(new Report_ChangedDomainEvent(Id)); } + public void Deactivate() { IsActive = false; UpdatedAt = DateTime.Now; AddDomainEvent(new Report_ChangedDomainEvent(Id)); } +} diff --git a/backend/F9WebAiTest.Domain/Entities/SYS/Reports/ReportFile.cs b/backend/F9WebAiTest.Domain/Entities/SYS/Reports/ReportFile.cs new file mode 100644 index 0000000..7b6ad46 --- /dev/null +++ b/backend/F9WebAiTest.Domain/Entities/SYS/Reports/ReportFile.cs @@ -0,0 +1,35 @@ +using FreeSql.DataAnnotations; + +namespace F9WebAiTest.Domain.Entities.SYS.Reports; + +/// +/// 报表模板文件(对齐 BBIT.F9.Domain.Entities.SYS.Reports.ReportFile)。 +/// 报表模板作为独立实体与报表定义(Report)分离存储,Report 通过 ReportFileId 关联其模板实例。 +/// .grf 本质是 UTF-8 JSON 文本,故以文本(DesignContent)保存。 +/// +[Table(Name = "sys_report_files")] +public class ReportFile : BusinessEntity +{ + [Column(Name = "report_id")] + public long ReportId { get; private set; } + + /// 模板设计内容(.grf 的 JSON 文本) + [Column(Name = "design_content", StringLength = -2)] + public string DesignContent { get; private set; } = ""; + + public ReportFile() { } + + public ReportFile(long id, long reportId, string designContent) + { + Id = id; + ReportId = reportId; + DesignContent = designContent ?? ""; + CreatedAt = DateTime.Now; + } + + public void UpdateContent(string designContent) + { + DesignContent = designContent ?? ""; + UpdatedAt = DateTime.Now; + } +} diff --git a/backend/F9WebAiTest.Domain/Events/SYS/Reports/Report_ChangedDomainEvent.cs b/backend/F9WebAiTest.Domain/Events/SYS/Reports/Report_ChangedDomainEvent.cs new file mode 100644 index 0000000..f106db9 --- /dev/null +++ b/backend/F9WebAiTest.Domain/Events/SYS/Reports/Report_ChangedDomainEvent.cs @@ -0,0 +1,6 @@ +using F9WebAiTest.Domain.Entities; + +namespace F9WebAiTest.Domain.Events.SYS.Reports; + +/// 报表变更领域事件(用于缓存刷新等) +public record Report_ChangedDomainEvent(long ReportId) : DomainEvent; diff --git a/backend/F9WebAiTest.Infrastructure/Assets/ReportTemplates/employee_directory.grf b/backend/F9WebAiTest.Infrastructure/Assets/ReportTemplates/employee_directory.grf new file mode 100644 index 0000000..a94d44f --- /dev/null +++ b/backend/F9WebAiTest.Infrastructure/Assets/ReportTemplates/employee_directory.grf @@ -0,0 +1,376 @@ +{ + "Version":"6.8.5.0", + "Title":"职工花名册", + "Author":"F9 报表中心", + "Description":"示例报表:列出职工的基本信息(数据源由 DsName=RepDt_Sys_Users 提供)。", + "Font":{ + "Name":"宋体", + "Size":90000, + "Weight":400, + "Charset":134 + }, + "Printer":{ + "TopMargin":1.5, + "BottomMargin":1.5 + }, + "DetailGrid":{ + "CenterView":true, + "PrintAdaptMethod":"ResizeToFit", + "Recordset":{ + "Field":[ + { + "Name":"EmployeeCode" + }, + { + "Name":"EmployeeName" + }, + { + "Name":"Gender" + }, + { + "Name":"DepName" + }, + { + "Name":"WorktypeName" + }, + { + "Name":"Phone" + }, + { + "Name":"RuzhiDate" + } + ] + }, + "Column":[ + { + "Name":"EmployeeCode", + "Width":2.5 + }, + { + "Name":"EmployeeName", + "Width":2.2 + }, + { + "Name":"Gender", + "Width":1.2 + }, + { + "Name":"DepName", + "Width":2.8 + }, + { + "Name":"WorktypeName", + "Width":2.4 + }, + { + "Name":"Phone", + "Width":2.6 + }, + { + "Name":"RuzhiDate", + "Width":2.4 + } + ], + "ColumnContent":{ + "Height":0.79375, + "ColumnContentCell":[ + { + "Column":"EmployeeCode", + "DataField":"EmployeeCode" + }, + { + "Column":"EmployeeName", + "DataField":"EmployeeName" + }, + { + "Column":"Gender", + "DataField":"Gender" + }, + { + "Column":"DepName", + "DataField":"DepName" + }, + { + "Column":"WorktypeName", + "DataField":"WorktypeName" + }, + { + "Column":"Phone", + "DataField":"Phone" + }, + { + "Column":"RuzhiDate", + "DataField":"RuzhiDate" + } + ] + }, + "ColumnTitle":{ + "BackColor":"C0C0C0", + "Height":0.79375, + "Font":{ + "Name":"宋体", + "Size":90000, + "Bold":true, + "Charset":134 + }, + "RepeatStyle":"OnPage", + "ColumnTitleCell":[ + { + "GroupTitle":false, + "Column":"EmployeeCode", + "BackColor":"FFFFFF", + "Text":"工号" + }, + { + "GroupTitle":false, + "Column":"EmployeeName", + "BackColor":"FFFFFF", + "Text":"姓名" + }, + { + "GroupTitle":false, + "Column":"Gender", + "BackColor":"FFFFFF", + "Text":"性别" + }, + { + "GroupTitle":false, + "Column":"DepName", + "BackColor":"FFFFFF", + "Text":"部门" + }, + { + "GroupTitle":false, + "Column":"WorktypeName", + "BackColor":"FFFFFF", + "Text":"工种" + }, + { + "GroupTitle":false, + "Column":"Phone", + "BackColor":"FFFFFF", + "Text":"电话" + }, + { + "GroupTitle":false, + "Column":"RuzhiDate", + "BackColor":"FFFFFF", + "Text":"入职日期" + } + ] + } + }, + "PageHeader":{ + "Height":1.32292, + "Control":[ + { + "Type":"MemoBox", + "Name":"MemoBox1", + "Anchor":"[Top|Right]", + "Left":9.5, + "Top":0.396875, + "Width":6.3, + "Height":0.47625, + "Font":{ + "Name":"楷体", + "Size":120000, + "Bold":true, + "Charset":134 + }, + "Text":"职工花名册 · 智慧缫丝系统" + }, + { + "Type":"Line", + "Name":"Line1", + "Anchor":"[Left|Top|Right]", + "Top":1.00542, + "Width":16.0073, + "Pen":{ + "Width":2 + } + } + ] + }, + "PageFooter":{ + "Height":1.32292, + "Control":[ + { + "Type":"MemoBox", + "Name":"MemoBox2", + "Anchor":"[Top|Right]", + "Left":13.2027, + "Top":0.396875, + "Width":2.59292, + "Height":0.608542, + "Text":"第[#SystemVar(PageNumber)#]页 共[#SystemVar(PageCount)#]页" + }, + { + "Type":"MemoBox", + "Name":"MemoBox3", + "Left":0.211667, + "Top":0.396875, + "Width":6.19125, + "Height":0.608542, + "Text":"打印时间:[#SystemVar(CurrentDateTime):yyyy年MM月dd日 HH:mm:ss#]" + } + ] + }, + "ReportHeader":[ + { + "Height":0.978958, + "Control":[ + { + "Type":"StaticBox", + "Name":"StaticBox1", + "Center":"Horizontal", + "Left":6.29708, + "Top":0.185208, + "Width":3.41313, + "Height":0.582083, + "Font":{ + "Name":"宋体", + "Size":150000, + "Bold":true, + "Charset":134 + }, + "Text":"职工花名册" + } + ] + }, + { + "Height":0.6, + "Control":[ + { + "Type":"StaticBox", + "Name":"LabelDep", + "Left":0.2, + "Top":0.05, + "Width":1.4, + "Height":0.5, + "Font":{ + "Name":"宋体", + "Size":90000, + "Charset":134 + }, + "Text":"部门:" + }, + { + "Type":"StaticBox", + "Name":"ParamDep", + "Left":1.7, + "Top":0.05, + "Width":2.2, + "Height":0.5, + "Font":{ + "Name":"宋体", + "Size":90000, + "Charset":134 + }, + "Parameter":"部门" + }, + { + "Type":"StaticBox", + "Name":"LabelKeyword", + "Left":4.0, + "Top":0.05, + "Width":1.6, + "Height":0.5, + "Font":{ + "Name":"宋体", + "Size":90000, + "Charset":134 + }, + "Text":"关键字:" + }, + { + "Type":"StaticBox", + "Name":"ParamKeyword", + "Left":5.7, + "Top":0.05, + "Width":1.8, + "Height":0.5, + "Font":{ + "Name":"宋体", + "Size":90000, + "Charset":134 + }, + "Parameter":"关键字" + }, + { + "Type":"StaticBox", + "Name":"LabelCount", + "Left":7.6, + "Top":0.05, + "Width":1.6, + "Height":0.5, + "Font":{ + "Name":"宋体", + "Size":90000, + "Charset":134 + }, + "Text":"记录数:" + }, + { + "Type":"StaticBox", + "Name":"ParamCount", + "Left":9.3, + "Top":0.05, + "Width":1.2, + "Height":0.5, + "Font":{ + "Name":"宋体", + "Size":90000, + "Charset":134 + }, + "Parameter":"记录数" + }, + { + "Type":"StaticBox", + "Name":"LabelQueryTime", + "Left":10.6, + "Top":0.05, + "Width":2.0, + "Height":0.5, + "Font":{ + "Name":"宋体", + "Size":90000, + "Charset":134 + }, + "Text":"查询时间:" + }, + { + "Type":"StaticBox", + "Name":"ParamQueryTime", + "Left":12.7, + "Top":0.05, + "Width":3.3, + "Height":0.5, + "Font":{ + "Name":"宋体", + "Size":90000, + "Charset":134 + }, + "Parameter":"查询时间" + } + ] + } + ], + "Parameter":[ + { + "Name":"部门", + "DataType":"String" + }, + { + "Name":"关键字", + "DataType":"String" + }, + { + "Name":"记录数", + "DataType":"Integer" + }, + { + "Name":"查询时间", + "DataType":"String" + } + ] +} diff --git a/backend/F9WebAiTest.Infrastructure/Assets/ReportTemplates/lsjl.grf b/backend/F9WebAiTest.Infrastructure/Assets/ReportTemplates/lsjl.grf new file mode 100644 index 0000000..e122a1d --- /dev/null +++ b/backend/F9WebAiTest.Infrastructure/Assets/ReportTemplates/lsjl.grf @@ -0,0 +1,312 @@ +{ + "Version": "6.8.5.0", + "Title": "落丝记录", + "Author": "F9 报表中心", + "Description": "生产-前缫:落丝记录明细(数据源 DsName=RepDt_Prod_Lsjl)。", + "Font": { + "Name": "宋体", + "Size": 90000, + "Weight": 400, + "Charset": 134 + }, + "Printer": { + "TopMargin": 1.5, + "BottomMargin": 1.5 + }, + "DetailGrid": { + "CenterView": true, + "PrintAdaptMethod": "ResizeToFit", + "Recordset": { + "Field": [ + { "Name": "日期" }, + { "Name": "车间" }, + { "Name": "机组" }, + { "Name": "庄口" }, + { "Name": "回数" }, + { "Name": "颜色" }, + { "Name": "半车" }, + { "Name": "落丝时间" }, + { "Name": "缫丝桶数" } + ] + }, + "Column": [ + { "Name": "日期", "Width": 2.0 }, + { "Name": "车间", "Width": 3.0 }, + { "Name": "机组", "Width": 1.4 }, + { "Name": "庄口", "Width": 3.2 }, + { "Name": "回数", "Width": 1.4 }, + { "Name": "颜色", "Width": 1.6 }, + { "Name": "半车", "Width": 1.4 }, + { "Name": "落丝时间", "Width": 1.8 }, + { "Name": "缫丝桶数", "Width": 1.5 } + ], + "ColumnContent": { + "Height": 0.79375, + "ColumnContentCell": [ + { "Column": "日期", "DataField": "日期" }, + { "Column": "车间", "DataField": "车间" }, + { "Column": "机组", "DataField": "机组" }, + { "Column": "庄口", "DataField": "庄口" }, + { "Column": "回数", "DataField": "回数" }, + { "Column": "颜色", "DataField": "颜色" }, + { "Column": "半车", "DataField": "半车" }, + { "Column": "落丝时间", "DataField": "落丝时间" }, + { "Column": "缫丝桶数", "DataField": "缫丝桶数" } + ] + }, + "ColumnTitle": { + "BackColor": "C0C0C0", + "Height": 0.79375, + "Font": { + "Name": "宋体", + "Size": 90000, + "Bold": true, + "Charset": 134 + }, + "RepeatStyle": "OnPage", + "ColumnTitleCell": [ + { "GroupTitle": false, "Column": "日期", "BackColor": "FFFFFF", "Text": "日期" }, + { "GroupTitle": false, "Column": "车间", "BackColor": "FFFFFF", "Text": "车间" }, + { "GroupTitle": false, "Column": "机组", "BackColor": "FFFFFF", "Text": "机组" }, + { "GroupTitle": false, "Column": "庄口", "BackColor": "FFFFFF", "Text": "庄口" }, + { "GroupTitle": false, "Column": "回数", "BackColor": "FFFFFF", "Text": "回数" }, + { "GroupTitle": false, "Column": "颜色", "BackColor": "FFFFFF", "Text": "颜色" }, + { "GroupTitle": false, "Column": "半车", "BackColor": "FFFFFF", "Text": "半车" }, + { "GroupTitle": false, "Column": "落丝时间", "BackColor": "FFFFFF", "Text": "落丝时间" }, + { "GroupTitle": false, "Column": "缫丝桶数", "BackColor": "FFFFFF", "Text": "缫丝桶数" } + ] + } + }, + "PageHeader": { + "Height": 1.32292, + "Control": [ + { + "Type": "MemoBox", + "Name": "MemoBox1", + "Anchor": "[Top|Right]", + "Left": 9.5, + "Top": 0.396875, + "Width": 6.3, + "Height": 0.47625, + "Font": { + "Name": "楷体", + "Size": 120000, + "Bold": true, + "Charset": 134 + }, + "Text": "落丝记录 · 智慧缫丝系统" + }, + { + "Type": "Line", + "Name": "Line1", + "Anchor": "[Left|Top|Right]", + "Top": 1.00542, + "Width": 16.0073, + "Pen": { + "Width": 2 + } + } + ] + }, + "PageFooter": { + "Height": 1.32292, + "Control": [ + { + "Type": "MemoBox", + "Name": "MemoBox2", + "Anchor": "[Top|Right]", + "Left": 13.2027, + "Top": 0.396875, + "Width": 2.59292, + "Height": 0.608542, + "Text": "第[#SystemVar(PageNumber)#]页 共[#SystemVar(PageCount)#]页" + }, + { + "Type": "MemoBox", + "Name": "MemoBox3", + "Left": 0.211667, + "Top": 0.396875, + "Width": 6.19125, + "Height": 0.608542, + "Text": "打印时间:[#SystemVar(CurrentDateTime):yyyy年MM月dd日 HH:mm:ss#]" + } + ] + }, + "ReportHeader": [ + { + "Height": 0.978958, + "Control": [ + { + "Type": "StaticBox", + "Name": "StaticBoxTitle", + "Center": "Horizontal", + "Left": 6.29708, + "Top": 0.185208, + "Width": 3.41313, + "Height": 0.582083, + "Font": { + "Name": "宋体", + "Size": 150000, + "Bold": true, + "Charset": 134 + }, + "Text": "落丝记录" + } + ] + }, + { + "Height": 1.2, + "Control": [ + { + "Type": "StaticBox", + "Name": "LabelDateStart", + "Left": 0.3, + "Top": 0.05, + "Width": 1.3, + "Height": 0.5, + "Font": { "Name": "宋体", "Size": 90000, "Charset": 134 }, + "Text": "开始日期:" + }, + { + "Type": "MemoBox", + "Name": "ParamDateStart", + "Left": 1.6, + "Top": 0.05, + "Width": 2.5, + "Height": 0.5, + "Font": { "Name": "宋体", "Size": 90000, "Charset": 134 }, + "Text": "[#{开始日期}#]" + }, + { + "Type": "StaticBox", + "Name": "LabelDateEnd", + "Left": 4.4, + "Top": 0.05, + "Width": 1.3, + "Height": 0.5, + "Font": { "Name": "宋体", "Size": 90000, "Charset": 134 }, + "Text": "结束日期:" + }, + { + "Type": "MemoBox", + "Name": "ParamDateEnd", + "Left": 5.7, + "Top": 0.05, + "Width": 2.5, + "Height": 0.5, + "Font": { "Name": "宋体", "Size": 90000, "Charset": 134 }, + "Text": "[#{结束日期}#]" + }, + { + "Type": "StaticBox", + "Name": "LabelDep", + "Left": 8.5, + "Top": 0.05, + "Width": 1.3, + "Height": 0.5, + "Font": { "Name": "宋体", "Size": 90000, "Charset": 134 }, + "Text": "车间:" + }, + { + "Type": "MemoBox", + "Name": "ParamDep", + "Left": 9.8, + "Top": 0.05, + "Width": 2.5, + "Height": 0.5, + "Font": { "Name": "宋体", "Size": 90000, "Charset": 134 }, + "Text": "[#{车间}#]" + }, + { + "Type": "StaticBox", + "Name": "LabelJizu", + "Left": 12.6, + "Top": 0.05, + "Width": 1.3, + "Height": 0.5, + "Font": { "Name": "宋体", "Size": 90000, "Charset": 134 }, + "Text": "机组:" + }, + { + "Type": "MemoBox", + "Name": "ParamJizu", + "Left": 13.9, + "Top": 0.05, + "Width": 2.5, + "Height": 0.5, + "Font": { "Name": "宋体", "Size": 90000, "Charset": 134 }, + "Text": "[#{机组}#]" + }, + { + "Type": "StaticBox", + "Name": "LabelZk", + "Left": 0.3, + "Top": 0.6, + "Width": 1.3, + "Height": 0.5, + "Font": { "Name": "宋体", "Size": 90000, "Charset": 134 }, + "Text": "庄口:" + }, + { + "Type": "MemoBox", + "Name": "ParamZk", + "Left": 1.6, + "Top": 0.6, + "Width": 2.5, + "Height": 0.5, + "Font": { "Name": "宋体", "Size": 90000, "Charset": 134 }, + "Text": "[#{庄口}#]" + }, + { + "Type": "StaticBox", + "Name": "LabelCount", + "Left": 4.4, + "Top": 0.6, + "Width": 1.3, + "Height": 0.5, + "Font": { "Name": "宋体", "Size": 90000, "Charset": 134 }, + "Text": "记录数:" + }, + { + "Type": "MemoBox", + "Name": "ParamCount", + "Left": 5.7, + "Top": 0.6, + "Width": 2.5, + "Height": 0.5, + "Font": { "Name": "宋体", "Size": 90000, "Charset": 134 }, + "Text": "[#{记录数}#]" + }, + { + "Type": "StaticBox", + "Name": "LabelQueryTime", + "Left": 8.5, + "Top": 0.6, + "Width": 1.3, + "Height": 0.5, + "Font": { "Name": "宋体", "Size": 90000, "Charset": 134 }, + "Text": "查询时间:" + }, + { + "Type": "MemoBox", + "Name": "ParamQueryTime", + "Left": 9.8, + "Top": 0.6, + "Width": 3.6, + "Height": 0.5, + "Font": { "Name": "宋体", "Size": 90000, "Charset": 134 }, + "Text": "[#{查询时间}#]" + } + ] + } + ], + "Parameter": [ + { "Name": "开始日期", "DataType": "String" }, + { "Name": "结束日期", "DataType": "String" }, + { "Name": "车间", "DataType": "String" }, + { "Name": "机组", "DataType": "String" }, + { "Name": "庄口", "DataType": "String" }, + { "Name": "记录数", "DataType": "Integer" }, + { "Name": "查询时间", "DataType": "String" } + ] +} diff --git a/backend/F9WebAiTest.Infrastructure/Data/DbSeeder.cs b/backend/F9WebAiTest.Infrastructure/Data/DbSeeder.cs index a6bcd53..0310dd2 100644 --- a/backend/F9WebAiTest.Infrastructure/Data/DbSeeder.cs +++ b/backend/F9WebAiTest.Infrastructure/Data/DbSeeder.cs @@ -1,4 +1,5 @@ using System.Data; +using System.Text; using FreeSql; using F9WebAiTest.Application.Interfaces.SnowflakeID; using F9WebAiTest.Domain.Common; @@ -10,6 +11,8 @@ using SysMenu = F9WebAiTest.Domain.Entities.SYS.RBAC.Menu; using SysRole = F9WebAiTest.Domain.Entities.SYS.RBAC.Role; using SysPermission = F9WebAiTest.Domain.Entities.SYS.RBAC.Permission; using SysRolePermission = F9WebAiTest.Domain.Entities.SYS.RBAC.RolePermission; +using SysReport = F9WebAiTest.Domain.Entities.SYS.Reports.Report; +using SysReportFile = F9WebAiTest.Domain.Entities.SYS.Reports.ReportFile; namespace F9WebAiTest.Infrastructure.Data; @@ -88,6 +91,20 @@ public static class DbSeeder // 6.69 修正前缫部门的职责类型(车间班次菜单随"生产-前缫"一起处理) FixQianxianDutyType(fsql); + // 6.695 质量模块(工艺执行书 / 样茧试缫) + SeedQcMenus(fsql, snowflake); + + // 6.71 报表中心菜单(/report 目录 + 报表中心/报表管理两个页面) + SeedReportMenus(fsql, snowflake); + // 6.72 旧版内嵌模板(sys_reports.template_data)迁移为独立模板实例 ReportFile + MigrateReportTemplateToFile(fsql, snowflake); + + // 6.72 报表中心示例报表(职工花名册) + SeedSampleReport(fsql, snowflake); + + // 6.73 落丝记录报表(生产-前缫,DsName=RepDt_Prod_Lsjl) + SeedLsjlReport(fsql, snowflake); + // 6.7 为菜单创建权限单元(Permission),并修正旧菜单权限关联(统一权限模型) FixMenuPermissions(fsql, snowflake); @@ -156,6 +173,7 @@ public static class DbSeeder EnsureColumn(fsql, "NotificationTypes", "RouteTemplate", "TEXT NOT NULL DEFAULT ''"); EnsureColumn(fsql, "NotificationMessages", "Category", "INTEGER NOT NULL DEFAULT 0"); EnsureColumn(fsql, "NotificationMessages", "RoutePath", "TEXT NOT NULL DEFAULT ''"); + EnsureColumn(fsql, "sys_reports", "report_file_id", "INTEGER NOT NULL DEFAULT 0"); } /// 为已有表补加列(列已存在则忽略) @@ -735,6 +753,42 @@ public static class DbSeeder } } + /// + /// 质量模块菜单:一级目录「质量」+ 工艺执行书 / 样茧试缫 两个页面。 + /// 模块落在「生产-后缫」之后、消息中心之前。 + /// + private static void SeedQcMenus(IFreeSql fsql, ISnowflakeIdGenerator snowflake) + { + try + { + EnsureTopMenuWithChildren(fsql, snowflake, "/qc", "质量", "Medal", 7, + new (string Name, string Path, string Component, string Permission, string Icon, int Sort)[] + { + ("工艺执行书", "/qc/gyzxs", "qc/gyzxs/index", "qc:gyzxs:list", "Document", 1), + ("样茧试缫", "/qc/yjss", "qc/yjss/index", "qc:yjss:list", "DataAnalysis", 2) + }); + + foreach (var (path, sort) in new (string Path, int Sort)[] + { + ("/attendance", 2), ("/base", 3), ("/employee", 4), + ("/prod/qianxian", 5), ("/prod/houxian", 6), ("/qc", 7), + ("/notification", 8), ("/sys", 9), ("/report", 10) + }) + { + fsql.Update() + .Set(x => x.SortOrder, sort) + .Where(x => x.ParentId == 0 && x.Path == path && x.SortOrder != sort) + .ExecuteAffrows(); + } + + Console.WriteLine("[DbSeeder] 质量模块菜单已就绪(工艺执行书 / 样茧试缫)"); + } + catch (Exception ex) + { + Console.WriteLine($"[DbSeeder] 质量模块菜单种子跳过:{ex.Message}"); + } + } + /// /// 确保一个一级目录菜单及其下属页面菜单存在(含父级、图标、启用状态校正),并给 admin 角色授权。 /// 注意:图标名必须是 Element Plus 图标组件名。 @@ -894,6 +948,22 @@ public static class DbSeeder // 2. 修正旧菜单权限关联(permission_id 旧值 = Menu.Id,改为 Permission.Id) fsql.Ado.ExecuteNonQuery($"UPDATE sys_rbac_role_permissions SET permission_id = {perm.Id} WHERE permission_id = {menu.Id};"); } + + // 3. 清理失效的菜单权限单元:ResourceId 不对应任何菜单 + // (前端曾用 Number() 传雪花 Id,超出 2^53 后精度丢失,写入的 resource_id 在菜单表里查不到, + // 表现为「勾选保存后重新打开仍是未勾选、该角色登录也看不到菜单」) + var menuIds = fsql.Select().ToList().Select(m => m.Id).ToHashSet(); + var orphanPerms = fsql.Select() + .Where(p => p.ResourceType == Resource_Type_Enum.菜单).ToList() + .Where(p => !menuIds.Contains(p.ResourceId)).ToList(); + foreach (var bad in orphanPerms) + { + fsql.Ado.ExecuteNonQuery($"DELETE FROM sys_rbac_role_permissions WHERE permission_id = {bad.Id};"); + fsql.Ado.ExecuteNonQuery($"DELETE FROM sys_rbac_permissions WHERE id = {bad.Id};"); + } + if (orphanPerms.Count > 0) + Console.WriteLine($"[DbSeeder] 已清理 {orphanPerms.Count} 条失效菜单权限单元(资源 Id 不存在)"); + Console.WriteLine("[DbSeeder] 菜单权限单元同步完成"); } catch (Exception ex) @@ -901,4 +971,399 @@ public static class DbSeeder Console.WriteLine($"[DbSeeder] 菜单权限单元同步跳过:{ex.Message}"); } } -} + + /// 报表中心菜单(/report 目录 + 报表中心 / 报表管理 两个页面菜单,admin 授权) + private static void SeedReportMenus(IFreeSql fsql, ISnowflakeIdGenerator snowflake) + { + try + { + EnsureTopMenuWithChildren(fsql, snowflake, "/report", "报表中心", "DataAnalysis", 9, + new (string Name, string Path, string Component, string Permission, string Icon, int Sort)[] + { + ("报表中心", "/report/center", "report/center/index", "report:center:list", "PieChart", 1), + ("报表管理", "/report/manage", "report/manage/index", "report:manage:list", "Document", 2) + }); + Console.WriteLine("[DbSeeder] 报表中心菜单已就绪"); + } + catch (Exception ex) + { + Console.WriteLine($"[DbSeeder] 报表中心菜单种子跳过:{ex.Message}"); + } + } + + /// + /// 旧版把模板二进制直接存在 sys_reports.template_data,现模板是独立实体 ReportFile(DesignContent 文本), + /// Report 通过 ReportFileId 关联。启动时把旧数据迁移过去,迁移完成后清空旧字段。 + /// + private static void MigrateReportTemplateToFile(IFreeSql fsql, ISnowflakeIdGenerator snowflake) + { + try + { + var legacy = fsql.Select() + .Where(r => !r.IsDeleted && r.ReportFileId == 0 && r.TemplateData != null && r.TemplateData.Length > 0) + .ToList(); + foreach (var report in legacy) + { + var text = Encoding.UTF8.GetString(report.TemplateData!); + if (text.Length > 0 && text[0] == '\uFEFF') text = text.Substring(1); + + var file = new SysReportFile(snowflake.CreateId(), report.Id, text); + fsql.Insert(file).ExecuteAffrows(); + report.BindReportFile(file.Id); + report.ClearLegacyTemplate(); + fsql.Update().SetSource(report).ExecuteAffrows(); + Console.WriteLine($"[DbSeeder] 报表「{report.Name}」模板已迁移到 ReportFile#{file.Id}"); + } + } + catch (Exception ex) + { + Console.WriteLine($"[DbSeeder] 报表模板迁移跳过:{ex.Message}"); + } + } + + /// 报表中心示例报表:职工花名册(模板为独立 ReportFile 实例 + F9 ReportConfig 查询配置) + private static void SeedSampleReport(IFreeSql fsql, ISnowflakeIdGenerator snowflake) + { + try + { + const string code = "employee_directory"; + const string name = "职工花名册"; + const string sql = "SELECT login_name AS LoginName, user_name AS UserName, phone_number AS PhoneNumber, email AS Email FROM sys_rbac_users WHERE is_deleted = 0 ORDER BY id LIMIT 1000"; + // F9 ReportConfig:DsName 指向后端查询提供者;Parms 定义动态查询控件 + const string config = "{\"DsName\":\"RepDt_Sys_Users\",\"ViewerType\":0,\"Parms\":[" + + "{\"Name\":\"q_dep\",\"Label\":\"部门\",\"Prompt\":\"含子部门,留空为全部\",\"IsRequired\":false," + + "\"ControlType\":\"AntdUI.Select\",\"Ds_Static\":null,\"Ds_Service\":\"/sys/department\",\"DefaultValue\":\"\"}," + + "{\"Name\":\"q_like\",\"Label\":\"关键字\",\"Prompt\":\"按工号 / 姓名模糊查询\",\"IsRequired\":false," + + "\"ControlType\":\"AntdUI.Input\",\"Ds_Static\":null,\"Ds_Service\":null,\"DefaultValue\":\"\"}]}"; + + var exist = fsql.Select().Where(r => r.Code == code && !r.IsDeleted).First(); + var templateJson = LoadEmbeddedTemplateJson("employee_directory.grf"); + + // 老版本种子生成的是手写 6.6.8.1 模板(设计器 6.8 无法打开),这里统一替换为 6.8.5.0 官方结构模板 + bool needReplaceTemplate = false; + if (exist != null) + { + var file = fsql.Select().Where(f => f.ReportId == exist.Id && !f.IsDeleted).First(); + needReplaceTemplate = file == null || !file.DesignContent.Contains("6.8.5.0"); + } + + if (exist == null) + { + var report = new SysReport(snowflake.CreateId(), code, name, "职工管理", "示例报表:按部门 / 关键字查询职工基本信息。", sql, "[]", 1, config); + + // 模板实例:ReportFile(对齐 F9 Report.ReportFileId) + if (templateJson != null) + { + var file = new SysReportFile(snowflake.CreateId(), report.Id, templateJson); + fsql.Insert(file).ExecuteAffrows(); + report.BindReportFile(file.Id); + } + fsql.Insert(report).ExecuteAffrows(); + + // 创建对应权限单元 + var perm = new SysPermission(snowflake.CreateId(), Permission_Type_Enum.Visble, Resource_Type_Enum.报表, code, report.Id); + perm.SetCode($"report:{code}:visble"); + fsql.Insert(perm).ExecuteAffrows(); + + // 授权给 admin 角色 + var adminRole = fsql.Select().Where(r => r.Code == "admin").First(); + if (adminRole != null) + { + fsql.Insert(new SysRolePermission(snowflake.CreateId(), adminRole.Id, perm.Id)).ExecuteAffrows(); + } + Console.WriteLine("[DbSeeder] 示例报表「职工花名册」已创建"); + } + else + { + // 已存在:替换旧模板 / 补全缺失的模板与查询配置 + bool changed = false; + if (needReplaceTemplate && templateJson != null) + { + var file = fsql.Select().Where(f => f.ReportId == exist.Id && !f.IsDeleted).First(); + if (file != null) + { + file.UpdateContent(templateJson); + fsql.Update().SetSource(file).ExecuteAffrows(); + } + else + { + file = new SysReportFile(snowflake.CreateId(), exist.Id, templateJson); + fsql.Insert(file).ExecuteAffrows(); + exist.BindReportFile(file.Id); + } + changed = true; + } + if (string.IsNullOrEmpty(exist.Config) || !exist.Config.Contains("q_dep")) + { + exist.UpdateBasic(exist.Code, exist.Name, exist.Category, exist.Description, exist.DataSql, exist.DataParams, exist.SortOrder, config); + changed = true; + } + if (changed) + { + fsql.Update().SetSource(exist).ExecuteAffrows(); + Console.WriteLine("[DbSeeder] 已更新示例报表模板/查询配置"); + } + } + } + catch (Exception ex) + { + Console.WriteLine($"[DbSeeder] 示例报表种子跳过:{ex.Message}"); + } + } + + /// + /// 落丝记录报表(生产-前缫):DsName=RepDt_Prod_Lsjl,参数为日期范围 / 车间 / 机组 / 庄口 / 关键字。 + /// 已存在的报表只补齐查询配置,不覆盖用户在设计器里维护的 .grf 模板。 + /// + private static void SeedLsjlReport(IFreeSql fsql, ISnowflakeIdGenerator snowflake) + { + try + { + const string code = "PROD_QS_1"; + const string name = "落丝记录"; + const string category = "生产.前缫"; + const string dsName = "RepDt_Prod_Lsjl"; + + // 日期范围参数默认值用「不限」:避免 paramInitialValue 把空值默认成今天,导致只查当天 + const string config = "{\"DsName\":\"RepDt_Prod_Lsjl\",\"ViewerType\":0,\"Parms\":[" + + "{\"Name\":\"q_date_start\",\"Label\":\"开始日期\",\"Prompt\":\"留空则不限开始日期\",\"IsRequired\":false," + + "\"ControlType\":\"AntdUI.DatePicker\",\"Ds_Static\":null,\"Ds_Service\":null,\"DefaultValue\":\"不限\"}," + + "{\"Name\":\"q_date_end\",\"Label\":\"结束日期\",\"Prompt\":\"留空则不限结束日期\",\"IsRequired\":false," + + "\"ControlType\":\"AntdUI.DatePicker\",\"Ds_Static\":null,\"Ds_Service\":null,\"DefaultValue\":\"不限\"}," + + "{\"Name\":\"q_dep\",\"Label\":\"车间\",\"Prompt\":\"含子车间,留空为全部\",\"IsRequired\":false," + + "\"ControlType\":\"F9-DEP-QS-SELECT\",\"Ds_Static\":null,\"Ds_Service\":null,\"DefaultValue\":\"\"}," + + "{\"Name\":\"q_jizu\",\"Label\":\"机组\",\"Prompt\":\"留空为全部\",\"IsRequired\":false," + + "\"ControlType\":\"AntdUI.Input\",\"Ds_Static\":null,\"Ds_Service\":null,\"DefaultValue\":\"\"}," + + "{\"Name\":\"q_zk\",\"Label\":\"庄口\",\"Prompt\":\"点击选择庄口\",\"IsRequired\":false," + + "\"ControlType\":\"F9-GYZK-SELECT\",\"Ds_Static\":null,\"Ds_Service\":null,\"DefaultValue\":\"\"}," + + "{\"Name\":\"q_like\",\"Label\":\"关键字\",\"Prompt\":\"庄口 / 规格 / 颜色\",\"IsRequired\":false," + + "\"ControlType\":\"AntdUI.Input\",\"Ds_Static\":null,\"Ds_Service\":null,\"DefaultValue\":\"\"}]}"; + + const string description = "生产-前缫:按日期范围 / 车间 / 机组 / 庄口查询落丝记录明细。"; + + var exist = fsql.Select().Where(r => r.Code == code && !r.IsDeleted).First(); + + if (exist == null) + { + var report = new SysReport(snowflake.CreateId(), code, name, category, description, "", "[]", 2, config); + + var newTemplate = LoadEmbeddedTemplateJson("lsjl.grf"); + if (newTemplate != null) + { + var newFile = new SysReportFile(snowflake.CreateId(), report.Id, newTemplate); + fsql.Insert(newFile).ExecuteAffrows(); + report.BindReportFile(newFile.Id); + } + fsql.Insert(report).ExecuteAffrows(); + + var perm = new SysPermission(snowflake.CreateId(), Permission_Type_Enum.Visble, Resource_Type_Enum.报表, code, report.Id); + perm.SetCode($"report:{code}:visble"); + fsql.Insert(perm).ExecuteAffrows(); + + var adminRole = fsql.Select().Where(r => r.Code == "admin").First(); + if (adminRole != null) + fsql.Insert(new SysRolePermission(snowflake.CreateId(), adminRole.Id, perm.Id)).ExecuteAffrows(); + + Console.WriteLine("[DbSeeder] 落丝记录报表已创建(DsName=RepDt_Prod_Lsjl)"); + return; + } + + // 已存在:DsName 未指向该 Provider、或参数标签全为空(程序生成的占位配置)时补齐查询配置 + // (数据 SQL 清空,避免与 DsName 混淆) + var changed = false; + if (string.IsNullOrEmpty(exist.Config) || !exist.Config.Contains(dsName) + || ConfigParamsMissingLabel(exist.Config)) + { + exist.UpdateBasic(exist.Code, exist.Name, + string.IsNullOrWhiteSpace(exist.Category) ? category : exist.Category, + string.IsNullOrWhiteSpace(exist.Description) ? description : exist.Description, + "", exist.DataParams, exist.SortOrder, config); + changed = true; + Console.WriteLine("[DbSeeder] 落丝记录报表查询配置已更新(DsName=RepDt_Prod_Lsjl)"); + } + + // 模板是占位模板(无标题 / 无参数声明,多为早期上传测试产物)时,替换为内置规范模板; + // 旧版内置模板有两类问题一并升级: + // 1) 用 Parameter 属性绑定报表参数(GR 推荐 [#{参数名}#] 文本标记) + // 2) 把「查询条件」(q_*) 误当成模板参数(那是查询面板的东西,不是报表参数) + var templateJson = LoadEmbeddedTemplateJson("lsjl.grf"); + var file = fsql.Select().Where(f => f.ReportId == exist.Id && !f.IsDeleted).First(); + var oursTemplate = file != null && file.DesignContent.Contains("\"Author\": \"F9 报表中心\""); + var legacyParamBinding = oursTemplate + && (file!.DesignContent.Contains("\"Parameter\": \"开始日期\"") + || TemplateHasQueryParams(file.DesignContent) + || TemplateHasStaticBoxParamBinding(file.DesignContent)); + if (templateJson != null && (file == null || legacyParamBinding || IsPlaceholderReportTemplate(file.DesignContent))) + { + if (file == null) + { + file = new SysReportFile(snowflake.CreateId(), exist.Id, templateJson); + fsql.Insert(file).ExecuteAffrows(); + } + else + { + file.UpdateContent(templateJson); + fsql.Update().SetSource(file).ExecuteAffrows(); + } + exist.BindReportFile(file.Id); + changed = true; + Console.WriteLine("[DbSeeder] 落丝记录报表模板已更新为内置模板(标题 + 查询参数区)"); + } + + if (changed) fsql.Update().SetSource(exist).ExecuteAffrows(); + } + catch (Exception ex) + { + Console.WriteLine($"[DbSeeder] 落丝记录报表种子跳过:{ex.Message}"); + } + } + + /// + /// 判断查询条件配置是否缺少标签(Parms 为空,或所有 Label 都为空 → 程序生成的占位配置)。 + /// 用户手工配过标签的配置不会被种子覆盖。 + /// + private static bool ConfigParamsMissingLabel(string config) + { + if (string.IsNullOrWhiteSpace(config)) return true; + try + { + using var doc = System.Text.Json.JsonDocument.Parse(config); + if (!doc.RootElement.TryGetProperty("Parms", out var ps) + || ps.ValueKind != System.Text.Json.JsonValueKind.Array + || ps.GetArrayLength() == 0) + return true; + + foreach (var p in ps.EnumerateArray()) + { + if (p.ValueKind != System.Text.Json.JsonValueKind.Object) continue; + var label = p.TryGetProperty("Label", out var l) && l.ValueKind == System.Text.Json.JsonValueKind.String + ? l.GetString() ?? "" + : ""; + if (!string.IsNullOrWhiteSpace(label)) return false; + } + return true; + } + catch + { + return true; + } + } + + /// + /// 判断模板是否把报表参数绑在 StaticBox 上(实测 [#{参数}#] 只在 MemoBox 生效,StaticBox 原样输出)。 + /// + private static bool TemplateHasStaticBoxParamBinding(string content) + { + if (string.IsNullOrWhiteSpace(content) || content.StartsWith("_WR", StringComparison.Ordinal)) return false; + try + { + using var doc = System.Text.Json.JsonDocument.Parse(content); + return ContainsStaticBoxParamBinding(doc.RootElement); + } + catch + { + return false; + } + } + + private static bool ContainsStaticBoxParamBinding(System.Text.Json.JsonElement el) + { + if (el.ValueKind == System.Text.Json.JsonValueKind.Object) + { + var type = el.TryGetProperty("Type", out var t) && t.ValueKind == System.Text.Json.JsonValueKind.String + ? t.GetString() ?? "" + : ""; + var text = el.TryGetProperty("Text", out var x) && x.ValueKind == System.Text.Json.JsonValueKind.String + ? x.GetString() ?? "" + : ""; + if (type == "StaticBox" && text.Contains("[#{")) return true; + + foreach (var prop in el.EnumerateObject()) + if (ContainsStaticBoxParamBinding(prop.Value)) return true; + } + else if (el.ValueKind == System.Text.Json.JsonValueKind.Array) + { + foreach (var item in el.EnumerateArray()) + if (ContainsStaticBoxParamBinding(item)) return true; + } + return false; + } + + /// + /// 判断模板是否把「查询条件」(q_* 参数) 误写成了模板参数。 + /// 模板参数应来自后端 Parameter_Data(如 车间 / 记录数 / 查询时间),不是查询面板的 q_*。 + /// + private static bool TemplateHasQueryParams(string content) + { + if (string.IsNullOrWhiteSpace(content) || content.StartsWith("_WR", StringComparison.Ordinal)) return false; + try + { + using var doc = System.Text.Json.JsonDocument.Parse(content); + if (!doc.RootElement.TryGetProperty("Parameter", out var ps) + || ps.ValueKind != System.Text.Json.JsonValueKind.Array) + return false; + + foreach (var p in ps.EnumerateArray()) + { + if (p.ValueKind != System.Text.Json.JsonValueKind.Object) continue; + if (!p.TryGetProperty("Name", out var n) || n.ValueKind != System.Text.Json.JsonValueKind.String) continue; + if ((n.GetString() ?? "").StartsWith("q_", StringComparison.Ordinal)) return true; + } + return false; + } + catch + { + return false; + } + } + + /// + /// 判断模板是否为占位模板:JSON 格式、Title 为空、且没有声明任何 Parameter。 + /// GR7 设计器保存的 _WR 压缩模板无法判断,一律视为正常模板(不覆盖)。 + /// + private static bool IsPlaceholderReportTemplate(string content) + { + if (string.IsNullOrWhiteSpace(content) || content.StartsWith("_WR", StringComparison.Ordinal)) return false; + try + { + using var doc = System.Text.Json.JsonDocument.Parse(content); + var root = doc.RootElement; + var title = root.TryGetProperty("Title", out var t) && t.ValueKind == System.Text.Json.JsonValueKind.String + ? t.GetString() ?? "" + : ""; + var hasParamDecl = root.TryGetProperty("Parameter", out var p) + && p.ValueKind == System.Text.Json.JsonValueKind.Array + && p.GetArrayLength() > 0; + return string.IsNullOrWhiteSpace(title) && !hasParamDecl; + } + catch + { + return false; + } + } + + /// 从嵌入资源读取报表 .grf 模板 JSON 文本(.grf 为 UTF-8 JSON,GR7 向下兼容 GR6 格式) + private static string? LoadEmbeddedTemplateJson(string fileName) + { + try + { + var asm = typeof(DbSeeder).Assembly; + var resName = asm.GetManifestResourceNames() + .FirstOrDefault(n => n.EndsWith("." + fileName, StringComparison.OrdinalIgnoreCase)); + if (string.IsNullOrEmpty(resName)) return null; + + using var stream = asm.GetManifestResourceStream(resName); + if (stream == null) return null; + using var reader = new StreamReader(stream, new UTF8Encoding(false)); + var json = reader.ReadToEnd(); + + // 统一去 BOM 后保存(ReportFile.DesignContent 存纯文本,输出 .grf 时再补 BOM) + return json.Length > 0 && json[0] == '\uFEFF' ? json.Substring(1) : json; + } + catch (Exception ex) + { + Console.WriteLine($"[DbSeeder] 读取嵌入模板失败:{ex.Message}"); + return null; + } + } +} \ No newline at end of file diff --git a/backend/F9WebAiTest.Infrastructure/F9WebAiTest.Infrastructure.csproj b/backend/F9WebAiTest.Infrastructure/F9WebAiTest.Infrastructure.csproj index af0cb94..7481365 100644 --- a/backend/F9WebAiTest.Infrastructure/F9WebAiTest.Infrastructure.csproj +++ b/backend/F9WebAiTest.Infrastructure/F9WebAiTest.Infrastructure.csproj @@ -15,6 +15,8 @@ + + diff --git a/backend/F9WebAiTest.Infrastructure/FreeSqls/Repositories/SYS/Departments/DepartmentWriteRepository.cs b/backend/F9WebAiTest.Infrastructure/FreeSqls/Repositories/SYS/Departments/DepartmentWriteRepository.cs index 2e5d205..06392c3 100644 --- a/backend/F9WebAiTest.Infrastructure/FreeSqls/Repositories/SYS/Departments/DepartmentWriteRepository.cs +++ b/backend/F9WebAiTest.Infrastructure/FreeSqls/Repositories/SYS/Departments/DepartmentWriteRepository.cs @@ -20,7 +20,11 @@ internal class DepartmentWriteRepository : F9RepositoryBase, IDepartmentWriteRep public async Task UpdateAsync(Department dep) { dep.UpdatedAt = DateTime.Now; - return await Orm.Update().SetSource(dep).ExecuteAffrowsAsync() > 0; + // 编辑只覆盖业务字段:启用/禁用有独立命令,不能被 SetSource 连带重置;创建信息与删除标记同理 + return await Orm.Update() + .SetSource(dep) + .IgnoreColumns(x => new { x.IsActive, x.IsDeleted, x.CreatedAt, x.CreatedBy }) + .ExecuteAffrowsAsync() > 0; } public async Task SoftDeleteAsync(long id) diff --git a/start.bat b/start.bat index 76c5273..d614d31 100644 --- a/start.bat +++ b/start.bat @@ -5,11 +5,16 @@ echo 启动 f9-web-ai-test(后端 + 前端) echo ============================================ echo. +set "ROOT=%~dp0" +rem 若 npm 不在 PATH,尝试使用本机已有的 node 目录(按需修改这一行) +set "NODE_DIR=C:\Users\32453\.workbuddy\binaries\node\versions\22.22.2-6" +if exist "%NODE_DIR%\npm.cmd" set "PATH=%NODE_DIR%;%PATH%" + echo [1/2] 启动后端 (.NET 8, 端口 3000)... -start "F9WebAiTest-Backend" cmd /k "cd /d C:\Users\32453\CodeBuddy\20260825142915\backend\F9WebAiTest.Api && dotnet run" +start "F9WebAiTest-Backend" cmd /k "cd /d %ROOT%backend\F9WebAiTest.Api && dotnet run --no-launch-profile" echo [2/2] 启动前端 (Vue3, 端口 5173)... -start "F9WebAiTest-Frontend" cmd /k "cd /d C:\Users\32453\CodeBuddy\20260825142915\web && npm run dev" +start "F9WebAiTest-Frontend" cmd /k "cd /d %ROOT%web && npm run dev" echo. echo 启动完成! diff --git a/web/public/grlib/grhtml5-7-min.js b/web/public/grlib/grhtml5-7-min.js new file mode 100644 index 0000000..8e7c5ed --- /dev/null +++ b/web/public/grlib/grhtml5-7-min.js @@ -0,0 +1,9 @@ +var gr={},DEBUG=0;!function(){"use strict";gr.const_={VERSION:"7.0.26.0930",ID_REPORT_STYLE:"_gridcss",ID_DETAILGRID:"_griddg",ATTR_CONTENT_RECNO:"_grRecNo",ATTR_GROUPH_INDEX:"_grGHIndex",ATTR_GROUPF_INDEX:"_grGFIndex",ATTR_GROUP_RECNO:"_grGRecNo",ATTR_FIELD:"_grfld",CSS_DG:"_grdg",CSS_DG_F:"._grdg",CSS_SH:"_grcsssh",CSS_CB:"_grcsscb",BIG_AMT_NUMBER:"零壹贰叁肆伍陆柒捌玖",HZ_NUMBER:"零一二三四五六七八九十",HZ_NUMRANK:"元十百千万十百千亿十百千",HZ_NEGATIVE:"负",HZ_ZERO_YUAN:"零元整",HZ_AMT_STEP:"元拾佰仟万拾佰仟亿拾佰仟",HZ_AMT_UNIT:"整角分",HZ_DATETIME_UNIT:"周日星期天",MILLISECONDS_DAY:864e5,TIMEZONE_OFFSET:6e4*(new Date).getTimezoneOffset()},gr.enum_={ColorIdent:{Black:0,Maroon:128,Green:32768,Olive:32896,Navy:8388608,Purple:8388736,Teal:8421376,Gray:10526880,Silver:12632256,Red:255,Lime:65280,Yellow:65535,Blue:16711680,Fuchsia:16711935,Aqua:16776960,LtGray:12698049,DkGray:8421504,White:16777215},PenStyle:{Solid:0,Dash:1,Dot:2,DashDot:3,DashDotDot:4},BorderStyle:{DrawLeft:1,DrawTop:2,DrawRight:4,DrawBottom:8},BackStyle:{Normal:1,Transparent:2},TextAlign:{TopLeft:17,TopCenter:18,TopRight:20,TopJustiy:144,BottomLeft:65,BottomCenter:66,BottomRight:68,BottomJustiy:192,MiddleLeft:33,MiddleCenter:34,MiddleRight:36,MiddleJustiy:160},TextOrientation:{Default:0,U2DL2R0:5,D2UL2R1:22,U2DR2L0:9,U2DR2L1:25,Invert:58,L2RD2U0:38,L2RD2U1:54},ParameterDataType:{String:1,Integer:2,Float:3,Boolean:5,DateTime:6},FieldType:{String:1,Integer:2,Float:3,Currency:4,Boolean:5,DateTime:6,Binary:7},ControlType:{StaticBox:1,ShapeBox:2,SystemVarBox:3,FieldBox:4,SummaryBox:5,RichTextBox:6,PictureBox:7,MemoBox:8,SubReport:9,Line:10,Chart:11,Barcode:12,FreeGrid:13,CharGridBox:14},SummaryFun:{Sum:1,Avg:2,Count:3,Min:4,Max:5,Var:6,VarP:7,StdDev:8,StdDevP:9,AveDev:10,DevSq:11,CountBlank:12,CountA:13,Distinct:14,AvgA:15,MinN:16,MaxN:17,StrMin:18,StrMax:19,VarA:20,VarPA:21,StdDevA:22,StdDevPA:23,AveDevA:24,DevSqA:25,SumAbs:26,SumAcc:27,GroupSumAcc:28,Join:29,JoinUnique:30},MathFun:{round45:1,round465:2,abs:5,acos:6,asin:7,atan:8,atan2:9,ceil:15,cos:16,exp:17,floor:18,log:19,maxArray:20,minArray:21,pow:22,round:23,sin:24,sqrt:25,tan:26},SystemVarType:{CurrentDateTime:1,RecordNo:4,RowNo:8,RecordCount:19,GroupNo:20,GroupCount:21,GroupRowNo:22,GroupRowCount:23},AnchorStyle:{Left:1,Top:2,Right:4,Bottom:8},AlignColumnSideStyle:{Left:1,Right:2,Both:3},DockStyle:{None:0,Left:1,Top:2,Right:3,Bottom:4,Fill:5},CenterStyle:{None:0,Horizontal:1,Vertical:2,Both:3},ShiftMode:{Never:0,Always:1,WhenOverLapped:2},Unit:{Cm:1,Inch:2},ShapeBoxType:{Circle:1,Ellipse:2,Rectangle:3,RoundRect:4,RoundSquare:5,Square:6,Line:7},PictureAlignment:{TopLeft:1,TopRight:2,Center:3,BottomLeft:4,BottomRight:5},PictureSizeMode:{Clip:1,Stretch:2,Zoom:3,EnsureFullView:4,Tile:5},PictureTransparentMode:{None:0,Overlying:1,Background:2},PictureRotateMode:{None:0,Left:1,Right:2,Flip:3,Mirror:4},ChartType:{BarChart:1,PieChart:2,LineChart:3,StackedBarChart:4,XYScatterChart:5,XYLineChart:6,CurveLineChart:7,XYCurveLineChart:8,Bubble:9,StackedBar100Chart:10,ColumnChart:11,StackedColumnChart:12,StackedColumn100Chart:13},ChartVarType:{XVal:1,YVal:2,ZVal:3,YVal100ByGroup:4,YVal100BySeries:5,YValTotalByGroup:6,YValTotalBySeries:7,SeriesLabel:8,GroupLabel:9},PointMarkerStyle:{None:-1,Square:0,SquareCheck:1,SquareCross:2,Circle:3,CirclePoint:4,CircleCross:5,Dimond:6,Triangle:7,Cross:8,Cross4:9,Star4:10,Star5:11,Star6:12,Star10:13},BarcodeType:{Code25Intlv:1,Code25Ind:2,Code25Matrix:3,Code39:4,Code39X:5,Code128A:6,Code128B:7,Code128C:8,Code93:9,Code93X:10,CodeMSI:11,CodePostNet:12,CodeCodabar:13,CodeEAN8:14,CodeEAN13:15,CodeUPC_A:16,CodeUPC_E0:17,CodeUPC_E1:18,CodeUPC_Supp2:19,CodeUPC_Supp5:20,CodeEAN128A:21,CodeEAN128B:22,CodeEAN128C:23,Code128Auto:24,CodeEAN128Auto:25,ITF_14:26,Pharmacode1T:27,Pharmacode2T:28,PDF417:50,QRCode:51,DataMatrix:52,GS1DataMatrix:53,GS1QRCode:54},BarcodeCaptionPosition:{None:1,Above:2,Below:3},BarcodeDirection:{LeftToRight:1,RightToLeft:2,TopToBottom:3,BottomToTop:4},DtmxEncoding:{Auto:2,Ascii:3,C40:4,Text:5,X12:6,Edifact:7,Base256:8},DtmxMatrixSize:{Auto:2,"10x10":4,"12x12":5,"14x14":6,"16x16":7,"18x18":8,"20x20":9,"22x22":10,"24x24":11,"26x26":12,"32x32":13,"36x36":14,"40x40":15,"44x44":16,"48x48":17,"52x52":18,"64x64":19,"72x72":20,"80x80":21,"88x88":22,"96x96":23,"104x104":24,"120x120":25,"132x132":26,"144x144":27,"8x18":28,"8x32":29,"12x26":30,"12x36":31,"16x36":32,"16x48":33},StringAlignment:{Near:1,Center:2,Far:3},PeriodType:{None:0,Day:1,Week:2,ThirdMonth:3,HalfMonth:4,Month:5,Quarter:6,HalfYear:7,Year:8,Calendar:9},OCGroupHeaderVAlign:{Top:1,Bottom:2,Middle:3},controlLayout:{auto:0,table:1,absolute:2},detailgridResize:{asDesign:0,fitWidth:1,onlyGrow:2}},gr.locale={supportIntl:!!window.Intl,decimalSep:".",thousandSep:",",dateSep:"-",timeSep:":",textAM:"上午",textPM:"下午",currencySymbol:"¥"},gr.script={names:{Global:"GlobalScript",Initialize:"InitializeScript",FetchRecord:"FetchRecordScript",BeforePostRecord:"BeforePostRecordScript",BeforeSort:"BeforeSortScript",ProcessBegin:"ProcessBeginScript",ProcessEnd:"ProcessEndScript",ProcessRecord:"ProcessRecordScript",GroupBegin:"GroupBeginScript",GroupEnd:"GroupEndScript",Format:"FormatScript",FieldGetDisplayText:"FieldGetDisplayTextScript",TextBoxGetDisplayText:"TextBoxGetDisplayTextScript",CustomDraw:"CustomDrawScript"},calcExp:function(expText){var result=NaN;expText="result="+expText;try{eval(expText)}catch(e){result=NaN}return result},nameMember:function(e){return e+"_f"},execEvent:function(Report,Sender,scriptName){try{eval("Report._rsns."+Sender[gr.script.nameMember(scriptName)]+"(Sender);")}catch(e){Sender.report.root._scriptFailed||(alert("执行报表脚本 '"+scriptName+"' 失败"+(Sender.Name?"\n对象名称: "+Sender.Name:"")+"\n错误信息: "+e.toString()+"\n脚本代码:\n"+Sender[scriptName]),Sender.report.root._scriptFailed=1)}}}}(),function(e){"use strict";var t=gr.enum_,r=gr.const_,o=gr.helper={},n=gr.common={},i=r.MILLISECONDS_DAY,a=r.TIMEZONE_OFFSET,l=o.assignJSONMembers=function(t,r,o){var n,i,a;o===e&&(o=t.report.isWR),o&&(a=gr.wr.wrPropNameDecode);for(i in r)n=r[i],"object"!=typeof n&&(o&&(i=a(i)),t.hasOwnProperty(i)&&(t[i]=n))},s=o.enumMemberValid=function(e,t,r){"number"!=typeof e[t]&&(e[t]=x(r,e[t]))},c=o.enumBitMemberValid=function(e,t,r){"number"!=typeof e[t]&&(e[t]=S(r,e[t]))},u=o.colorMemberValid=function(e,r,o){var n=e[r];"number"==typeof n?4278190080&n||!o||(e[r]=n|o<<24):(n=t.ColorIdent.hasOwnProperty(n)?t.ColorIdent[n]:parseInt("0x"+n,16),4278190080&n||!o||(n|=o<<24),e[r]=n)},d=o.penStyleText=function(e){var r={Solid:"solid",Dash:"dashed",Dot:"dotted",DashDot:"dashed",DashDotDot:"dotted"};return g(e.Width)+" "+r[v(t.PenStyle,e.Style)]+" "+w(e.Color)},f=o.fontHeight=function(e){return 4*e.Point/3},p=(o.fontString=function(e,t,r){return t+" "+g(e)+" "+r},o.fontCSSText=function(e){var t="",r=[];return e.Italic&&r.push("italic"),e.Bold&&r.push("bold"),r.push(g(f(e)),e.Name),r.forEach(function(e){t&&(t+=" "),t+=e}),t}),h=(o.tagCSSChild=function(e){return 1==e?"first-child":"nth-child("+e+")"},o.tagCSSChildR=function(e){return 1==e?"last-child":"nth-last-child("+e+")"},o.intFixed=function(e,t){var r,o=e+"";if(t>0)if(r=t-o.length,0>r)o=o.substr(-r);else for(;r-- >0;)o="0"+o;return o},function(e){var t=e.toFixed(2),r=t.length-1;return"0"===t.charAt(r)&&("0"===t.charAt(r-1)&&(r-=2),t=t.substr(0,r)),t}),g=o.pixelsToHtml=function(e){return h(e)+"px"},m=(o.percentToHtml=function(e){return h(e)+"%"},o.cloneDate=function(e){return new Date(e)}),C=o.confirmDateValue=function(t){function o(){function o(){var e=t.indexOf(":"),o=e>0&&8>e,n=t.split(/[\:\-\/ ]/g),i=n.length,a=i>0?+n[0]:0,l=i>1?+n[1]:0,s=i>2?+n[2]:0,c=i>3?+n[3]:0,u=i>4?+n[4]:0,d=i>5?+n[5]:0;return isNaN(l)&&(e=n[1],l=r.HZ_NUMBER.indexOf(e.charAt(0))+1,e.length>=3&&(l+=r.HZ_NUMBER.indexOf(e.charAt(1))+1)),o&&(e=a,a=c,c=e,e=l,l=u,u=e,e=s,s=d,d=e),s>31&&(e=a,a=s,s=e,e=l,l=s,s=e),l>12&&(e=l,l=s,s=e),new Date(a,--l,s,c,u,d)}var n;return"string"==typeof t?(n=Date.parse(t),t=t?isNaN(n)?o():new Date(n):e):(n=new L,n.AsFloat=+t,t=n.value),t}return Date.prototype.isPrototypeOf(t)?t:L.prototype.isPrototypeOf(t)?t.value:o()},b=(o.confirmCloneDateValue=function(e){return Date.prototype.isPrototypeOf(e)?m(e):C(e)},o.strimDate=function(e){var t=e.getTime();e.setTime(t-(t-6e4*e.getTimezoneOffset())%i)},o.incDate=function(e){e.setTime(e.getTime()+i)},o.incDate2=function(e,t){e.setTime(e.getTime()+t*i)}),x=(o.periodRangeBy=function(e,t){function r(){11>s?++s:(++c,s=0)}var o,n=e.getDate(),i=e.getMonth(),a=e.getFullYear(),l=n,s=i,c=a;if(2>=t)o={begin:m(e),end:m(e)},b(o.end,1===t?1:7);else{switch(t){case 3:10>=n?(n=1,l=11):20>=n?(n=11,l=21):(n=21,r(),l=1);break;case 4:15>=n?(n=1,l=16):(n=16,r(),l=1);break;case 5:n=l=1,r();break;case 6:n=l=1,i-=i%3,s=i,r(),r(),r();break;case 7:n=l=1,i=6>i?0:6,s=6>i?6:0,s||c++;break;case 8:n=l=1,i=s=0,c++}o={begin:new Date(a,i,n),end:new Date(c,s,l)}}return o},o.periodRangeNext=function(e,t){function r(){11>s?++s:(++c,s=0)}var o,n=e.begin.getDate(),i=e.begin.getMonth(),a=e.begin.getFullYear(),l=n,s=i,c=a;if(2>=t)o={begin:m(e.end),end:m(e.end)},b(o.end,1===t?1:7);else{switch(t){case 3:10>=n?(n=11,l=21):20>=n?(n=21,l=1,r()):(n=1,l=11,r(),a=c,i=s);break;case 4:15>=n?(n=16,l=1,r()):(n=1,l=16,r(),a=c,i=s);break;case 5:r(),a=c,i=s,r();break;case 6:r(),r(),r(),a=c,i=s,n=l,r(),r(),r();break;case 7:i?(i=0,a++,s=6):(i=6,s=0),c++;break;case 8:a++,c++,c++}o={begin:new Date(a,i,n),end:new Date(c,s,l)}}return o},o.confirmBooleanValue=function(e){if(e.constructor===Boolean.prototype.constructor)return e;if("string"==typeof e){if(e.toLowerCase(),"TRUE"===e||"true"===e)return!0;e=parseInt(e)}return!!e},o.ensureNameText=function(e){return/^[A-Za-z_\u0100-\uffff][\w\u0100-\uffff]*$/.test(e)||(e="["+e+"]"),e},o.enumName2Value=function(t,r){var o=t[r];if(o===e){r=r.toUpperCase();for(var n in t)if(n.toUpperCase()===r)return t[n];o=-1}return o}),v=o.enumValue2Name=function(e,t){for(var r in e)if(e[r]===t)return r;return""},S=function(e,t){var r,o,n,i=0;for(t=t.substring(1,t.length-1),r=t.split("|"),n=r.length,o=0;n>o;o++)i+=e[r[o]]||0;return i},T=o.rgba2color=function(e,t,r,o){return y(e|t<<8|r<<16,o)},y=o.colorAlpha=function(e,t){return e|t<<24},F=o.color2rgba=function(e){return{r:255&e,g:e>>8&255,b:e>>16&255,a:e>>24&255}},w=o.colorValue2Html=function(e){function t(e){return(16>e?"0":"")+e.toString(16)}var r=255&e,o=e>>8&255,n=e>>16&255,i=e>>24&255;return"#"+t(r)+t(o)+t(n)+(i?t(255-i):"")},D=o.colorIsBlack=function(e){return 16777215&e},P=(o.decodeTextColor=function(e){var t,r,o,n,i,a=4;return"rgb("===e.substr(0,4)&&(t=e.indexOf(")",a),-1!==t&&(e=e.substring(4,t),r=e.split(","),3===r.length&&(o=parseInt(r[0]),n=parseInt(r[1]),i=parseInt(r[2]),o>=0&&255>=o&&n>=0&&255>=n&&i>=0&&255>=i)))?{color:T(o,n,i,255),index:t+1}:0},o.colorGradientLight=function(e){var t=F(e),r=120/255;return t.r+=(255-t.r)*r,t.g+=(255-t.g)*r,t.b+=(255-t.b)*r,t.r>255&&(t.r=255),t.g>255&&(t.g=255),t.b>255&&(t.b=255),T(t.r,t.g,t.b,t.a)},o.colorGradientDark=function(e){var t=F(e),r=30/255;return t.r-=t.r*r,t.g-=t.g*r,t.b-=t.b*r,t.r<0&&(t.r=0),t.g<0&&(t.g=0),t.b<0&&(t.b=0),T(t.r,t.g,t.b,t.a)},o.prototypeLinkExtend=function(e,t){function r(){}var o,n=e.prototype;r.prototype=t.prototype,o=new r;for(var i in n)o[i]=n[i];e.prototype=o,e.prototype.constructor=e},o.prototypeCopyExtend=function(e,t){var r=t.prototype,o=e.prototype;for(var n in r)o.hasOwnProperty(n)||(o[n]=r[n]);o.constructor=e},o.createArray=function(e,t){for(var r=[],o=0;o++=0;)o>r&&(e=t.substring(r,o),/\S/g.test(e)&&n.push(e)),r=o,o=t.indexOf(">",r+1),n.push(t.substring(r,++o)),r=o,o=t.indexOf("<",r);return n}var o;/\s/g.test(t)&&(t=r().join(""));try{o=(new DOMParser).parseFromString(t,"text/xml")}catch(n){o=e}return o},o.xmlToReportDataJSON=function(e){var t,r,o,n,i="",a={};for(o=e.childNodes[0].firstChild;o;){for(o.nodeName!==i&&(i=o.nodeName,r=[],a[o.nodeName]=r),t={},n=o.firstChild;n;)t[n.nodeName]=n.hasOwnProperty("text")?n.text:n.textContent,n=n.nextSibling;r.push(t),o=o.nextSibling}return a},o.getRelativePosition=function(e){var t,r,o=e.originalEvent||e,n=e.currentTarget||e.srcElement,i=n.getBoundingClientRect();return o.touches?(t=o.touches[0].clientX-i.left,r=o.touches[0].clientY-i.top):(t=o.clientX-i.left,r=o.clientY-i.top),{x:t,y:r}},o.addEvent=function(e,t,r){e.addEventListener?e.addEventListener(t,r):e.attachEvent?e.attachEvent("on"+t,r):e["on"+t]=r}),E=o.removeEvent=function(e,t,r){e.removeEventListener?e.removeEventListener(t,r,!1):e.detachEvent?e.detachEvent("on"+t,r):e["on"+t]=noop},G=(o.bindEvents=function(e,t,r){e.events||(e.events={}),t.forEach(function(t){e.events[t]=function(){r.apply(e,arguments)},B(e.canvas,t,e.events[t])})},o.unbindEvents=function(e,t){t.forEach(function(t,r){E(e.canvas,r,t)})},o.toRadians=function(e){return e*Math.PI/180}),L=(o.toDegree=function(e){return 180*e/Math.PI},n.DateTime=function(){this.value=new Date});n.DateTime.prototype={get Year(){return this.value.getFullYear()},get Month(){return this.value.getMonth()+1},get Day(){return this.value.getDate()},get Hour(){return this.value.getHours()},get Minute(){return this.value.getMinutes()},get Second(){return this.value.getSeconds()},get DayOfWeek(){return this.value.getDay()},get DayOfYear(){var e=this.value,t=e.getFullYear();return(new Date(t,e.getMonth(),e.getDate()).getTime()-new Date(t,0,1).getTime())/i+1},get AsFloat(){return(this.value.getTime()+new Date(1970,0,1).getTime()-new Date(1899,11,30).getTime()-a)/i},set AsFloat(e){this.value.setTime(e*i+new Date(1899,11,30).getTime()-new Date(1970,0,1).getTime()+a)},ValueFromDate:function(e,t,r){this.value=new Date(e,t-1,r)},ValueFromDateTime:function(e,t,r,o,n,i){this.value=new Date(e,t-1,r,o,n,i)},Format:function(e){return new gr.format.DateTimeFormatter(e).format(this.value)}};var N=n.Rect=function(e,t,r,o){var n=this;n.left=e,n.top=t,n.right=r,n.bottom=o};n.Rect.prototype={clone:function(){var e=this;return new n.Rect(e.left,e.top,e.right,e.bottom)},IsRectEmpty:function(){var e=this;return e.left>=e.right||e.top>=e.bottom},PtInRect:function(e,t){var r=this;return r.left<=e&&e0?o!==r&&(e.ctx.font=p(o)):e.ctx.font=""},fontPointTo:function(e){var t=this,r=t.usingFont.clone();r.Point=e,t.selectFont(r)},pushClipRect:function(e,t,r,o){var n=this,i=this.ctx,a=n.clips,l=a.length,s=new N(e,t,e+r,t+o);l&&s.IntersectRect(a[l-1]),a.push(s),i.beginPath(),i.rect(s.left,s.top,s.Width(),s.Height()),i.clip()},popClipRect:function(){var e=this,t=this.ctx,r=t.canvas,o=e.clips,n=o.length;rect,o.pop(rect),rect=n>1?o[n-2]:new N(0,0,r.width,r.height),t.beginPath(),t.rect(rect.left,rect.top,rect.Width(),rect.Height()),t.clip()},measureTextWidth:function(e){return this.ctx.measureText(e).width},measureTextHeight:function(e){return f(this.usingFont)},drawText:function(e,t,r){this.ctx.fillText(e,t,r)},drawTextCenter:function(e,t,r){var o=this.ctx;o.textAlign="center",o.fillText(e,t,r),o.textAlign="start"},drawTextAlign:function(e,t,r,o,n,i,a){var l,s=this,c=f(s.usingFont),u=2&i,d=4&i,p=32&i,h=64&i;p?r+=(n-c)/2:h&&(r+=n-c),(u||d)&&(l=s.measureTextWidth(e),(!a||o>l)&&(t+=u?(o-l)/2:o-l)),a?s.ctx.fillText(e,t,r,o):s.ctx.fillText(e,t,r)},drawTextAlign2:function(e,t,r,o){this.drawTextAlign(e,t.left,t.top,t.Width(),t.Height(),r,o)},drawFormatText:function(e,t,r,o,n,i){var a=this;0===i.TextOrientation?i.TextAngle?a.drawTextRotate(e,t,r,i.TextAngle):i.CharacterSpacing||i.LineSpacing||i.FirstCharIndent||i.ParagraphSpacing||1!==i.FontWidthRatio||128&i.TextAlign?a.drawTextAlign(e,t,r,o,n,i.TextAlign):a.drawTextAlign(e,t,r,o,n,i.TextAlign):a.drawOrientationText(e,t,r,o,n,i)},drawFormatText2:function(e,t,r){this.drawFormatText(e,t.left,t.top,t.Width(),t.Height(),r)},drawOrientationText:function(e,t,r,o,n,i){var a=this,l=a.ctx,s=0,c=e.length,u=f(a.usingFont),d=i.TextOrientation,p=32&d;if(p)a.drawTextAlign(e,t,r,o,n,i.TextAlign);else for(;c>s;)l.fillText(e[s++],t,r),r+=u},drawTextRotate:function(e,t,r,o){var n=this.ctx;n.translate(t,r),n.rotate(G(-o)),this.ctx.fillText(e,0,0),n.setTransform(1,0,0,1,0,0)},drawEqualSpaceText:function(e,t,r,o){for(var n,i,a=this,l=t,s=e.length,c=0,u=(r-t)/s;s>c;)n=t+u*(c+1),i=e.substr(c++,1),a.drawText(i,(n+l-a.measureTextWidth(i))/2,o),l=n},beginPath:function(){var e=this;e.ctx.beginPath(),e.inpath=1},closePath:function(){this.ctx.closePath()},stroke:function(){var e=this;e.ctx.stroke(),e.inpath=0},fill:function(){var e=this;e.ctx.fill(),e.inpath=0},strokefill:function(){var e=this;e.ctx.stroke(),e.ctx.fill(),e.inpath=0},fillRect:function(e,t,r,o,n){var i=this;i.selectFillColor(n),i.ctx.fillRect(e,t,r,o),i.restoreFillStyle(n)},fillRect2:function(e,t){this.fillRect(e.left,e.top,e.Width(),e.Height(),t)},moveTo:function(e,t){var r=this,o=r.ctx;!r.inpath&&o.beginPath(),o.moveTo(e,t)},lineTo:function(e,t){var r=this,o=r.ctx;o.lineTo(e,t),!r.inpath&&o.stroke()},drawLine:function(e,t,r,o){var n=this.ctx;n.beginPath(),n.moveTo(e,t),n.lineTo(r,o),n.stroke()},drawPolyLine:function(e,t,r){var o=this.ctx,n=0,i=e.length;for(o.beginPath(),o.moveTo(e[n++],e[n++]);i>n;)o.lineTo(e[n++],e[n++]);r&&o.closePath(),o.stroke(),t&&o.fill()},bezierCurveTo:function(e,t,r,o,n,i){this.ctx.bezierCurveTo(e,t,r,o,n,i)},polyBezier:function(e){var t,r=this.ctx,o=e.length;for(r.beginPath(),r.moveTo(e[0].x,e[0].y),t=1;o>t;t++)r.bezierCurveTo(e[t].x,e[t++].y,e[t].x,e[t++].y,e[t].x,e[t].y);r.stroke()},circle:function(e,t,r,o,n){var i=this.ctx,a=(o>r?r:o)/2;e+=r/2,t+=o/2,i.beginPath(),i.arc(e,t,a,0,2*Math.PI),i.stroke(),n&&i.fill()},ellipse:function(e,t,r,o,n){var i=this.ctx,a=r>o?r:o,l=r/a,s=o/a;e+=r/2,t+=o/2,a/=2,i.beginPath(),i.save(),i.scale(l,s),i.arc(e/l,t/s,a,0,2*Math.PI),i.restore(),i.stroke(),n&&i.fill()},rectangle:function(e,t,r,o,n){var i=this;i.ctx.strokeRect(e,t,r,o),n&&i.ctx.fillRect(e,t,r,o)},rectangle2:function(e,t){this.rectangle(e.left,e.top,e.Width(),e.Height(),t)},square:function(e,t,r,o,n){var i=o>r?r:o;e+=(r-i)/2,t+=(o-i)/2,this.rectangle(e,t,i,i,n)},roundRectangle:function(e,t,r,o,n,i,a){var l=this.ctx;l.beginPath(),l.moveTo(e+n,t),l.lineTo(e+r-n,t),l.quadraticCurveTo(e+r,t,e+r,t+i),l.lineTo(e+r,t+o-i),l.quadraticCurveTo(e+r,t+o,e+r-n,t+o),l.lineTo(e+n,t+o),l.quadraticCurveTo(e,t+o,e,t+o-i),l.lineTo(e,t+i),l.quadraticCurveTo(e,t,e+n,t),l.stroke(),a&&l.fill()},roundSquare:function(e,t,r,o,n,i,a){var l=o>r?r:o;e+=(r-l)/2,t+=(o-l)/2,this.roundRectangle(e,t,l,l,n,i,a)},arc:function(e,t,r,o,n){var i=this,a=i.ctx,l=!i.inpath;l&&a.beginPath(),a.arc(e,t,r,-G(o),-G(n),n>=o),l&&a.stroke()},ellipseArc:function(e,t,r,o,n,i){var a=this,l=a.ctx,s=!a.inpath,c=l.ellipse,u=r/2,d=o/2,f=e+u,p=t+d,h=i>=n,n=-G(n),i=-G(i);s&&l.beginPath(),c?l.ellipse(f,p,u,d,0,n,i,h):l.arc(f,p,Math.min(u,d),n,i,h),s&&l.stroke()},pie:function(e,t,r,o,n,i){var a=this,l=a.ctx;o=G(o),n=G(n),l.beginPath(),l.moveTo(e,t),l.arc(e,t,r,-o,-n,1),l.closePath(),l.stroke(),i&&l.fill()},ellipsePie:function(e,t,r,o,n,i,a){var l,s,c=this.ctx,u=c.ellipse,d=r/2,f=o/2,p=e+d,h=t+f,g=i>=n;n=G(n),i=G(i),u||(d=f=Math.min(d,f)),l=p+Math.cos(n)*d,s=h-Math.sin(n)*f,c.beginPath(),c.moveTo(p,h),c.lineTo(l,s),u?c.ellipse(p,h,d,f,0,-n,-i,g):c.arc(p,h,d,-n,-i,g),c.closePath(),c.stroke(),a&&c.fill()},drawBar:function(e,t,r,o){var n,i=this,a=i.ctx,l=[{x:e.left,y:e.bottom},{x:e.left,y:e.top},{x:e.right,y:e.top},{x:e.right,y:e.bottom}];i.selectFillColor(r),i.selectPen(t),a.fillRect(e.left,e.top,e.Width(),e.Height()),a.beginPath(),n=l[o],a.moveTo(n.x,n.y),n=l[++o%4],a.lineTo(n.x,n.y),n=l[++o%4],a.lineTo(n.x,n.y),n=l[++o%4],a.lineTo(n.x,n.y),a.stroke(),i.restorePen(),i.restoreFillStyle()},DrawHorzLine:function(e,t,r){this.drawLine(t,e,r,e)},DrawVertLine:function(e,t,r){this.drawLine(e,t,e,r)},DrawPointMarker:function(t,r,o,n){function i(){var e,o=10===r?4:11===r?5:12===r?6:10,n=2*Math.PI/o,i=n/2,a=t.Width()/2,l=a/2,s=t.left+a,u=t.top+a,d=Math.PI/2,f=[];for(e=0;o>e;e++)f.push(s+Math.cos(d)*a,u-Math.sin(d)*a),d+=i,f.push(s+Math.cos(d)*l,u-Math.sin(d)*l),d+=i;c.drawPolyLine(f,1,1)}function a(){var e,i=o&&u;i&&(e=o.clone(),e.Color=n,c.selectPen(e)),c.drawLine(t.left,t.top,t.right,t.bottom),c.drawLine(t.left,t.bottom,t.right,t.top),9===r&&(l=(t.left+t.right)/2,c.drawLine(l,t.top,l,t.bottom),s=(t.top+t.bottom)/2,c.drawLine(t.left,s,t.right,s)),i&&c.restorePen()}var l,s,c=this,u=n!==e,d=t.Width(),f=Math.min(4,(d-4)/2);switch(u&&c.selectFillColor(n),o&&c.selectPen(o),r){case 3:case 4:case 5:c.ellipse(t.left,t.top,t.Width(),t.Height(),1),4===r?c.circle((t.left+t.right)/2-1,(t.top+t.bottom)/2-1,2,2,0):5===r&&(d=Math.min(4,d/3),c.drawLine(t.left+d,t.top+d,t.right-d,t.bottom-d),c.drawLine(t.right-d,t.top+d,t.left+d,t.bottom-d));break;case 0:case 1:case 2:c.rectangle(t.left,t.top,t.Width(),t.Height(),1),1===r?c.drawPolyLine([t.left+f,t.top+d/2,t.left+d/2,t.bottom-f,t.right-f,t.top+f]):2===r&&(c.drawLine(t.left+f,t.top+f,t.right-f,t.bottom-f),c.drawLine(t.right-f,t.top+f,t.left+f,t.bottom-f));break;case 8:case 9:a();break;case 6:c.drawPolyLine([(t.left+t.right)/2,t.top,t.left,(t.top+t.bottom)/2,(t.left+t.right)/2,t.bottom,t.right,(t.top+t.bottom)/2],1,1);break;case 7:c.drawPolyLine([(t.left+t.right)/2,t.top,t.left,t.bottom,t.right,t.bottom],1,1);break;case 10:case 11:case 12:case 13:i()}o&&c.restorePen(),u&&c.restoreFillStyle()},_applyPen:function(){var e=this,t=e.ctx,r=e.pens,o=r[r.length-1];t.lineWidth=o.Width,t.strokeStyle=w(o.Color),t.setLineDash(o.getDashStyle())}},n.Graphics=function(e){var t=this;t.report=e,t.ByUnit=0},n.Graphics.prototype={toPixels:function(e){var t=this;return t.ByUnit?t.report.UnitToPixels(e):e},pixelsTo:function(e){var t=this;return t.ByUnit?t.report.PixelsToUnit(e):e},get Left(){return 0},get Top(){return 0},get Width(){var e=this;return e.pixelsTo(e.ctx.width)},get Height(){var e=this;return e.pixelsTo(e.ctx.height)},attach:function(e){this.ctx=e},MoveTo:function(e,t){var r=this;r.ctx.moveTo(r.toPixels(e),r.toPixels(t))},LineTo:function(e,t){var r=this;r.ctx.lineTo(r.toPixels(e),r.toPixels(t))},FillRect:function(e,t,r,o,n){var i=this;i.ctx.fillRect(i.toPixels(e),i.toPixels(t),i.toPixels(r),i.toPixels(o),n)},Rectangle:function(e,t,r,o,n){var i=this;i.ctx.rectangle(i.toPixels(e),i.toPixels(t),i.toPixels(r),i.toPixels(o),n)},RoundRect:function(e,t,r,o,n,i,a){var l=this;l.ctx.roundRectangle(l.toPixels(e),l.toPixels(t),l.toPixels(r),l.toPixels(o),l.toPixels(n),l.toPixels(i),a)},Ellipse:function(e,t,r,o,n){var i=this;i.ctx.ellipse(i.toPixels(e),i.toPixels(t),i.toPixels(r),i.toPixels(o),n)},Pie:function(e,t,r,o,n,i){var a=this;a.ctx.pie(a.toPixels(e),a.toPixels(t),a.toPixels(r),o,n,i)},Arc:function(e,t,r,o,n){var i=this;i.ctx.arc(i.toPixels(e),i.toPixels(t),i.toPixels(r),o,n)},EllipseArc:function(e,t,r,o,n,i){var a=this;a.ctx.ellipseArc(a.toPixels(e),a.toPixels(t),a.toPixels(r),a.toPixels(o),n,i)},EllipsePie:function(e,t,r,o,n,i,a){var l=this;l.ctx.ellipsePie(l.toPixels(e),l.toPixels(t),l.toPixels(r),l.toPixels(o),n,i,a)},DrawLabelText:function(e,t,r){var o=this;o.ctx.drawText(e,o.toPixels(t),o.toPixels(r))},DrawRotateText:function(e,t,r,o){var n=this;n.ctx.drawTextRotate(e,n.toPixels(t),n.toPixels(r),o)},DrawText:function(e,t,r,o,n,i,a){var l=this;l.ctx.drawTextAlign(e,l.toPixels(t),l.toPixels(r),l.toPixels(o),l.toPixels(n),i)},DrawFormatText:function(e,t,r,o,n,i){var a=this;a.ctx.drawFormatText(e,a.toPixels(t),a.toPixels(r),a.toPixels(o),a.toPixels(n),i)},DrawFormatTextShrinkToFit:function(e,t,r,o,n,i){var a=this;a.ctx.drawTextAlign(e,a.toPixels(t),a.toPixels(r),a.toPixels(o),a.toPixels(n),i.TextAlign,1)},CalcLabelTextWidth:function(e){var t=this;return t.pixelsTo(t.ctx.measureTextWidth(e))},CalcLabelTextHeight:function(e){var t=this;return t.pixelsTo(t.ctx.measureTextHeight(e))},CalcTextOutLen:function(e,t,r,o){return r.length},CalcDrawFormatTextHeight:function(e,t,r){var o=this;return o.pixelsTo(o.ctx.measureTextHeight(e))},CalcDrawFormatTextWidth:function(e,t){var r=this;return r.pixelsTo(r.ctx.measureTextWidth(e))},CalcDrawFormatTextOutLen:function(e,t,r,o){return e.length},DrawPicture:function(e,t,r,o,n,i,a,l){},SelectFont:function(e){this.ctx.selectFont(e.font)},RestoreFont:function(){this.ctx.restoreFont()},FontPointChangeTo:function(e){this.ctx.fontSizeTo(e)},FontSizeRestore:function(){this.ctx.restoreFont()},SelectTextColor:function(e){this.ctx.selectFillColor(e)},RestoreTextColor:function(){this.ctx.restoreFillStyle()},SelectPen:function(e,t,r){this.ctx.selectPen2(e,t,r)},RestorePen:function(){this.ctx.restorePen()},SelectFillColor:function(e){this.ctx.selectFillColor(e)},RestoreFillColor:function(){this.ctx.restoreFillStyle()},PushClipRect:function(e,t,r,o){var n=this;n.ctx.pushClipRect(n.toPixels(e),n.toPixels(t),n.toPixels(r),n.toPixels(o))},PopClipRect:function(){this.ctx.popClipRect()},CurveTo:function(e,t,r,o,n,i){var a=this;a.ctx.bezierCurveTo(a.toPixels(e),a.toPixels(t),a.toPixels(r),a.toPixels(o),a.toPixels(n),a.toPixels(i))},CalcCurveControlPoints:function(e,t,r,o,n,i){var a=this,l=.4,s=Math.sqrt(Math.pow(r-e,2)+Math.pow(o-t,2)),c=Math.sqrt(Math.pow(n-r,2)+Math.pow(i-o,2)),u=s+c,d=1,f=1; +u>0&&(d=l*s/u,f=l*c/u),a.CCPInnerX=r-d*(n-e),a.CCPInnerY=o-d*(i-t),a.CCPOuterX=r+f*(n-e),a.CCPOuterY=o+f*(i-t)},DrawPointMarker:function(e,t,r,o){var n=this;t=n.toPixels(t),r=n.toPixels(r),o=n.toPixels(o/2),n.ctx.DrawPointMarker(new N(t-o,r-o,t+o,r+o),e)},BeginPath:function(){this.ctx.beginPath()},ClosePath:function(){this.ctx.closePath()},EndPath:function(){},StrokePath:function(){this.stroke()},FillPath:function(){this.fill()},StrokeAndFillPath:function(){this.strokefill()}},n.HtmlStyles=function(){this.items=[]},n.HtmlStyles.prototype={add:function(e,t){this.items.push({name:e,value:t})},addBackColor:function(e){this.add("background-color",w(e))},addCellBorder:function(e){var t=this;e.ShowRowLine&&t.add("border-bottom",d(e.ColLinePen)),e.ShowColLine&&t.add("border-right",d(e.RowLinePen))},addObjectPadding:function(e){var t=e.padding,r="";return t.Right===t.Left&&t.Top===t.Bottom?(t.Right!==t.Top&&(r=g(t.Top)+" "),r+=g(t.Right)):r=g(t.Top)+" "+g(t.Right)+" "+g(t.Bottom)+" "+g(t.Left),this.add("padding",r)},addBorder:function(e){var t,r=this;e.Styles&&(t=d(e.Pen),15===e.Styles?r.add("border",t):(1&e.Styles&&r.add("border-left",t),4&e.Styles&&r.add("border-right",t),2&e.Styles&&r.add("border-top",t),8&e.Styles&&r.add("border-bottom",t))),e.Shadow&&(t=g(e.ShadowWidth)+" ",r.add("box-shadow",t+t+w(e.ShadowColor)))},addTextFormat:function(e,t){var r,o=this,n=e.TextFormat,i=n.LineSpacing,a="text-align",l=n.WordWrap;1&n.TextAlign?o.add(a,"left"):2&n.TextAlign?o.add(a,"center"):4&n.TextAlign?o.add(a,"right"):o.add(a,"justify"),t&&(a="vertical-align",16&n.TextAlign?o.add(a,"top"):32&n.TextAlign?o.add(a,"middle"):o.add(a,"bottom")),n.EndEllipsis&&(o.add("text-overflow","ellipsis"),l=!1),l?o.add("white-space","normal"):(o.add("overflow","hidden"),o.add("white-space","nowrap")),n.CharacterSpacing&&o.add("letter-spacing",g(n.CharacterSpacing)),i&&(r=f(e.obj.getUsingFont()),o.add("line-height",h((r+i)/r))),n.FirstCharIndent&&o.add("text-indent",g(n.FirstCharIndent)),D(n.ForeColor)&&o.add("color",w(n.ForeColor))},getText:function(){var e="";return this.items.forEach(function(t){e+=t.name+":"+t.value+";"}),e},clear:function(){this.items=[]}};var H=n.HtmlStyles;n.HtmlElement=function(e,t){var r=this;r.tag=e,r.classes=[],t.addChild(this)},n.HtmlElement.prototype={get styles(){var e=this;return e._styles||(e._styles=new H),e._styles},addClass:function(e){this.classes.push(e)},addStyle:function(e,t){this.styles.add(e,t)},addBackColorStyle:function(e){this.styles.addBackColor(e)},addAttribute:function(e,t){var r=this;r._attributes||(r._attributes=[]),r._attributes.push({name:e,value:t})},addChild:function(e){var t=this;t.children||(t.children=[]),t.children.push(e)},beginText:function(){var t=this,r="<"+t.tag;return t.classes.length>0&&(r+=' class="',t.classes.forEach(function(e,t){t>0&&(r+=" "),r+=e}),r+='"'),t._styles&&(r+=' style="'+t._styles.getText()+'"'),t._attributes&&t._attributes.forEach(function(t){var o=t.value;r+=" "+t.name,o!==e&&(r+='="'+o+'"')}),t.skipend&&(r+="/"),r+=">"},endText:function(){var e=this;return e.skipend?"":""}}}(),function(e){"use strict";gr.wr={WRZipSource:['":"','","','"},{"','":{"','},{"','":',',"',"false"],WRZipDest:"!$%&@^`~",WRAsciiMap_R:"r<~VH6SAZ?yiO %1#Ek>]`+|F_t/$RgQnX=YbqL)7uU9,oaB5}-'ev4;JD.2WsP@3zhl\"cfC^(K0mx!&Td{*GM:[pIj8\\Nw",propNameMap:{AlternatingBackColor:"G",BackColor:"Q",BarcodeType:"b",BorderCustom:"i",CharSpacing:"x",DBFieldName:"WA",FreeCell:"AB",GroupLabel:"OB",GroupTitle:"PB",Height:"ZB",IsCrossTab:"jB",LeftMargin:"tB",LnSpacing:"JC",Name:"YC",Oriention:"eC",RightMargin:"qD",SeriesLabel:"HE",Type:"LF",Value:"UF",Width:"eF"},propNameMap_R:{UG:"AbortOnError",A:"AdjustRowHeight",B:"AlignColumn",C:"AlignColumnEx",D:"AlignColumnSide",E:"AlignToGrid",F:"Alignment",VG:"AlternateFonts",G:"AlternatingBackColor",H:"AlwaysShowHScrollBar",I:"AlwaysShowVScrollBar",J:"Anchor",K:"AppendBlankCol",L:"AppendBlankColWidth",M:"AppendBlankRow",N:"AppendBlankRowAtLast",O:"AppendBlankRowExclude",P:"Author",Q:"BackColor",R:"BackImage",S:"BackImageFile",T:"BackImagePreview",U:"BackImagePrint",V:"BackStyle",W:"BackgroundColor",WG:"Bar3DAxisDepth",XG:"BarDPI",X:"BarRatio",Y:"BarSingleSeriesColor",Z:"BarWidth",a:"BarWidthPercent",b:"BarcodeType",YG:"BeforeHeaders",c:"BeforePostRecordScript",d:"BeforeSortScript",e:"BeginDateParameter",f:"Bold",ZG:"BookmarkLevel",g:"BookmarkText",h:"BorderColor",i:"BorderCustom",j:"BorderPrintType",k:"BorderStyles",l:"BorderWidth",m:"BottomMargin",n:"BubbleScalePerCm",o:"ByFields",p:"ByY2Axis",q:"CanGrow",r:"CanShrink",s:"CaptionAlignment",t:"CaptionPosition",u:"Center",v:"CenterView",w:"CenterWithDetailGrid",x:"CharSpacing",y:"CharacterSpacing",z:"Charset",AA:"Chart3D",aG:"Chart3DViewAngle",BA:"ChartType",CA:"CheckSum",bG:"ClassPos",DA:"CodePage",EA:"ColLineColor",FA:"ColLineWidth",GA:"ColSpan",HA:"Color",IA:"Column",JA:"ColumnCount",KA:"ColumnMove",LA:"ColumnResize",MA:"ConditionScript",NA:"ConnectionString",OA:"ContinuePrint",PA:"CoordLineColor",QA:"CoordLineVisible",RA:"CornerDx",SA:"CornerDy",TA:"Cursor",UA:"CustomDraw",VA:"CustomDrawScript",WA:"DBFieldName",XA:"DataField",YA:"DataName",ZA:"DataType",cG:"DateTimeAxis",aA:"Description",tG:"DetailToLastPage",bA:"Direction",cA:"DisabledSumFields",dA:"DisplayField",eA:"Dock",fA:"DtmxEncoding",gA:"DtmxMatrixSize",hA:"DtmxModuleSize",iA:"Editable",jA:"EndDateParameter",kA:"EndEllipsis",lA:"ExportBeginScript",mA:"ExportEndScript",nA:"FalseText",oA:"FetchRecordScript",pA:"FillColor",qA:"FillColorAuto",rA:"FillStyle",sA:"FirstCharIndent",tA:"FixCols",uA:"FixedWidth",vA:"FlowTo",wA:"FontWidthRatio",xA:"ForeColor",yA:"Format",zA:"FormatScript",AB:"FreeCell",dG:"GeneratePagesBeginScript",eG:"GeneratePagesEndScript",BB:"GetDisplayTextScript",CB:"GlobalScript",DB:"GridColsPerUnit",fG:"GridCount",EB:"GridLinePrintType",FB:"GridRowsPerUnit",gG:"GridSpacing",hG:"GridWidth",GB:"GroupAuto",HB:"GroupAutoSum",IB:"GroupBeginScript",JB:"GroupCount",KB:"GroupEndScript",LB:"GroupField",MB:"GroupIndex",NB:"GroupKeepTogether",OB:"GroupLabel",PB:"GroupTitle",QB:"GrowToBottom",RB:"GrowToNextRow",SB:"HCrossFields",TB:"HCrossPeriodType",UB:"HNewPageFixed",VB:"HPercentColumns",WB:"HResort",XB:"HSortAsc",YB:"HTotalAtFirst",ZB:"Height",iG:"HeightScale",aB:"HideOnRecordsetEmpty",bB:"HtmlTags",cB:"Image",dB:"ImageFile",eB:"ImageIndex",fB:"IncludeFooter",gB:"InitializeScript",hB:"InnerIndent",iB:"InnerStyles",jB:"IsCrossTab",kB:"Italic",lB:"KeepTogether",mB:"L2R",nB:"Label",oB:"LabelAsGroup",pB:"LabelInBar",qB:"LabelText",rB:"LabelTextAngle",sB:"Left",tB:"LeftMargin",uB:"LegendAtBottom",vB:"LegendColumnCount",wB:"LegendCursor",xB:"LegendShowSum",yB:"LegendSumLabel",zB:"LegendValueVisible",AC:"LegendVisible",BC:"Length",CC:"LimitsPerPage",DC:"LineColor",EC:"LineSpacing",FC:"LineType",GC:"LineVisible",HC:"LineWeight",IC:"ListCols",JC:"LnSpacing",KC:"Lock",LC:"Locked",MC:"MarginBeginWeight",NC:"MarginEndWeight",OC:"MarginGap",PC:"MarkerColor",QC:"MarkerColorAuto",RC:"MarkerLegendShow",SC:"MarkerSize",TC:"MarkerStyle",UC:"Max",jG:"MaxAtScale",VC:"Min",WC:"MirrorMargins",XC:"MonoPrint",YC:"Name",ZC:"NegativeAsZero",kG:"NegativeValueAbs",lG:"NegativeValueColor",aC:"NewPage",bC:"NewPageColumn",cC:"OccupiedColumns",dC:"OccupyColumn",eC:"Oriention",fC:"PDF417Columns",gC:"PDF417ErrorLevel",hC:"PDF417Rows",iC:"PDF417Simple",jC:"PaddingBottom",kC:"PaddingLeft",lC:"PaddingRight",mC:"PaddingTop",nC:"PageColumnCount",oC:"PageColumnDirection",pC:"PageColumnGroupNA",qC:"PageColumnSpacing",rC:"PageDivideCount",sC:"PageDivideLine",tC:"PageDivideSpacing",uC:"PageEndScript",vC:"PageGroup",wC:"PageProcessRecordScript",xC:"PageStartScript",yC:"ParagraphSpacing",zC:"Parameter",AD:"ParentPageSettings",BD:"PenColor",CD:"PenStyle",DD:"PenWidth",ED:"PercentFormat",FD:"Picture",mG:"Pie3DHeightDepth",GD:"Pitch",nG:"Point",HD:"PointWeight",ID:"PrintAdaptFitText",JD:"PrintAdaptMethod",KD:"PrintAdaptRFCStep",LD:"PrintAdaptRepeat",MD:"PrintAdaptTryToOnePage",ND:"PrintAsDesignPaper",OD:"PrintAtBottom",PD:"PrintBeginScript",QD:"PrintEndScript",RD:"PrintGridBorder",SD:"PrintOffsetSaveToLocal",TD:"PrintOffsetX",UD:"PrintOffsetY",VD:"PrintPageScript",WD:"PrintRotation",XD:"PrintToStretch",YD:"PrintType",ZD:"PrinterName",aD:"ProcessBeginScript",bD:"ProcessEndScript",cD:"ProcessRecordScript",dD:"QRCodeErrorLevel",eD:"QRCodeMask",fD:"QRCodeVersion",gD:"QuerySQL",hD:"RTF",iD:"RTrimBlankChars",jD:"RankNo",kD:"RegisterNo",lD:"RelationFields",mD:"RepeatOnPage",nD:"RepeatStyle",oD:"ReportFile",pD:"ResetPageNumber",qD:"RightMargin",oG:"RotateDegree",rD:"RotateMode",sD:"RowCount",tD:"RowLineColor",uD:"RowLineWidth",vD:"RowSelection",wD:"RowSpan",xD:"RowsIncludeGroup",yD:"RowsPerPage",zD:"SameAsColumn",AE:"ScriptType",BE:"SelectionBackColor",CE:"SelectionForeColor",DE:"SeriesAuto",EE:"SeriesCount",FE:"SeriesCursor",GE:"SeriesField",HE:"SeriesLabel",IE:"SeriesName",JE:"Shadow",KE:"ShadowColor",LE:"ShadowWidth",ME:"ShapeType",NE:"SharePrintSetupOptions",OE:"ShiftMode",pG:"ShiftModeOnHide",PE:"ShowColLine",QE:"ShowGrid",RE:"ShowMoneyDigit",SE:"ShowMoneyLabel",TE:"ShowMoneyLine",UE:"ShowMoneyLineColor",VE:"ShowMoneySepLineColor",WE:"ShowMoneyWidth",XE:"ShowPreviewWndScript",YE:"ShowRowLine",ZE:"ShrinkFontToFit",aE:"SingleSeriesColored",bE:"Size",cE:"SizeMode",dE:"SkipQuery",qG:"SmartRowHeight",eE:"SortAsc",fE:"SortCaseSensitive",gE:"SortFields",hE:"SortSummaryBox",iE:"Space",jE:"SpanToNewPage",uG:"SpanpageOptimize",kE:"Strikethrough",lE:"Style",mE:"Styles",nE:"SubtotalCols",oE:"SummaryFun",pE:"SystemVar",rG:"TOCColumnCount",sG:"TOCReportHeader",qE:"Tag",rE:"Text",sE:"TextAlign",tE:"TextAngle",uE:"TextFormat",vE:"TextOrientation",wE:"TextVisible",xE:"Title",yE:"TitleRepeat",zE:"TitleRows",AF:"ToNewExcelSheet",BF:"TooltipText",CF:"Top",DF:"TopMargin",EF:"TotalCols",FF:"TotalExcludeColumns",GF:"TotalHPercentColumns",HF:"TotalVPercentColumns",IF:"Transparent",JF:"TransparentMode",KF:"TrueText",LF:"Type",MF:"U2D",NF:"Underline",OF:"Unit",PF:"VAlign",QF:"VCrossFields",RF:"VPercentColumns",SF:"VResort",TF:"VSortAsc",UF:"Value",VF:"ValueAsPercent",WF:"ValueFormat",XF:"ValueVisible",YF:"Version",ZF:"Visible",aF:"Watermark",bF:"WatermarkAlignment",cF:"WatermarkSizeMode",dF:"Weight",eF:"Width",fF:"WordWrap",gF:"XAxisLabel",hF:"XAxisMaximum",iF:"XAxisMinimum",jF:"XAxisSpace",kF:"XAxisTextAngle",lF:"XAxisTextFormat",mF:"XAxisTextVisible",nF:"XAxisVisible",oF:"XValueField",pF:"XmlTableName",qF:"YAxisLabel",rF:"YAxisMaximum",sF:"YAxisMinimum",tF:"YAxisSpace",uF:"YAxisTextFormat",vF:"YAxisTextVisible",wF:"YAxisVisible",xF:"YValueField",yF:"ZValueField",zF:"ZValueFormat"},wrPropNameEncode:function(e){var t=gr.wr.propNameMap[e];return t?t:e},wrPropNameDecode:function(e){var t=gr.wr.propNameMap_R[e];return t?t:e},decodeWR:function(e){function t(e){return e>=" "&&"~">=e?p[e.charCodeAt(0)-32]:e}function r(e){return e>=" "&&"~">=e?p[e.charCodeAt(0)-32]:String.fromCharCode(21^e.charCodeAt(0))}var o,n,i,a,l,s="",c=4,u=1,d=e.length,f=gr.wr,p=f.WRAsciiMap_R,h=f.WRZipSource,g=f.WRZipDest,m=[];if("_"===e[c]&&(++c,u=0),u)for(;d>c;)s+=t(e[c++]);else for(;d>c;)s+=r(e[c++]);for(c=0,d=0,a=s[c];"["==a;){for(l={},o=++c;","!=a&&(a=s[++c],!("0">a||a>"9")););if(","!=a)break;for(l.f=+s.substring(o,c),o=++c;"]"!=a&&(a=s[++c],!("0">a||a>"9")););if("]"!=a)break;l.s=+s.substring(o,c++),m.push(l),d=c,a=s[c]}for(e=s.substr(d).split(""),l=m.length;l-- >0;)n=m[l].f,i=m[l].s,a=e[n],e[n]=e[i],e[i]=a;for(d=e.length,s="",c=0;d>c;c++)a=e[c],l=g.indexOf(a),l>=0?s+=h[l]:("#"==a&&(a=e[++c]),s+=a);return s}}}(),function(e){"use strict";var t=(gr.enum_,gr.const_),r=gr.common,o=gr.locale,n=gr.helper,i=n.intFixed,a=n.confirmDateValue,l=n.confirmBooleanValue,s=n.rgba2color,c=n.color2rgba,u=n.decodeTextColor,d=gr.format={};d.BooleanFormatter=function(e){this.parse(e)},d.BooleanFormatter.prototype={parse:function(e){var t,r=this;e?(t=e.indexOf(":"),-1===t?(r.trueValueText=e,r.falseValueText=""):(r.trueValueText=e.substr(0,t),r.falseValueText=e.substr(t+1))):(r.trueValueText="true",r.falseValueText="false")},format:function(e){var t=this;return e=l(e),e?t.trueValueText:t.falseValueText}};var f=function(){this.defaultMe()};f.prototype={defaultMe:function(){var e=this;e.precision=9,e.leastPrecision=0,e.leastIntegerWidth=1,e.showPositive=!1,e.showNegative=!0,e.showSymbolAtHead=!0,e.negativeBracket=!1,e.intSepStep=0,e.asPercent=!1,e.asCurrency=!1,e.blankSepSymbol=!1,e.firstCurrencySymbol=!0,e.exponent=!1,e.textColor=0,e.prefix="",e.suffix=""},isCustomTextColor:function(){return 0!==this.textColor},parse:function(e){function t(e){return-1!=="0#.,+-()$%".indexOf(e)}var r,o,n,i,a,l=this;if(l.defaultMe(),e){if(r=u(e),r&&(e=e.substr(r.index),l.textColor=r.color),a=e.length,l.exponent="e"===e.charAt(0),!l.exponent){for(o=0;a>o&&!t(e.charAt(o));)++o;if(o>0&&(l.prefix=e.substr(0,o),e=e.substr(o),a=e.length),a>o){for(o=a-1;!t(e.charAt(o))&&o>0;)--o;a-1>o&&(l.suffix=e.substr(o+1),e=e.substr(0,o+1),a=e.length)}}for(i=e.indexOf("."),-1===i&&(i=a),o=e.indexOf(","),o>=0&&(l.intSepStep=i-o-1,l.intSepStep<0&&(l.intSepStep=3)),l.negativeBracket=e.indexOf("(")>=0&&e.indexOf(")")>=0,l.asPercent=e.indexOf("%")>=0,l.blankSepSymbol=e.indexOf(" ")>=0,o=e.indexOf("+"),l.showPositive=o>=0,n=e.indexOf("-"),l.showNegative=n>=0||!l.negativeBracket&&!l.isCustomTextColor(),n>o&&(o=n),n=e.indexOf("$"),l.asCurrency=n>=0,n>o&&(o=n),l.showSymbolAtHead=-1===o||i>o,l.asCurrency&&(l.negativeBracket?(o=e.indexOf(l.showSymbolAtHead?"(":")"),l.firstCurrencySymbol=o>n):l.firstCurrencySymbol=o>=n),l.leastIntegerWidth=0,o=0;i>o;++o)"0"===e.charAt(o)&&++l.leastIntegerWidth;for(l.leastPrecision=0,l.precision=0,o=i+1;a>o;++o)"0"===e.charAt(o)?(++l.leastPrecision,++l.precision):"#"===e.charAt(o)&&++l.precision}},format:function(e){var t,r,n,i,a,l,s,u,d,f,p,h=this,g="";if(e=+e,h.isCustomTextColor()&&(t=c(h.textColor),g="[_color#=rgb("+t.r+","+t.g+","+t.b+")]"),h.asPercent&&(e*=100),h.exponent){if(r=e,0>r&&(r=-r),n=0,0!==r){for(;r>=10;)r/=10,++n;for(;1>r;)r*=10,--n}return h.exponent=!1,0>e&&(r=-r),g+=h.format(r),g+="e",n>=0?g+="+":(g+="-",n=-n),g+=n,h.exponent=!0,g}if(p=e.toFixed(h.precision),f=p.length,h.precision>h.leastPrecision){for(i=f-1,a=i-h.precision+h.leastPrecision;i>a&&"0"===p.charAt(i);i--);"."!==p.charAt(i)&&i++,il&&(l=f),g+=h.prefix,h.showSymbolAtHead?(h.asCurrency&&h.firstCurrencySymbol&&(g+=o.currencySymbol),0>e?h.negativeBracket&&(g+="("):h.showPositive&&e>0&&(g+="+"),h.asCurrency&&!h.firstCurrencySymbol&&(g+=o.currencySymbol),h.blankSepSymbol&&(g+=" ")):h.negativeBracket&&0>e&&(g+="("),i=0,s=l,0>e&&(--s,++i,h.showNegative&&(g+="-")),u=h.leastIntegerWidth-s,d=s,u>0)for(d+=u;d>s;)g+="0",d--,h.intSepStep>0&&d%h.intSepStep===0&&(g+=o.thousandSep);else for(;u++<0&&"0"===p.charAt(i);)i++;if(h.intSepStep>0)for(;d>0;)g+=p.charAt(i++),--d%h.intSepStep===0&&0!==d&&(g+=o.thousandSep);else g+=p.substring(i,l);return f>l&&(g+=p.substr(l)),h.showSymbolAtHead?h.negativeBracket&&0>e&&(g+=")"):(h.blankSepSymbol&&(g+=" "),h.asCurrency&&h.firstCurrencySymbol&&(g+=o.currencySymbol),0>e?g+=h.negativeBracket?")":"-":h.showPositive&&e>0&&(g+="+"),h.asCurrency&&!h.firstCurrencySymbol&&(g+=o.currencySymbol)),h.asPercent&&(g+="%"),g+=h.suffix}};var p=function(e){e=e.substring(1,e.length-1);for(var t=e.split(","),r=0;r1&&""!==t[1]&&(o.negativeAnalyser=new f,o.negativeAnalyser.parse(t[1])),r>2&&""!==t[2]&&(o.zeroAnalyser=new f,o.zeroAnalyser.parse(t[2]))}var r,o=this;if(o.defaultMe(),e){var n=e.length;r=e.indexOf(g),-1!==r?(o.formatType=1,e=e.substr(0,r)+e.substr(r+g.length),""!==e&&t(e)):n>=2&&e.substr(0,2)===h?2===n?o.formatType=2:(o.formatType=3,t(e.substr(2))):"{"===e.charAt(0)&&"}"===e.charAt(n-1)?(o.formatType=4,o.intEnumAnalyser=new p(e)):"!hz"===e?o.formatType=5:"!rm"===e?o.formatType=6:"!c"===e?o.formatType=7:"!b"===e?o.formatType=8:"!a"===e?o.formatType=9:"!A"===e?o.formatType=10:t(e)}},format:function(r){function o(e,t,r,o){var n,i,a=!1,l=!1,s=0,c="";for(n=t-1;n>=0;n--,s++)i=parseInt(e.charAt(n)),s%4===0&&s>0&&(a=!0),0===i?l=!0:(l&&(""!==c&&(c=r.charAt(0)+c),l=!1),s>0&&(a&&s%4!==0&&(c=o.charAt(s-s%4)+c),a=!1,c=o.charAt(s)+c),c=r.charAt(i)+c);return""==c&&(c=r.charAt(0)),c}function n(e,r){return o(e,r,t.BIG_AMT_NUMBER,t.HZ_AMT_STEP)}function i(e){var r,o,n="",i=e.length;for(o=0;i>o;o++)r=parseInt(e.charAt(o)),isNaN(r)||(n+=t.BIG_AMT_NUMBER.charAt(r));return n}function a(e){var r,o,i,a,l,s=0>=e;return e>-.005&&.005>e?t.HZ_ZERO_YUAN:(s&&(e=-e),a=e.toFixed(2),r=a.indexOf("."),l=n(a,r),""!==l&&(l+=t.HZ_AMT_STEP.charAt(0)),o=parseInt(a.charAt(r+1)),i=parseInt(a.charAt(r+2)),0===o&&0===i?l+=t.HZ_AMT_UNIT[0]:(0===o&&""===l||(l+=t.BIG_AMT_NUMBER.charAt(o)),0!==o&&(l+=t.HZ_AMT_UNIT.charAt(1)),0!==i?(l+=t.BIG_AMT_NUMBER.charAt(i),l+=t.HZ_AMT_UNIT.charAt(2)):l+=t.HZ_AMT_UNIT.charAt(0)),s&&(l=t.HZ_NEGATIVE+l),l)}function l(e,r){var o,i,a,l,s=Math.max(0,r.precision),c=Math.min(s,r.leastPrecision),u=-.5*Math.pow(.1,s),d=u>=e;for(0>e&&(e=-e),a=e.toFixed(s),o=a.length,i=a.indexOf("."),-1===i&&(i=o),l=n(a,i),""===l&&(l=t.BIG_AMT_NUMBER.charAt(0)),o--;o>i+c&&"0"===a.charAt(o);o--);if(o>i)for(l+="点",i++;o>=i;i++)l+=t.BIG_AMT_NUMBER.charAt(parseInt(a.charAt(i)));return d&&(l=t.HZ_NEGATIVE+l),""!==r.prefix&&(l=r.prefix+l),""!==r.suffix&&(l+=r.suffix),l}function s(e){var r,n=new f;return n.Precision=0,r=n.format(e),r=o(r,r.length,t.HZ_NUMBER,t.HZ_NUMRANK),e>=10&&20>e&&(r=r.substr(1)),r}function c(e){var t,r="";if(0>e||e>=4e3)return"N/A";for(t=Math.floor(e/1e3)%10;t-- >0;)r+="Ⅿ";if(t=Math.floor(e/100)%10,3>=t)for(;t-- >0;)r+="Ⅽ";else 4==t?(r+="Ⅽ",r+="Ⅾ"):5==t?r+="Ⅾ":8>=t?(r+="Ⅾ",r+="Ⅽ",t>=7&&(r+="Ⅽ"),t>=8&&(r+="Ⅽ")):(r+="Ⅽ",r+="Ⅿ");if(t=Math.floor(e/10)%10,3>=t)for(;t-- >0;)r+="Ⅹ";else 4==t?(r+="Ⅹ",r+="Ⅼ"):5==t?r+="Ⅼ":8>=t?(r+="Ⅼ",r+="Ⅹ",t>=7&&(r+="Ⅹ"),t>=8&&(r+="Ⅹ")):(r+="Ⅹ",r+="Ⅽ");return t=e%10,t&&(r+=String.fromCharCode(8544+t-1)),r}function u(e){var t="N/A";return e>=0&&50>=e&&(t=0==e?"⓪":20>=e?String.fromCharCode(9312+e-1):35>=e?String.fromCharCode(12881+e-21):String.fromCharCode(12977+e-36)),t}function d(e){return e>0&&20>e?String.fromCharCode(9332+e-1):"N/A"}function p(e){return e>0&&26>=e?String.fromCharCode(97+e-1):"N/A"}function h(e){return e>0&&26>=e?String.fromCharCode(65+e-1):"N/A"}var g,m=this;if(r=+r,isNaN(r))return"";switch(m.formatType){case 0:case 1:if(m.zeroAnalyser&&r>-.5&&.5>r){var C=m.positiveAnalyser.precision;m.positiveAnalyser.asPercent&&(C+=2);var b=C;m.negativeAnalyser&&(b=m.negativeAnalyser.precision,m.negativeAnalyser.asPercent&&(b+=2));var x=Math.pow(.1,b)*-.5,v=.5*Math.pow(.1,C);r>x&&v>r&&(g=m.zeroAnalyser.format(0))}g===e&&(g=m.negativeAnalyser&&0>r?m.negativeAnalyser.format(r):m.positiveAnalyser.format(r)),1===m.formatType&&(g=i(g));break;case 2:g=a(r);break;case 3:g=l(r,m.positiveAnalyser);break;case 4:g=m.intEnumAnalyser.format(r);break;case 5:g=s(r);break;case 6:g=c(r);break;case 7:g=u(r);break;case 8:g=d(r);break;case 9:g=p(r);break;case 10:g=h(r)}return g}},d.DateTimeFormatter=function(e){this.parse(e)},d.DateTimeFormatter.prototype={parse:function(r){function n(){for(w=1;D[F+1]===P;)F++,w++}function a(){var e=w;w%=2,e=(e-w)/2,e>0&&l(e)}function l(e){var t="",r=B.length;do t+=P;while(--e>0);r>0&&15===B[r-1].type?B[r-1].data+=t:B.push({type:15,data:t,funDisplayText:x})}function s(e,t){return i(e.getFullYear(),t.subtype)}function c(r,n){var a=r.getMonth();return n.subtype<=4?i(a+1,n.subtype):o.supportIntl?5===n.subtype?new Intl.DateTimeFormat(e,{month:"short"}).format(r):new Intl.DateTimeFormat(e,{month:"long"}).format(r):10>a?t.HZ_NUMBER.charAt(a):t.HZ_NUMBER.charAt(9)+t.HZ_NUMBER.charAt(a-10)+t.HZ_DATETIME_UNIT.charAt(5)}function u(e,t){return i(e.getDate(),t.subtype)}function d(r,n){var i;return o.supportIntl?5===n.subtype?new Intl.DateTimeFormat(e,{weekday:"short"}).format(r):new Intl.DateTimeFormat(e,{weekday:"long"}).format(r):(i=r.getDay(),(5===n.subtype?t.HZ_DATETIME_UNIT.substr(0,1):t.HZ_DATETIME_UNIT.substring(2,4))+(i>0?t.HZ_NUMBER.charAt(i-1):t.HZ_DATETIME_UNIT.charAt(1)))}function f(e,t){var r=e.getHours()%12;return r||(r=12),i(r,t.subtype)}function p(e,t){return i(e.getHours(),t.subtype)}function h(e,t){return i(e.getMinutes(),t.subtype)}function g(e,t){return i(e.getSeconds(),t.subtype)}function m(e,t){return e.toLocaleDateString()}function C(e,t){return e.toLocaleTimeString()}function b(e,t){return e.toLocaleString()}function x(e,t){return t.data}function v(e,t){return e.getHours()<12?o.textAM:o.textPM}function S(e,t){return o.dateSep}function T(e,t){return o.timeSep}var y,F,w,D,P,R,A,B=this.items=[];if(r){D=r.split(""),y=D.length,F=0;do{switch(P=D[F],n(),A={},P){case"d":3>w?(A.type=3,A.funDisplayText=u,A.subtype=1===w?0:2):(A.type=4,A.funDisplayText=d,A.subtype=3===w?5:6);break;case"M":A.type=2,A.funDisplayText=c,1===w?A.subtype=0:2===w?A.subtype=2:3===w?A.subtype=5:A.subtype=6;break;case"y":A.type=1,A.funDisplayText=s,A.subtype=3>w?2:0;break;case"h":A.type=5,A.funDisplayText=f,A.subtype=1===w?0:2;break;case"H":A.type=6,A.funDisplayText=p,A.subtype=1===w?0:2;break;case"m":A.type=7,A.funDisplayText=h,A.subtype=1===w?0:2;break;case"s":A.type=8,A.funDisplayText=g,A.subtype=1===w?0:2;break;case"t":2===w&&(A.type=20,A.funDisplayText=v);break;case"%":a(),w&&(R=D[F+1],++F,"d"===R?(A.type=10,A.funDisplayText=m):"t"===R?(A.type=11,A.funDisplayText=C):"g"===R?(A.type=12,A.funDisplayText=b):F--);break;case":":a(),w&&(A.type=22,A.funDisplayText=T);break;case"/":a(),w&&(A.type=21,A.funDisplayText=S);break;default:l(w)}A.type&&15!==A.type&&B.push(A)}while(++F0)for(o+=".",r=0;t>r;r++)o+="0";return m.NumberFormat(e,o)},NumberFormatToBigHZAmount:function(e){return m.NumberFormat(e,"$$")},NumberRound45:function(e,t){var r=Math.pow(10,t);return Math.round(e*r)/r},NumberRound465:function(e,t){var r,o,n,i=Math.pow(10,t+2);return e=Math.floor(e*i),r=e%10,o=e%100-r,e-=o+r,o>=60?e+=100:50===o&&(r>0?e+=100:(n=e%1e3/100,n%2&&(e+=100))),e/i},DisableDebug:function(){},ExtractFileName:function(e){return""},ExtractFilePath:function(e){return""},FormatTextHeight:function(e,t,r,o){return 0},FormatTextOutLen:function(e,t,r,o,n){return 0},FormatTextWidth:function(e,t,r){return 0},GetDefaultPrinterName:function(){return""},GetFontCount:function(){return 0},GetFontName:function(e){return""},GetPrinterCount:function(){return 0},GetPrinterName:function(e){return""},GetPrinterPaperCount:function(e){return 0},GetPrinterPaperName:function(e){return""},LoadFontFromDir:function(e){},LoadFontFromFile:function(e){return 0},Log:function(e){},PathFileExist:function(e){return 0},ReportFontReplace:function(e,t,r){return 0},SetMoneyLabelText:function(e){},TransferResponse:function(){return""}}}(),function(e){"use strict";var t=gr.enum_,r=gr.helper,o=gr.format,n=gr.Utility,i=r.ensureNameText,a=r.enumName2Value,l=r.enumValue2Name,s=r.prototypeCopyExtend,c=(o.BooleanFormatter,o.DateTimeFormatter),u=o.NumericFormatter,d=gr.exp={},f="[#",p="#]",h=2,g=function(e){for(var t,r,o=e.length,n=0,i=0,a=0,l=[];o>n;)t=e[n],a?")"===t?a--:"("===t&&a++:","===t?(r=e.substring(i,n),r&&l.push(r),i=n+1):"("===t&&++a,++n;return r=e.substring(i,n),r&&l.push(r),l},m=d.SummaryItem=function(){};m.prototype={init:function(e,t){var r=this;e&&(r.paramExp=new w(r.report,e)),t&&(r.displayField=r.report.RunningFieldByName(t))},getAsFloat:function(){var e=this;return e.valueField?e.valueField.AsFloat:e.value},getAsString:function(){var e=this;return e.valueField?e.valueField.AsString:e.value},getValue:function(){return this.getAsFloat()},setValue:function(e){var t=this;t.valueField?t.valueField.AsFloat=e:t.value=e},isDateTimeValue:function(){var e=this;return e.argExp&&e.argExp.isSingleVariable()&&e.argExp.varItems[0].varField.isDateTimeValue()},beginSummaryValue:function(){var t=this,r=t.SummaryFun;t.recordCount=0,4===r||5===r?t.value=e:27!==r&&28!==r&&(t.value=0),t.values=[]},summaryCurRecord:function(){var t,r=this,o=r.values,n=r.SummaryFun,i=r.paramExp;switch(++r.recordCount,n){case 12:case 13:return void(i&&i.isNull()&&(r.value+=1));case 14:case 29:case 30:return void(i&&o.push(i.getDisplayText()));case 19:case 18:return void(i&&(t=i.getDisplayText(),0===r.value?r.value=t:(19===n&&r.value.localeCompare(t)<0||18===n&&r.value.localeCompare(t)>0)&&(r.value=t)))}switch(t=i?i.calculate():0,n){case 1:case 2:case 27:case 28:isNaN(t)||(r.value+=t);break;case 26:isNaN(t)||(r.value+=Math.abs(t));break;case 4:case 5:i&&!i.isNull()&&(r.value===e||5===n&&r.valuet)&&(r.value=t,r.displayField&&(r.stringValue=r.displayField.DisplayText));break;case 6:case 7:case 8:case 9:case 10:case 11:o.push(t);break;case 16:case 17:case 15:case 20:case 21:case 22:case 23:case 24:case 25:i&&!i.isNull()&&o.push(t)}},endSummaryValue:function(){var t,r,o=this,n=o.values,i=n?n.length:0;switch(o.SummaryFun){case 2:o.recordCount>0?o.value/=o.recordCount:o.value=e;break;case 3:o.value=o.recordCount;break;case 6:case 20:o.value=i>1?o.devSq()/(i-1):e;break;case 7:case 21:o.value=i>1?o.devSq()/i:e;break;case 8:case 22:o.value=i>1?Math.sqrt(o.devSq()/(i-1)):e;break;case 9:case 23:o.value=i>1?Math.sqrt(o.devSq()/i):e;break;case 10:case 24:o.value=o.aveDev();break;case 11:case 25:o.value=o.devSq();break;case 16:case 17:if(n.sort(function(e,t){return e-t}),io;)t=r.values[o++],e===t?i--:e=t;return i}};var C=function(e,t,r){var o,n=this,i=g(r),a="";n.report=e,n.SummaryFun=t,i.length>0&&(o=i[0]),i.length>1&&(n.rankNo=parseInt(i[1])),n.init(o),n.paramExp&&(i=n.paramExp.varItems,1===i.length&&i[0].varField.object&&(a=i[0].varField.object.Format)),n.formater=new u(a)};C.prototype={type:4,getDisplayText:function(){var e=this,t=e.SummaryFun;return 29===t||30===t||18===t||19===t?e.getAsString():e.formater.format(e.getAsFloat())},getExpText:function(){var e=this;return l(t.SummaryFun,e.SummaryFun)+"("+e.paramExp?e.paramExp.getExpText():""+e.RankNo?","+e.RankNo:")"}},s(C,m);var b=function(e,t,r){var o=this,n=g(r);o.report=e,o.MathFun=t,o.paramExps=[],n.forEach(function(t){o.paramExps.push(new w(e,t))})};b.prototype={type:5,getAsFloat:function(){var e,r,o=this,i=o.MathFun,a=o.paramExps,s=a.length,c=1===s,u=0;if(1===i||2===i||22===i||9===i?c=2==s:20!==i&&21!==i||(c=s>1),!c)return alert("表达式中函数 '"+l(t.MathFun,i)+"' 的参数未正确提供。"),NaN;switch(e=a[0].calculate(),r=2===s?a[1].calculate():0,i){case 1:u=n.NumberRound45(e,r);break;case 2:u=n.NumberRound465(e,r);break;case 5:u=Math.abs(e);break;case 6:u=Math.acos(e);break;case 7:u=Math.asin(e);break;case 8:u=Math.atan(e);break;case 9:u=Math.atan2(e,r);break;case 15:u=Math.ceil(e);break;case 16:u=Math.cos(e);break;case 17:u=Math.exp(e);break;case 18:u=Math.floor(e);break;case 19:u=Math.log(e);break;case 20:if(u=e,s>1&&(r>e&&(u=r),s>2))for(;s-- >2;)e=a[s].calculate(),e>u&&(u=e);break;case 21:if(u=e,s>1&&(e>r&&(u=r),s>2))for(;s-- >2;)e=a[s].calculate(),u>e&&(u=e);break;case 22:u=Math.pow(e,r);break;case 23:u=Math.round(e);break;case 24:u=Math.sin(e);break;case 25:u=Math.sqrt(e);break;case 26:u=Math.tan(e)}return u},getValue:function(){var e=this,t=e.valueField;return t?t.AsFloat:e.getAsFloat()},getDisplayText:function(){var e=this,t=e.valueField;return t?t.DisplayText:e.getAsFloat()+""},getExpText:function(){for(var e=this,r=e.paramExps,o=r.length,n=0,i=l(t.MathFun,e.MathFun)+"(";o>n;)n>0&&(i+=","),i+=r[n].getExpText();return i+=")"},isDateTimeValue:function(){return 0}};var x=function(e,r){var o=this,n=r.split(",");o["var"]=a(t.SystemVarType,n[0]),n.length>1&&(o.groupIndex=parseInt(n[1])),o.report=e};x.prototype={type:3,getAsFloat:function(){var e=this;return e.report.SystemVarValue2(e["var"],e.groupIndex)},getValue:function(){return this.getAsFloat()},getDisplayText:function(){var e=this;return e["var"]>0?e.getAsFloat()+"":""},getExpText:function(){var e=this;return"SystemVar("+l(t.SystemVarType,e["var"])+e.groupIndex?","+e.groupIndex:")"},isDateTimeValue:function(){return 1===this["var"]}};var v=function(e){this.oParameter=e};v.prototype={type:2,getAsFloat:function(){return this.oParameter.AsFloat},getValue:function(){return this.oParameter.Value},getDisplayText:function(){var e=this;return e.valueField?e.valueField.DisplayText:e.oParameter.DisplayText},getExpText:function(){return i(this.oParameter.Name)},isDateTimeValue:function(){return 6===this.oParameter.DataType}};var S=function(e){this.object=e};S.prototype={type:1,getAsFloat:function(){return this.object.AsFloat},getValue:function(){return this.object.Value},getDisplayText:function(){return this.object.DisplayText},getExpText:function(){return i(this.object.Name)},isDateTimeValue:function(){return 6===this.object.FieldType}};var T=function(e){this.object=e};T.prototype={type:6,getAsFloat:function(){return parseFloat(this.object.DisplayText)},getValue:function(){return this.getDisplayText()},getDisplayText:function(){return this.object.DisplayText},getExpText:function(){return i(this.object.Name)},isDateTimeValue:function(){return 0}};var y=function(e,t,r){var o=this;o.varField=e,o.beginIndex=t,o.endIndex=r},F=function(e,t){function r(){for(c=u[n];C.test(c);)c=u[++n]}function o(e,r){var o=c===e;return o&&(n=t.indexOf(r,i),-1===n?m=!1:(d=t.substring(i+1,n), +a=++n)),o}var n,i,a,l,s,c,u,d,f,p,h,g=this,m=!0,C=/\s/,b=/[\d.]/,x=/[()+-\/%*]/,v=/[()+-\/%*\s]/;for(u=t.split(""),s=u.length,g.expText=t,g.varItems=[],n=0;s>n&&m;)if(r(),x.test(c)||b.test(c))n++;else{if(i=n,o("{","}"))p=g.decodeVariable(e,d,1);else{if(!o("[","]")){for(;s>n&&!v.test(u[n]);)n++;if(a=n,d=t.substring(i,a),!d)continue}if(r(),"("===c){for(l=++n,h=1;s>n;)if(c=u[n++],")"===c){if(h--,0>=h)break}else"("===c&&h++;f=t.substring(l,n-1),a=n,p=g.decodeFunction(e,d,f)}else p=g.decodeVariable(e,d,0)}p?g.varItems.push(new y(p,i,a)):m=!1}g.valid=m};F.prototype={calculate:function(){var e=this,t=0,r="",o=NaN;return e.valid&&(e.varItems.forEach(function(o){t=Number.POSITIVE_INFINITY||o<=Number.NEGATIVE_INFINITY)&&(o=NaN)),o},isSingleVariable:function(){var e=this;return 1===e.varItems.length&&0===e.varItems[0].beginIndex&&e.varItems[0].endIndex===e.expText.length}};var w=function(e,t){F.call(this,e,t)};w.prototype={decodeVariable:function(t,r,o){var n,i=e;return o?(n=t.ParameterByName(r))&&(i=new v(n)):(n=t.RunningFieldByName(r))?i=new S(n):(n=t.ParameterByName(r))?i=new v(n):(n=t.ControlByName(r))&&(i=new T(n)),i},decodeFunction:function(e,r,o){var n,i=a(t.SummaryFun,r);return i>=0?n=new C(e,i,o):"SystemVar"===r?n=new x(e,o):(i=a(t.MathFun,r),i>=0&&(n=new b(e,i,o))),n},getExpText:function(){var e=this,t=0,r="";return e.varItems.forEach(function(o){t=0&&(o=t.indexOf("[",s),!(-1===o||o>r))&&(o=t.indexOf("]",r),-1!==o);)s=o+1,r=t.indexOf(":",s);r>=0&&(d=l.format=t.substr(r+1),t=t.substr(0,r)),F.call(l,e,t),i=l.isSingleVariable(),i&&(a=l.varItems[0].varField),!d&&i||(n=i&&a.isDateTimeValue(),!n&&d&&"!"!=d.charAt(0)&&(n=/[yMdthHms]/.test(d)&&!/[0#e$]/.test(d)),l.formater=n?new c(d):new u(d)),d&&i&&(a.format=d)};D.prototype={getDisplayText:function(){function e(e){return'"'+e.expText+'" 不是有效的运算表达式'}var t,r=this,o=r.isSingleVariable(),n=r.varItems;return r.valid?!r.formater||o&&n[0].varField.stringValue?t=n[0].varField.getDisplayText():(t=o?n[0].varField.getValue():r.calculate(),t=r.formater.format(t)):t=e(this),t}},s(D,F);var P=function(e,t){D.call(this,e,t)};P.prototype={decodeVariable:w.prototype.decodeVariable,decodeFunction:w.prototype.decodeFunction,isPureNumeric:function(){var e=this;return u.prototype.isPrototypeOf(e.formater)||e.isSingleVariable()&&!e.varItems[0].varField.isDateTimeValue()}},s(P,D);var R=function(e){this.oParameter=e};R.prototype={getAsFloat:function(){return this.oParameter.AsFloat},getValue:function(){return this.oParameter.Value},getDisplayText:function(){return this.oParameter.DisplayText},isDateTimeValue:function(){return 6===this.oParameter.DataType}};var A=function(e,t){var r=this;r.textBuilder=e,r.varType=t};A.prototype={getAsFloat:function(){function e(){var e,t,r=n.graphs,o=r.length;for(e=0;o>e&&(t=r[e],!t.some(function(e){return e===i}));e++);return t}var t,r=this,o=r.textBuilder,n=o.chart,i=o.seriesIndex,a=o.groupIndex;if(n.IsScatterGraph())switch(t=n.Value(i,a),r.varType){case 1:t=t.x;break;case 2:t=t.y;break;case 3:t=t.z;break;case 5:t=t.z/n.GetSeriesTotalValue(i);break;case 7:t=n.GetSeriesTotalValue(i)}else switch(t=n.Value(i,a),r.varType){case 4:t/=n.GetGroupTotalValue(e(),a);break;case 6:t=n.GetGroupTotalValue(e(),a);break;case 5:t/=n.GetSeriesTotalValue(i);break;case 7:Val=n.GetSeriesTotalValue(i)}return t},getValue:function(){return this.getAsFloat()},getDisplayText:function(){var e=this,t=e.varType,r=e.textBuilder,o=r.seriesIndex,i=r.groupIndex,a=r.chart,l=a.IsScatterGraph(),s=a.graphSerieses[o],c=e.getAsFloat(),u=0;if(l)switch(t){case 1:u=1;break;case 2:u=2;break;case 3:case 7:u=3;break;case 5:u=5;break;case 8:u=4}else switch(t){case 1:case 8:case 9:u=4;break;case 2:case 6:case 7:u=2;break;case 4:case 5:u=5}switch(u){case 1:c=a.XAxis.ScaleFormatParser.format(c);case 2:c=l?a.YAxisOfSeries(s).ScaleFormatParser.format(c):s.ValueFormatParser.format(c);break;case 3:c=s.ValueFormatParser.format(c);break;case 4:c=8===t?a.SeriesLabel(o):a.GroupLabel(i);break;case 5:c=n.NumberFormat(c,"0.00%")}return c},isDateTimeValue:function(){return 0}};var B=function(e,t){D.call(this,e,t)};B.prototype={decodeVariable:function(t,r,o){var n=t.chart.report.ParameterByName(r);return n?new R(n):e},decodeFunction:function(e,r,o){var n;return"chartvar"===r.toLowerCase()&&(o=a(t.ChartVarType,o),o>0&&(n=new A(e,o))),n}},s(B,D);var E=function(e){this.text=e};E.prototype={getDisplayText:function(){return this.text}};var G=d.TextBuilder=function(e,t){var r,o=this,n=e.XAxis?B:P,i=0,a=0,l=0;for(o.items=[],e.XAxis&&(o.chart=e,e=this);(a=t.indexOf(f,l))>=0;)l=t.indexOf(p,a+h),l>=0&&(r=new n(e,t.substring(a+h,l)),l+=h,r.valid&&(a>i&&o.items.push(new E(t.substring(i,a))),i=l,o.items.push(r)),a=l);il;){if(e=a[l++],t=e.varItems)for(n=t.length,o=0;n>o;++o)if(r=t[o].varField,3===r.type&&r["var"]<0)return"";c+=e.getDisplayText()}return c},generateChartDisplayText:function(e,t){var r=this;return r.seriesIndex=e,r.groupIndex=t,r.generateDisplayText()},isStaticText:function(){var e=this;return 1==e.items.length&&E.prototype.isPrototypeOf(e.items[0])}}}(),function(undefined){"use strict";function CrossSummaryGetFieldSuffix(e){switch(e){case 2:return csz_HAverage_Suffix;case 15:return csz_HAverageA_Suffix;case 3:return csz_HCount_Suffix;case 13:return csz_HCountA_Suffix;case 4:return csz_HMin_Suffix;case 5:return csz_HMax_Suffix}return""}function CalcPointMarkerRect(e,t,r){return new Rect(e-r,t-r,e+r,t+r)}gr.dom={};var grenum=gr.enum_,grconst=gr.const_,grcommon=gr.common,grhelper=gr.helper,grformat=gr.format,grexp=gr.exp,script=gr.script,BooleanFormatter=grformat.BooleanFormatter,DateTimeFormatter=grformat.DateTimeFormatter,NumericFormatter=grformat.NumericFormatter,SummaryItem=grexp.SummaryItem,TextBuilder=grexp.TextBuilder,DateTime=grcommon.DateTime,Rect=grcommon.Rect,Pen=grcommon.Pen,Border=grcommon.Border,Font=grcommon.Font,FontWrapper=grcommon.FontWrapper,TextFormat=grcommon.TextFormat,ImageList=grcommon.ImageList,Context=grcommon.Context,Graphics=grcommon.Graphics,HtmlStyles=grcommon.HtmlStyles,HtmlElement=grcommon.HtmlElement,assignJSONMembers=grhelper.assignJSONMembers,enumMemberValid=grhelper.enumMemberValid,enumBitMemberValid=grhelper.enumBitMemberValid,colorMemberValid=grhelper.colorMemberValid,fontString=grhelper.fontString,fontHeight=grhelper.fontHeight,intFixed=grhelper.intFixed,pixelsToHtml=grhelper.pixelsToHtml,percentToHtml=grhelper.percentToHtml,penStyleText=grhelper.penStyleText,tagCSSChild=grhelper.tagCSSChild,tagCSSChildR=grhelper.tagCSSChildR,cloneDate=grhelper.cloneDate,confirmDateValue=grhelper.confirmDateValue,confirmCloneDateValue=grhelper.confirmCloneDateValue,strimDate=grhelper.strimDate,incDate=grhelper.incDate,incDate2=grhelper.incDate2,confirmBooleanValue=grhelper.confirmBooleanValue,ensureNameText=grhelper.ensureNameText,enumName2Value=grhelper.enumName2Value,enumValue2Name=grhelper.enumValue2Name,rgba2color=grhelper.rgba2color,colorAlpha=grhelper.colorAlpha,color2rgba=grhelper.color2rgba,colorGradientLight=grhelper.colorGradientLight,colorGradientDark=grhelper.colorGradientDark,colorValue2Html=grhelper.colorValue2Html,prototypeLinkExtend=grhelper.prototypeLinkExtend,prototypeCopyExtend=grhelper.prototypeCopyExtend,createArray=grhelper.createArray,assignObjectEx=grhelper.assignObjectEx,assignObject=grhelper.assignObject,assignObjectAtom=grhelper.assignObjectAtom,parseXML=grhelper.parseXML,getRelativePosition=grhelper.getRelativePosition,addEvent=grhelper.addEvent,bindEvents=grhelper.bindEvents,wrPropNameEncode=gr.wr?gr.wr.wrPropNameEncode:undefined,toDegree=grhelper.toDegree,toRadians=grhelper.toRadians,uidCanvas=function(){var e=0;return function(){return"-gr-canvas-"+e++}}(),calcSectionsHeightPercent=function(e){var t=0,r=0;e.length&&(e.forEach(function(e){t+=e.pxHeight}),e.forEach(function(e){e.pctHeight=Math.round(100*e.pxHeight/t),r+=e.pctHeight}),e[0].pctHeight+=100-r)},calcSectionsPixelTotalHeight=function(e){var t=0;return e.forEach(function(e){t+=e.pxHeight}),t},Collection=function(e){var t=this;t.owner=e,t.items=[]};Collection.prototype={loadFromJSON:function(e){var t,r=0;if(e)for(t=e.length;t>r;)this._doloadItem(e[r++])},getJsonMember:function(e){return this.owner.report.isWR?wrPropNameEncode(e):e},assign:function(e){var t=this;t.RemoveAll(),e.forEach(function(e){t.Add().assign(e)})},attachData:function(){for(var e=this.items,t=e.length;t--;)e[t].attachData()},prepare:function(){for(var e=this.items,t=0,r=e.length;r>t;)e[t++].prepare()},unprepare:function(){for(var e=this.items,t=e.length;t--;)e[t].unprepare()},generate:function(e){for(var t=this.items,r=0,o=t.length;o>r;)t[r++].generate(e)},itemByName:function(e){var t,r=this.items,o=r.length;if(e)for(e=e.toUpperCase();o--;)if(t=r[o],t.Name.toUpperCase()===e)return t;return undefined},indexOfCOMIndex:function(e){return e?("number"!=typeof e&&(e=this.IndexByName(e||"")),--e):e=-1,e},indexOf:function(e){for(var t=this.items,r=t.length;r--;)if(e===t[r])return r;return-1},decodeItems:function(e){var t,r=this,o=[];return e&&(t=e.split(";"),t.forEach(function(e){var t;e=e.trim(),t=r.itemByName(e),t&&o.push(t)})),o},forEach:function(e){this.items.forEach(e)},_doloadItem:function(e){this.Add().loadFromJSON(e)},get Count(){return this.items.length},Item:function(e){var t=this;return t.items[t.indexOfCOMIndex(e)]},Add:function(){var e=this,t=e._createItem();return e.items.push(t),t},Remove:function(e){var t=this;e=t.indexOfCOMIndex(e),e>=0&&t.items.splice(e,1)},RemoveAll:function(){this.items=[]},IndexByName:function(e){var t=this.items,r=t.length;if(e)for(;r--;)if(t[r].Name===e)return r+1;return-1},ItemAt:function(e){var t=this.items;return e--,e>=0&&e0&&e--<=len)if(o=r[e],t>e){do r[e]=r[e+1];while(++et);r[t]=o}}};var Controls=function(e){Collection.call(this,e)};Controls.prototype={_doloadItem:function(e){var t=this,r=t.Add(enumName2Value(grenum.ControlType,e[t.getJsonMember("Type")]));r&&r.loadFromJSON(e)},assign:function(e){var t=this;t.RemoveAll(),e.forEach(function(e){t.Add(e.ControlType).assign(e)})},layout:function(){for(var e=this,t=e.items,r=e.owner.getRect(),o=0,n=t.length;n>o;)t[o++].layout(r)},generate:function(e){for(var t,r=this.items,o=r.length;o--;)t=r[o],t.Visible&&t.generate(e)},Add:function(e){function t(e,t){var r=undefined;switch(e){case 1:r=new StaticBox(t);break;case 2:r=new ShapeBox(t);break;case 10:r=new Line(t);break;case 3:r=new SystemVarBox(t);break;case 4:r=new FieldBox(t);break;case 5:r=new SummaryBox(t);break;case 7:r=new PictureBox(t);break;case 8:r=new MemoBox(t);break;case 9:r=new SubReport(t);break;case 11:r=new Chart(t);break;case 12:r=new Barcode(t);break;case 13:r=new FreeGrid(t);break;case 14:r=new CharGridBox(t);break;case 6:r=new RichTextBox(t)}return r}var r=this,o=t(e,r.owner);return o&&r.items.push(o),o}},prototypeLinkExtend(Controls,Collection);var Columns=function(e){Collection.call(this,e)};Columns.prototype={_doloadItem:function(e){var t=this,r=new Column(t.owner);r.loadFromJSON(e),t.items.push(r)},addTo:function(e){var t,r,o=this,n=o.owner.ColumnContent.ContentCells,i=new Column(o.owner);return o.items.push(i),t=new ColumnTitleCell(e,!1),e.items.push(t),i.TitleCell=t,t.Column=i,r=new ColumnContentCell(n),n.items.push(r),i.ContentCell=r,r.Column=i,i},Add:function(){var e=this;return e.addTo(e.owner.ColumnTitle.TitleCells)},Remove:function(e){function t(e,r){var o,n,i=e.indexOf(e);if(i>=0)return e.splice(i,1),1;for(i=e.length,n=0;i>n;n++)if(o=e[n],o.GroupTitle&&t(o.SubTitles.items,r))return 1;return 0}var r,o=this,n=o.owner,i=n.ColumnContent.ContentCells.items;e=o.indexOfCOMIndex(e),e>=0&&(r=o.items[e],i.splice(i.indexOf(r.ContentCell),1),t(n.ColumnTitle.TitleCells.items,r.TitleCell),o.items.splice(e,1))},RemoveAll:function(){var e=this,t=e.owner;e.items=[],t.ColumnContent.ContentCells.items=[],t.ColumnTitle.TitleCells.items=[]}},prototypeLinkExtend(Columns,Collection);var ColumnTitleCells=function(e,t){var r=this;Collection.call(r,e),r.supcell=t};ColumnTitleCells.prototype={_doloadItem:function(e){var t=this,r=new ColumnTitleCell(t,e[t.getJsonMember("GroupTitle")]);r.loadFromJSON(e),t.items.push(r)},assign:function(e){var t=this;t.items=[],e.forEach(function(e){var r=new ColumnTitleCell(t,e.GroupTitle);t.items.push(r),r.assign(e),e.GroupTitle&&r.SubTitles.assign(e.SubTitles)})},AddGroup:function(e,t){var r=this,o=new ColumnTitleCell(r,!0);return o.Name=e,o.Text=t,r.items.push(o),o},RemoveGroup:function(e){var t=this,r=t.Item(e);r&&r.GroupTitle&&(Collection.prototype.Remove.call(t,e),t.items=t.items.concat(r.SubTitles.items))},Add:function(){},Remove:function(e){},RemoveAll:function(){}},prototypeLinkExtend(ColumnTitleCells,Collection);var ColumnContentCells=function(e){Collection.call(this,e)};ColumnContentCells.prototype={_doloadItem:function(e){var t=this,r=new ColumnContentCell(t);r.loadFromJSON(e),t.items.push(r)},assign:function(e){var t=this;t.items=[],e.forEach(function(e){var r=new ColumnContentCell(t);t.items.push(r),r.assign(e)})},Add:function(){},ChangeItemOrder:function(e,t){},Remove:function(e){},RemoveAll:function(){}},prototypeLinkExtend(ColumnContentCells,Collection);var Fields=function(e){Collection.call(this,e)};Fields.prototype._createItem=function(){return new Field(this.owner)},prototypeLinkExtend(Fields,Collection);var Groups=function(e){Collection.call(this,e)};Groups.prototype={_createItem:function(){return new Group(this.owner)},_doloadItem:function(e){this.Add().loadFromJSON(e)}},prototypeLinkExtend(Groups,Collection);var ReportHeaders=function(e){Collection.call(this,e)};ReportHeaders.prototype._createItem=function(){return new ReportHeader(this.owner)},prototypeLinkExtend(ReportHeaders,Collection);var ReportFooters=function(e){Collection.call(this,e)};ReportFooters.prototype._createItem=function(){return new ReportFooter(this.owner)},prototypeLinkExtend(ReportFooters,Collection);var Parameters=function(e){Collection.call(this,e)};Parameters.prototype._createItem=function(){return new Parameter(this.owner)},prototypeLinkExtend(Parameters,Collection);var Object=function(e){var t=this;t.owner=e,t.report=e.report,t.Tag=""};Object.prototype={afterLoad:function(e){},loadFromJSON:function(e){var t=this;e&&(assignJSONMembers(t,e),t.afterLoad(e))},assign:function(e){var t,r,o=this,n=o.Font,i=e.onclick,a=e.ondblclick;for(var l in e)o.hasOwnProperty(l)&&(t=e[l],r=typeof t,"object"!==r&&"array"!==r&&(o[l]=t));n&&n.assign(e.Font),i&&(o.onclick=i),a&&(o.ondblclick=a),o.children&&o.children.forEach(function(t){var r=e[t];r&&(r.owner?o[t].assign(r):assignObject(o[t],r))})},registerEventClass:function(){var e=this,t=e.report.viewer._domevents,r=e.onclick,o=e.ondblclick;r&&!e.onclickClassID&&(t.push({obj:e,type:1,fun:r}),e.onclickClassID=t.length),o&&!e.ondblclickClassID&&(t.push({obj:e,type:2,fun:o}),e.ondblclickClassID=t.length)},resetEventClass:function(){var e=this;e.onclickClassID&&delete e.onclickClassID,e.ondblclickClassID&&delete e.ondblclickClassID},addElementEventClass:function(e){var t=this,r=t.report.viewer,o=t.onclickClassID,n=t.ondblclickClassID;o&&e.addClass(r._getEventCSSName(o)),n&&e.addClass(r._getEventCSSName(n))},getUsingFont:function(){return this.Font.UsingFont()},getJsonMember:function(e){return this.report.isWR?wrPropNameEncode(e):e},toFillBack:function(){var e=this;return e.BackColor!==e.owner.BackColor||e.fixed},getRunningNo:function(){var e=this,t=e.report.ownerSR,r=t?1e4*t.getRunningNo():0;return r+e._runningNo()},_runningNo:function(){return 0}};var CellBase=function(e){var t=this;Object.call(t,e),t._borderCustom=!1,t.BackColor=e.BackColor,t.CanGrow=!1,t.CanShrink=!1,t.Font=new FontWrapper(e.Font),t._freeCell=!0,t.setFreeCell(!1)};CellBase.prototype={loadFromJSON:function(e){var t=this;e&&(t.setFreeCell(!!e[t.getJsonMember("FreeCell")]),assignJSONMembers(t,e),t.afterLoad(e))},afterLoad:function(e){var t=this,r=t.report,o=r.viewer.alpha,n=r.isWR;colorMemberValid(t,"BackColor",o.background),colorMemberValid(t,"ForeColor",o.text),t.Font.loadFromJSON(e.Font,n),t.setBorderCustom(e[t.getJsonMember("BorderCustom")]),t._borderCustom&&t.Border.loadFromJSON(e.Border,o.border,n),t.FreeCell?t.Controls.loadFromJSON(e.Control):t.TextFormat.loadFromJSON(e,n)},assign:function(e){var t,r=this;r.setFreeCell(e.FreeCell),r.setBorderCustom(e.BorderCustom),Object.prototype.assign.call(r,e),r.Font.assign(e.Font),e.Border&&assignObject(r.Border,e.Border),e.FreeCell?(r.Controls.assign(e.Controls),r.isSingleDockControl()&&(t=r.Controls.items[0],!t.onclick&&r.onclick&&(t.onclick=r.onclick),!t.ondblclick&&r.ondblclick&&(t.ondblclick=r.ondblclick))):assignObject(r.TextFormat,e.TextFormat)},attachData:function(){var e=this;e.FreeCell?e.Controls.attachData():e._getWrapperClass().attachData.call(e)},prepare:function(){var e,t=this,r=t.report.viewer,o=t.Controls,n=t.Font.font;t.registerEventClass(),t.FreeCell&&(o.layout(),t.isSingleDockControl()||2===t.report.controlPosition||(e=t.getRect(),Section.prototype.buildTableLayout.call(t,e.Width(),e.Height())),o.prepare(),t.isSingleDockControl()&&(e=o.items[0],!e.onclick&&t.onclick&&(e.onclick=t.onclick,e.onclickClassID=t.onclickClassID),!e.ondblclick&&t.ondblclick&&(e.ondblclick=t.ondblclick,e.ondblclickClassID=t.ondblclickClassID))),n&&(t.defaultFontStyle=r.selectFontItem(n)),t.tableRows&&t.tableRows.forEach(function(e){e.defaultStyle=r.selectSectionItem(e)}),t.defaultStyle=r.selectCellItem(t)},unprepare:function(){var e=this;e.FreeCell?e.Controls.unprepare():e._getWrapperClass().unprepare.call(e),e.resetEventClass(),delete e.defaultFontStyle,delete e.defaultStyle},doGenerate:function(e){var t=this,r=t.report.viewer,o=t.Controls,n=t.Font.font,i=t.defaultFontStyle;e.addClass(r.selectCell(t)),n&&i&&e.addClass(r.selectFont(n,i)),t.addStyles&&t.addStyles(e),t.FreeCell?t.isSingleDockControl()?o.items[0].generateInCell(e):(t.addElementEventClass(e),t.tableRows?(e.addStyle("padding","0px"),e=new HtmlElement("table",e),e.addStyle("border-collapse","collapse"),Section.prototype.generateTableRows.call(t,e)):(e.addStyle("position","relative"),o.generate(e))):(t.addElementEventClass(e),TextBox.prototype.generateNormal.call(t,e))},setFreeCell:function(e){var t,r=this;r._freeCell!==e&&(r._freeCell=e,e?(r.Controls=new Controls(r),delete r.ControlType):(t=r._getWrapperClass(),t.createWrapper.call(r),r.ControlType=t.ControlType,delete r.Controls))},setBorderCustom:function(e){var t=this;t._borderCustom!==e&&(t._borderCustom=e,e?t.Border=new Border(12):delete t.Border)},setForeColor:function(e){var t=this;t.FreeCell?t.Controls.forEach(function(t){t.ForeColor=e}):t.ForeColor=e},getDisplayText:function(){var e=this;return e._getWrapperClass().getDisplayText.call(e)},setDisplayText:function(e){var t=this;return t._getWrapperClass().setDisplayText.call(t,e)},getControls:function(){var e=this;return e.FreeCell?e.Controls.items:[e]},isSingleDockControl:function(){var e=this.Controls.items;return 1===e.length&&5===e[0].Dock},isControlPositionClass:function(){return 0},_getDisplayText:function(){var e=this;return e._getWrapperClass()._getDisplayText.call(e)},get AsTextBox(){var e=this;return e._freeCell?undefined:e}},prototypeCopyExtend(CellBase,Object);var FreeGridCell=function(e,t,r){var o=this;CellBase.call(o,e),o.row=t,o.col=r,o.ColSpan=1,o.RowSpan=1,o.Text="",o.DataName=""};FreeGridCell.prototype={generate:function(e){var t=this;e=new HtmlElement("td",e),t.ColSpan>1&&e.addAttribute("colspan",t.ColSpan+""),t.RowSpan>1&&e.addAttribute("rowspan",t.RowSpan+""),t.doGenerate(e)},ownerGrid:function(){return this.owner},_getWrapperClass:function(){return MemoBox.prototype},getRect:function(){for(var e=this,t=e.owner,r=e.col,o=e.row,n=r+e.ColSpan,i=o+e.RowSpan,a=0,l=0;n>r;)a+=t.columns[r++].pxWidth;for(;i>o;)l+=t.rows[o++].pxHeight;return new Rect(0,0,a,l)},_runningNo:function(){return this.owner.owner._runningNo()},get FreeCell(){return!!this._freeCell},set FreeCell(e){this.setFreeCell(e)},get BorderCustom(){return!!this._borderCustom},set BorderCustom(e){this.setBorderCustom(e)},get DisplayText(){var e=this;return e.FreeCell?"":e.getDisplayText()},set DisplayText(e){var t=this;!t.FreeCell&&t.setDisplayText(e)}},prototypeCopyExtend(FreeGridCell,CellBase);var ColumnCell=function(e){var t=this;CellBase.call(t,e),t.Column=""};ColumnCell.prototype={afterLoad:function(e){var t=this;CellBase.prototype.afterLoad.call(t,e),t.Column=t.owner.owner.Columns.itemByName(t.Column)},prepare:function(){var e=this;CellBase.prototype.prepare.call(e),e.CanShrink&&(e.owner.CanShrink=1)},ownerGrid:function(){return this.owner.owner}},prototypeCopyExtend(ColumnCell,CellBase);var ColumnContentCell=function(e){var t=this;ColumnCell.call(t,e.owner),t.DataField=""};ColumnContentCell.prototype={_getWrapperClass:function(){return FieldBox.prototype},afterLoad:function(e){var t=this;ColumnCell.prototype.afterLoad.call(t,e),t.Column.ContentCell=t},generate:function(e){var t=this,r=t.Column,o=r._crossOrderNo;e=new HtmlElement("td",e),o&&e.addAttribute("_grcrossno",o),t.fixed&&t.ownerGrid().hasMergedColumn&&e.addAttribute("id",r.idMGC),t.doGenerate(e)},generateMerge:function(e,t){var r=this,o=r.report.viewer,n=r.Font.font,i=r.Column._crossOrderNo;e=new HtmlElement("td",e),i&&e.addAttribute("_grcrossno",i),e.addClass(o.selectCell(r)),n&&e.addClass(o.selectFont(n,r.defaultFontStyle)),t.generateInCell(e)},isControlPositionClass:function(){return this.FreeCell},get Name(){return this.Column.Name},set Name(e){this.Column.Name=e},get FreeCell(){return!!this._freeCell},set FreeCell(e){this.setFreeCell(e)},get BorderCustom(){return!!this._borderCustom},set BorderCustom(e){this.setBorderCustom(e)},get DisplayText(){var e=this;return e.FreeCell?"":e.getDisplayText()},set DisplayText(e){var t=this;!t.FreeCell&&t.setDisplayText(e)},getRect:function(){var e=this;return new Rect(0,0,e.Column.pxWidth,e.owner.pxHeight)},_runningNo:function(){return this.ownerGrid().Recordset.RecordNo+1}},prototypeCopyExtend(ColumnContentCell,ColumnCell);var ColumnTitleCell=function(e,t){var r=this;ColumnCell.call(r,e.owner),r.SupCell=e.supcell,r.Visible=!0,r.GroupTitle=t,r.Text="",r.Height=0,t&&(r._name="",r.SubTitles=new ColumnTitleCells(r.owner,r))};ColumnTitleCell.prototype={_getWrapperClass:function(){return MemoBox.prototype},afterLoad:function(e){var t=this;ColumnCell.prototype.afterLoad.call(t,e),t.GroupTitle?(t._name=e[t.getJsonMember("Name")],t.SubTitles.loadFromJSON(e.ColumnTitleCell)):t.Column.TitleCell=t},prepare:function(){var e=this;ColumnCell.prototype.prepare.call(e),e.SubTitles&&e.SubTitles.prepare()},generate:function(e){e=new HtmlElement("th",e),this.doGenerate(e)},addStyles:function(e){var t=this,r=t.GroupTitle?1:t.owner.layerCount-t.owner.generatingLayer;t.colspan>1&&e.addAttribute("colspan",t.colspan+""),r>1&&e.addAttribute("rowspan",r+"")},getRect:function(){var e=this,t=e.owner.layerCount-e.layer,r=e.Column,o=0;return e.GroupTitle?(t=1,r=e.findFirstColumn(),r&&(o=e.findLastColumn().pxRight-r.pxLeft)):o=r.pxWidth,new Rect(0,0,o,e.owner.pxHeight*t)},toFillBack:function(){var e=this;return e.report.fixed?1:Object.prototype.toFillBack.call(e)},findFirstColumn:function(){var e,t=this,r=t.Column;return t.GroupTitle&&(e=t.SubTitles.items,r=e.length>0?e[0].findFirstColumn():undefined),r},findLastColumn:function(){var e,t=this,r=t.Column;return t.GroupTitle&&(e=t.SubTitles.items,r=e.length>0?e[e.length-1].findLastColumn():undefined),r},encloseColumnObject:function(e){var t=this;t.owner.owner._ColumnMoveTo(e,t)},get FreeCell(){return!!this._freeCell},set FreeCell(e){this.setFreeCell(e)},get BorderCustom(){return!!this._borderCustom},set BorderCustom(e){this.setBorderCustom(e)},get DisplayText(){var e=this;return e.FreeCell?"":e.getDisplayText()},set DisplayText(e){var t=this;!t.FreeCell&&t.setDisplayText(e)},get Name(){var e=this;return e.GroupTitle?e._name:e.Column.Name},set Name(e){var t=this;t.GroupTitle?t._name=e:t.Column.Name=e},EncloseColumn:function(e){var t=this,r=t.owner.owner,o=r.Columns.Item(e);o&&r._ColumnMoveTo(o,t)},AddSubGroupTitle:function(e,t){return this.SubTitles.AddGroup(e,t)}},prototypeCopyExtend(ColumnTitleCell,ColumnCell);var Control=function(e){var t=this;Object.call(t,e),t.Left=0,t.Top=0,t.Width=0,t.Height=0,t.Anchor=3,t.Dock=0,t.Center=0,t.AlignColumnSide=3,t.AlignColumn="",t.AlignColumnEx="",t.Locked=!1,t.ShiftMode=1,t.PaddingLeft=0,t.PaddingRight=0,t.PaddingTop=0,t.PaddingBottom=0,t.BackColor=e.BackColor,t.BackStyle=2,t.ForeColor=e.ForeColor?e.ForeColor:colorAlpha(0,t.report.viewer.alpha.text),t.Visible=!0,t.CustomDraw=!1,t.Border=new Border(0),t.Font=new FontWrapper(e.Font),t.Name="",t.CustomDrawScript=""};Control.prototype={children:["Border"],attachData:function(){},afterLoad:function(e){var t=this,r=t.report.isWR,o=t.report.viewer.alpha;enumMemberValid(t,"Dock",grenum.DockStyle),enumMemberValid(t,"Center",grenum.CenterStyle),enumMemberValid(t,"AlignColumnSide",grenum.AlignColumnSideStyle),enumMemberValid(t,"ShiftMode",grenum.ShiftMode),enumMemberValid(t,"BackStyle",grenum.BackStyle),enumBitMemberValid(t,"Anchor",grenum.AnchorStyle),colorMemberValid(t,"BackColor",o.background),colorMemberValid(t,"ForeColor",o.text),t.Font.loadFromJSON(e.Font,r),t.Border.loadFromJSON(e.Border,o.border,r)},layout:function(e){var t,r=this,o=r.report,n=o.size2Pixel(r.Left),i=o.size2Pixel(r.Top),a=o.size2Pixel(r.Width),l=o.size2Pixel(r.Height),s=new Rect(n,i,n+a,i+l),c=o.getRunningColumn(r.AlignColumn),u=o.getRunningColumn(r.AlignColumnEx);if(c&&!c.Visible&&(c=undefined),u&&!u.Visible&&(u=undefined),c||u)switch(c&&u?u.pxLefto;)if(e=t[o]," "===e&&r-1>o&&" "===t[o+1])for(;r>o&&" "===t[o];)n+=" ",o++;else n+=e,o++}var r=this,o="
",n=r.getDisplayText();r.TextFormat.HtmlTags||(/[\r\n&<>"]/.test(n)&&(n=n.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/\r\n/g,o).replace(/\r/g,o).replace(/\n/g,o)),/ /.test(n)&&t()),e.innerText=n},getDisplayText:function(){var e=this;return e.displayTextAssigned=!1,e.doingGetDisplayText||(e.doingGetDisplayText=!0,e.report.fireTextBoxGetDisplayText(e),e.doingGetDisplayText=!1),e.displayTextAssigned?e.assignedDisplayText:e._getDisplayText()},setDisplayText:function(e){var t=this;t.assignedDisplayText=e,t.displayTextAssigned=!0},DrawDefault:function(){var e=this,t=e.getDisplayText(),r=e.canvas;e.report.Graphics.DrawFormatText(t,0,0,r.width,r.height,e.TextFormat)}},prototypeCopyExtend(TextBox,Control);var StaticBox=function(e){var t=this;TextBox.call(t,e), +t.Text="",t.Parameter=""};StaticBox.prototype={ControlType:1,_getDisplayText:function(){var e=this;return e.valueField?e.valueField.DisplayText:e.oParameter?e.oParameter.DisplayText:e.Text},attachData:function(){var e=this;e.Parameter&&(e.oParameter=e.report.ParameterByName(e.Parameter))},unprepare:function(){var e=this;TextBox.prototype.unprepare.call(e),delete e.oParameter},get DisplayText(){return this.getDisplayText()},set DisplayText(e){this.setDisplayText(e)},get AsStaticBox(){return this}},prototypeCopyExtend(StaticBox,TextBox);var MemoBox=function(e){var t=this;TextBox.call(t,e),t.Text=""};MemoBox.prototype={ControlType:8,createWrapper:function(){var e=this;TextBox.prototype.createWrapper.call(e),e.Text=""},_getDisplayText:function(){return this.textBuilder.generateDisplayText()},attachData:function(){var e=this;e.textBuilder=new TextBuilder(e.report,e.Text)},getGroupVars:function(){function e(r){r.varItems&&r.varItems.forEach(function(r){var o=r.varField,n=o.type;4!==n&&2!==n&&5!==n||t.push(o),4===n?o.paramExp&&e(o.paramExp):5===n&&o.paramExps.forEach(e)})}var t=[];return this.textBuilder.forEach(e),t},isPureNumericExpression:function(){var e=this;return e.prepare(),e.textBuilder&&1===e.textBuilder.items.length&&e.textBuilder.items[0].isPureNumeric()},get DisplayText(){return this.getDisplayText()},set DisplayText(e){this.setDisplayText(e)},get AsFloat(){var e=this.textBuilder.items[0],t=e&&e.__proto__.hasOwnProperty("calculate")?e.calculate():0;return isNaN(t)?0:t},get Value(){return this.getDisplayText()},get AsMemoBox(){return this}},prototypeCopyExtend(MemoBox,TextBox);var FieldBox=function(e){var t=this;TextBox.call(t,e),t.DataField=""};FieldBox.prototype={ControlType:4,createWrapper:function(){var e=this;TextBox.prototype.createWrapper.call(e),e.DataField=""},attachData:function(){var e=this;e.oDataField=e.report.RunningFieldByName(e.DataField)},unprepare:function(){var e=this;TextBox.prototype.unprepare.call(e),delete e.oDataField},_getDisplayText:function(){var e=this.oDataField;return e?e.DisplayText:""},get DisplayText(){return this.getDisplayText()},set DisplayText(e){this.setDisplayText(e)},get AsFieldBox(){return this}},prototypeCopyExtend(FieldBox,TextBox);var SummaryBox=function(e){var t=this;TextBox.call(t,e),t.SummaryFun=1,t.RankNo=1,t.DataField="",t.DisplayField="",t.Format=""};SummaryBox.prototype={ControlType:5,afterLoad:function(e){var t=this;TextBox.prototype.afterLoad.call(t,e),enumMemberValid(t,"SummaryFun",grenum.SummaryFun)},attachData:function(){var e,t,r=this,o=r.Format;r.init(r.DataField,r.DisplayField),o||(e=r.report.RunningFieldByName(r.DataField),e&&(o=e.Format)),t=r.paramExp,t&&1===t.varItems.length&&1===t.varItems[0].type&&6===t.varItems[0].object.FieldType?r.formater=new DateTimeFormatter(o):r.formater=new NumericFormatter(o)},_getDisplayText:function(){var e,t=this,r=t.SummaryFun,o=t.valueField;if(18===r||19===r||29===r||30===r||t.displayField)e=o?o.DisplayText:t.value;else{if(o){if(o.IsNull)return"";e=o.AsFloat}else e=t.value;e=t.formater.format(e)}return e},get DisplayText(){return this.getDisplayText()},set DisplayText(e){this.setDisplayText(e)},get Value(){return this.getValue()},set Value(e){this.setValue(e)},get AsSummaryBox(){return this}},prototypeCopyExtend(SummaryBox,TextBox),prototypeCopyExtend(SummaryBox,SummaryItem);var SystemVarBox=function(e){var t=this;TextBox.call(t,e),t.SystemVar=-1,t.GroupIndex=1,t.Format=""};SystemVarBox.prototype={ControlType:3,afterLoad:function(e){var t=this;TextBox.prototype.afterLoad.call(t,e),enumMemberValid(t,"SystemVar",grenum.SystemVarType)},attachData:function(){var e=this,t=e.Format;1===e.SystemVar?e.formater=new DateTimeFormatter(t):e.formater=new NumericFormatter(t)},_getDisplayText:function(){var e=this,t=e.SystemVar;return t>0?e.formater.format(e.report.SystemVarValue2(t,e.GroupIndex)):""},get DisplayText(){return this.getDisplayText()},set DisplayText(e){this.setDisplayText(e)},get AsSystemVarBox(){return this}},prototypeCopyExtend(SystemVarBox,TextBox);var CanvasBox=function(e){Control.call(this,e)};CanvasBox.prototype={generateContent:function(e){this.generateCanvas(e)},drawCanvas:function(e){var t=this;t.draw(e),t.CustomDraw&&Control.prototype.drawCanvas.call(t,e)},DrawDefault:function(){this.draw()}},prototypeCopyExtend(CanvasBox,Control);var Line=function(e){var t=this;CanvasBox.call(t,e),t.L2R=!0,t.U2D=!0,t.LinePen=new Pen};Line.prototype={ControlType:10,children:["LinePen"],afterLoad:function(e){var t=this,r=t.report,o=r.PixelsToUnit(t.LinePen.Width);CanvasBox.prototype.afterLoad.call(t,e),t.LinePen.loadFromJSON(e.Pen,r.viewer.alpha.stroke,r.isWR),t.Height"==T.substr(0,6)){for(e=7,t=T.length;t>e&&"<"!=T.charAt(e);)++e;t>e&&""==T.substr(e,7)&&(r=T.substr(7,e-7),v.IsIcon=1,v.IconIndex=+r,T=T.substr(e+7))}v.IsIcon||(v.Char=T.charAt(0),T=T.substr(1))}function n(){var e,t,r;e=1.25*F.usingFont.Point,t=(i.Width()-e)/2,r=i.clone(),t>0&&(r.left+=t,r.right-=t),v.IconIndex<0}var i,a,l=this,s=l.report,c=l.canvas,u=s.UnitToPixels(l.GridWidth),d=s.UnitToPixels(l.GridSpacing),f=l.GridCount,p=l.ClassPos,h=l.GridLinePen,g=l.ClassLinePen,m=c.width,C=c.height,b=new Rect(0,0,m,C),x=b.clone(),v={},S=[],T=l.tbText.generateDisplayText(),y=l.tbLabel.generateDisplayText(),F=new Context(c.getContext("2d"));if(F.selectFont(l.getUsingFont()),F.selectPen(h),0==u&&(u=f>0?(b.Width()-d*(f-1))/f:F.usingFont.LineWidth),y&&r(),0>=f&&(f=(x.Width()+d+u/4)/(u+d)),h.Width>0&&f>0){for(F.beginPath(),i=x.clone(),a=0;f>a;++a){if(i.left=i.right-u,d>0){if(y?(F.moveTo(i.left,i.top),F.lineTo(i.left,i.bottom),f-1>a&&(F.moveTo(i.left+d,i.top),F.lineTo(i.left+d,i.bottom))):F.rectangle2(i),i.lefta&&a+1!=p&&(F.moveTo(i.left,i.top),F.lineTo(i.left,i.bottom))}i.right-=u+d}F.stroke()}for(0==d&&p>0&&g.Width>0&&(F.selectPen(g),a=x.right-u*p,F.moveTo(a,i.top),F.lineTo(a,i.bottom),F.restorePen());T;)v={},o(),S.push(v);for(S.length>f&&(S.length=f),i=x.clone(),a=S.length;--a>=0&&(v=S[a],i.left=i.right-u,!(i.left+t(u)m?u=m:m=u),d=u*w,f=m*D,I=(s-d)/2,k=(c-f)/2,B||(g=I,I=k,k=g),W.left+=I,W.right=W.left+d,W.top+=k,W.bottom=W.top+f,y){case 1:l=C;break;case 2:l=x;break;case 3:l=F;break;default:l=A}for(h=W.clone(),l.init(),R=P.length,g=0,t=0;D>t;++t){for(r=o(P[g++])<<4|o(P[g++]),n=128,p=r&n?S:v,i=1,n>>=1,e=1;w>e;++e)0==e%8&&(r=o(P[g++])<<4|o(P[g++]),n=128),a=r&n?S:v,p===a?++i:(l.draw(),p=a,i=1),n>>=1;l.end()}}function i(){switch(T){case 15:M=15,O=0;break;case 16:case 17:case 18:M=12,O=12}}function a(){function e(){var e=m.BarRatio;switch(T){case 1:case 2:case 4:case 14:case 15:case 5:case 13:case 16:case 17:case 18:case 19:case 20:2>e&&(e=2),e>3&&(e=3);break;case 3:2.25>e&&(e=2.25),e>3&&(e=3)}return[1,e,1.5*e,2*e]}function t(e){var t=o(e);return{w:p[t%4],lt:Math.floor(t/4)}}function r(e){for(var r=e.length,o=0;r-- >0;)o+=t(e[r]).w;return o}var n,i,a,l,f=m.Alignment,p=e(),C=r(P),i=m.BarWidth,x=G/A.length/10,F=2*c/5,w=E?c+2*L/3:c;if(_=x*M,V=x*O,u=i?report.UnitToPixels(i):(s-_-V)/C,d=u*C+_+V,H=d>s+1,!H){switch(f){case 1:I=_;break;case 2:I=(s-d)/2+_;break;default:I=s-d+_}switch(h=W,y){case 1:h.left+=I;break;case 2:h.right-=I;break;case 3:h.top+=I;break;default:h.bottom-=I}for(g=0;R>g;++g)switch(n=t(P[g]),i=n.w*u,a=2===n.lt?F:3==n.lt?w:c,l=0==n.lt?v:S,y){case 1:h.right=h.left+i,h.bottom=h.top+a,b.fillRect2(h,l),h.left=h.right;break;case 2:h.left=h.right-i,h.bottom=h.top+a,b.fillRect2(h,l),h.right=h.left;break;case 3:h.bottom=h.top+i,h.left=h.right-a,b.fillRect2(h,l),h.top=h.bottom;break;default:h.top=h.bottom-i,h.left=h.right-a,b.fillRect2(h,l),h.bottom=h.top}}}function l(){var e,t,r,o,n=m.CaptionAlignment,i=1===n?33:2===n?34:36,a=m.Alignment,l=U.left+I;if(B?(3===F&&(k=-k),U.top+=k,U.bottom+=k):(2===F&&(I=-I),U.left+=I,U.right+=I),E)switch(g=0,T){case 15:l-=_,b.drawText(A.substr(g++,1),l,U.top),l=U.left+I+4*u,o=l+42*u,b.drawEqualSpaceText(A.substr(g,6),l,o,U.top),g+=6,l=U.left+I+50*u,o=l+41*u,b.drawEqualSpaceText(A.substr(g,6),l,o,U.top);break;case 14:l+=4*u,o=l+28*u,b.drawEqualSpaceText(A.substr(g,4),l,o,U.top),g+=4,l=U.left+I+36*u,o=l+27*u,b.drawEqualSpaceText(A.substr(g,4),l,o,U.top);break;case 17:case 18:b.fontPointTo(Math.floor(.77*b.usingFont.Point)),L=fontHeight(b.usingFont),G=b.measureTextWidth("0"),l-=_,b.drawText("0",l,U.bottom-L),e=A.substr(6),G=b.measureTextWidth(e),l=U.right-I+V-G,b.drawText(e,l,U.bottom-L),b.restoreFont(),l=U.left+I+4*u,o=l+42*u,b.drawEqualSpaceText(A.substr(0,6),l,o,U.top);break;case 16:b.fontSizeTo(Math.floor(.77*b.usingFont.Point)),L=fontHeight(b.usingFont),e=A.substr(0,1),G=b.measureTextWidth(e),l-=_,b.drawText(e,l,U.bottom-L),e=A.substr(11),G=b.measureTextWidth(e),l=U.left+I+95*u+Math.max(V-G,2),b.drawText(e,l,U.bottom-L),b.restoreFont(),l=U.left+I+11*u,o=l+35*u,++g,b.drawEqualSpaceText(A.substr(g,5),l,o,U.top),l=o+5*u,o=l+35*u,g+=5,b.drawEqualSpaceText(A.substr(g,5),l,o,U.top)}else if(B){switch(a){case 1:U.right=U.left+d;break;case 2:U.left=(U.left+U.right-d)/2,U.right=U.left+d;break;default:U.left=U.right-d}b.drawTextAlign2(A,U,i)}else{switch(a){case 1:U.bottom=U.top+d;break;case 2:U.top=(U.top+U.bottom-d)/2,U.bottom=U.top+d;break;default:U.top=U.bottom-d}switch(t=U.right,n){case 1:r=U.top;break;case 2:r=(U.top+U.bottom-G)/2;break;default:r=U.bottom-G}b.drawTextRotate(A,t,r,270)}}var s,c,u,d,f,p,h,g,m=this,C=m.canvas,b=new Context(C.getContext("2d")),x=m.getUsingFont(),v=m.BackColor,S=m.ForeColor,T=m.BarcodeType,y=m.Direction,F=m.CaptionPosition,w=e.cols,D=e.rows,P=e.graph,R=P?P.length:0,A=e.text?e.text:m.DisplayText,t=t(T),B=1===y||2===y,E=r(T)&&1===y&&3===F,G=0,L=fontHeight(x),N=t?Math.max(L,10):0,M=0,O=0,_=0,V=0,H=!1,I=0,k=0,j=new Rect(0,0,C.width,C.height),W=j.clone(),U=j.clone();if(w){if(b.selectFont(x),1!==F&&(B?2===F?(U.bottom=U.top+L,W.top=U.bottom+N):(U.top=U.bottom-L,W.bottom=U.top-N):2===F?(U.left=U.right-L,W.right=U.left-N):(U.right=U.left+L,W.left=U.right+N)),s=B?W.Width():W.Height(),c=B?W.Height():W.Width(),10>s||10>c)return void b.restoreFont();1!=F&&(G=b.measureTextWidth(A)),t?n():(i(),a()),H?pdd.drawTextCenter("条宽度太大,条码无法完整显示",(j.left+j.right)/2,(j.top+j.bottom-L)/2):1!=F&&l(),b.restoreFont()}},get Value(){return this.getDisplayText()},get DisplayText(){return this.getDisplayText()},get AsBarcode(){return this}},prototypeCopyExtend(Barcode,CanvasBox);var PictureBox=function(e){var t=this;Control.call(t,e),t.Alignment=3,t.SizeMode=3,t.TransparentMode=0,t.RotateMode=0,t.ImageIndex=0,t.Picture="",t.ImageFile="",t.DataField=""};PictureBox.prototype={ControlType:7,afterLoad:function(e){var t=this;Control.prototype.afterLoad.call(t,e),enumMemberValid(t,"SizeMode",grenum.PictureSizeMode),enumMemberValid(t,"TransparentMode",grenum.PictureTransparentMode),enumMemberValid(t,"RotateMode",grenum.PictureRotateMode)},attachData:function(){var e=this;e.oDataField=e.report.RunningFieldByName(e.DataField)},generateNormal:function(e){var t,r,o,n,i,a,l,s,c,u,d=this,f=d.Picture,p=d.oDataField,h=d.ImageFiles,g=h?h[d.getOwnerDetailGrid().Recordset.RecordNo]:d.ImageFile,m=d.ImageIndex,C=d.SizeMode;if(f?i="data:image;base64,"+f:p?(i=p.AsString,a=i.length,a>256?i="data:image;base64,"+i:5>a&&(i=p.AsInteger)):g?i=g:m&&(i=m),i&&!isNaN(i)&&i>0&&(t=d.report._imageList,i=t&&i<=t.Count?"data:image;base64,"+t.Item(i):undefined),i)if(isNaN(i))if(5===C)e.addStyle("background-image","url("+i+")");else{if(r=d.pxRect,r||(r=d.owner.getRect()),o=r.Width(),n=r.Height(),e.addStyle("text-align","center"),e.addStyle("vertical-align","middle"),l=new HtmlElement("img",e),l.skipend=1,2===d.SizeMode)c=o,u=n;else try{s=document.createElement("img"),s.src=i,s.width&&s.height?3===d.SizeMode||s.width>o||s.height>n?s.width*n>o*s.height?c=o:u=n:s.width&&s.height?(c=s.width,u=s.height):(c=o,u=n):(c=o,u=n)}catch(b){c=o,u=n}c&&l.addAttribute("width",Math.round(c)),u&&l.addAttribute("height",Math.round(u)),l.addAttribute("src",i)}else i=+i,e.addStyle("text-align","center"),e.addStyle("vertical-align","middle"),l=new HtmlElement("input",e),l.skipend=1,l.addAttribute("type","checkbox"),l.addAttribute("id",d.getCheckBoxID()),l.addClass(grconst.CSS_CB),-1!==i&&-3!==i||l.addAttribute("checked"),p&&l.addAttribute(grconst.ATTR_FIELD,p.Name),d.report._has_cb=1},getCheckBoxID:function(){var e,t=this,r=t.owner.owner;return e=r.ContentCells?"CBC"+r.owner.Recordset.RecordNo:r.Header?"CBG"+r.recordset.recordNo:t.Name},set Text(e){this[e.length>256?"Picture":"ImageFile"]=e},get checked(){var e=this,t=e.oDataField,r=t?t.AsInteger:e.ImageIndex;return-1===r||-3===r},set checked(e){var t,r=this,o=r.oDataField,n=e?-1:-2;o?(t=o.owner,t.Edit(),o.AsInteger=n,t.Post()):r.ImageIndex=n,document.getElementById(r.getCheckBoxID()).checked=e},get AsPictureBox(){return this},LoadFromFile:function(e){var t=this,r=t.getOwnerDetailGrid();r?(t.ImageFiles||(t.ImageFiles={}),t.ImageFiles[r.Recordset.RecordNo]=e):t.ImageFile=e}},prototypeCopyExtend(PictureBox,Control);var RichTextBox=function(e){var t=this;Control.call(t,e)};RichTextBox.prototype={ControlType:6,get AsRichTextBox(){return this},generateNormal:function(e){e.innerText="不支持RichTextBox"}},prototypeCopyExtend(RichTextBox,Control);var SubReport=function(e){var t=this;Control.call(t,e),t.HideOnRecordsetEmpty=!1,t.RelationFields="",t.Report=new Report(e.report.viewer,t)};SubReport.prototype={ControlType:9,afterLoad:function(e){var t=this,r=e.Report;Control.prototype.afterLoad.call(t,e),t.Report.isWR=t.report.isWR,r&&t.Report.loadFromJSON(r)},prepare:function(){var e=this,t=e.report;Control.prototype.prepare.call(e),t._srChildren||(t._srChildren=[]),t._srChildren.push(e),e.prepareRelationRecordset()},unprepare:function(){var e=this;Control.prototype.unprepare.call(e),e.Report.unprepare()},prepareRelationRecordset:function(){var e,t,r,o,n,i,a=this,l=a.RelationFields,s=a.report,c=s.DetailGrid,u=a.Report,d=u.DetailGrid;if(l&&d&&c&&(e=c.Recordset,r=d.Recordset,t=e.decodeFields(l),o=r.decodeFields(l),t.length===o.length&&r.RecordCount)){for(a._rmFields=t,a._rdFields=o,i=a._relations=[],r.First(),n=[],i.push(n),n.push(r.curRecord),r.keepValue(),r.Next();!r.Eof();)r.fieldsKeepedValueChanged(o)&&(n=[],i.push(n),r.keepValue()),n.push(r.curRecord),r.Next();i.sort(function(e,t){for(var r,n,i,a=0,l=o.length;l>a;){if(r=o[a++]._tableMember,n=e[0][r],i=t[0][r],i>n)return-1;if(n>i)return 1}return 0})}},generateNormal:function(e){for(var t,r,o,n=this,i=n.Report.DetailGrid?n.Report.DetailGrid.Recordset:undefined,a=n._relations,l=n.report,s=l.DetailGrid,c=n.getOwnerDetailGrid(),u=n.Report.Parameters,d=u.Count;d>0;)r=u.Item(d--),o=r.Name,c?(t=l.FieldByName(o),t?r.Value=t.Value:(t=l.ParameterByName(o),t&&(r.Value=t.Value))):(t=l.ParameterByName(o),t?r.Value=t.Value:s&&(t=l.FieldByName(o),t&&(r.Value=t.Value)));a&&n.attachRelationTable(),(!n.HideOnRecordsetEmpty||!i||i.RecordCount)&&n.Report.generate(e),a&&n.detachRelationTable()},detachRelationTable:function(){var e=this.Report.DetailGrid.Recordset;e._dataTable=e._bakTable,e._relTblIndex=-1,delete e._bakTable},attachRelationTable:function(){function e(){for(var e,r,o=n._rmFields,i=n._rdFields,a=o.length,s=0;a>s;){if(r=t[i[s]._tableMember],e=l[o[s++]._tableMember],e>r)return-1;if(r>e)return 1}return 0}var t,r,o,n=this,i=n._relations,a=n.Report.DetailGrid.Recordset,l=n.report.DetailGrid.Recordset.curRecord,s=i.length-1,c=0;for(a._bakTable=a._dataTable,a._dataTable=[];s>=c;)if(r=Math.floor((c+s)/2),t=i[r][0],o=e(),0>o)c=r+1;else{if(!(o>0)){a._dataTable=i[r],a._relTblIndex=r;break}s=r-1}return!0},attachRelationTable2:function(e){var t=this;t.Report.DetailGrid.Recordset._dataTable=t._relations[e]},get AsSubReport(){return this}},prototypeCopyExtend(SubReport,Control);var FreeGridColumn=function(e){var t=this;Object.call(t,e),t.Visible=!0,t.FixedWidth=!1,t.Width=t.report.cm2Size(3)};FreeGridColumn.prototype={prepare:function(){self.pxWidth=this.report.size2Pixel(self.Width)},get FreeGrid(){return this.owner}},prototypeCopyExtend(FreeGridColumn,Object);var FreeGridRow=function(e){var t=this;Object.call(t,e),t.Visible=!0,t.Height=t.report.cm2Size(.58)};FreeGridRow.prototype={prepare:function(){self.pxHeight=this.report.size2Pixel(self.Height)},get FreeGrid(){return this.owner}},prototypeCopyExtend(FreeGridRow,Object);var FreeGrid=function(e){var t=this;Control.call(t,e),t.ColumnCount=5,t.RowCount=5,t.TitleRows=1,t.ShowColLine=!0,t.ShowRowLine=!0,t.ColLinePen=new Pen,t.RowLinePen=new Pen,t.columns=[],t.rows=[],t.cells=[]};FreeGrid.prototype={ControlType:13,attachData:function(){var e,t,r,o=this;for(t=o.cells.length,e=0;t>e;e++)r=o.cells[e],r&&r.forEach(function(e){e.attachData(),e.prepare()})},prepareChildren:function(){var e,t=this,r=t.columns,o=t.rows,n=t.cells;for(e=r.length=t.ColumnCount;e--;)r[e]||(r[e]=new FreeGridColumn(t));for(e=n.length=o.length=t.RowCount;e--;)o[e]||(o[e]=new FreeGridRow(t)),n[e]||(n[e]=[],n[e].length=t.ColumnCount)},prepare:function(){var e=this,t=new HtmlStyles;e.columns.length===e.ColumnCount&&e.rows.length===e.RowCount&&e.cells.length===e.RowCount||e.prepareChildren(),Control.prototype.prepare.call(e),t.addCellBorder(e),e.blankCellStyle=e.report.viewer.addCustomStyle(t)},assign:function(e){var t=this;Control.prototype.assign.call(t,e),e.columns.forEach(function(e,r){var o=new FreeGridColumn(t);o.assign(e),o.pxWidth=e.pxWidth,t.columns[r]=o}),e.rows.forEach(function(e,r){var o=new FreeGridRow(t);o.assign(e),o.pxHeight=e.pxHeight,t.rows[r]=o}),e.cells.forEach(function(e,r){e.forEach(function(e,o){var n=new FreeGridCell(t,r,o);n.assign(e),t.cells[r]||(t.cells[r]=[]),t.cells[r][o]=n})}),t.Height=e.Height},afterLoad:function(e){var t,r,o,n,i,a=this,l=a.report,s=a.Border,c=a.columns=[],u=a.rows=[],d=a.cells=[];if(Control.prototype.afterLoad.call(a,e),a.prepareChildren(),e.FreeGridColumn)for(n=e.FreeGridColumn.length,o=0;n>o;o++)r=e.FreeGridColumn[o],c[r.index-1].loadFromJSON(r);if(e.FreeGridRow)for(n=e.FreeGridRow.length,o=0;n>o;o++)r=e.FreeGridRow[o],u[r.index-1].loadFromJSON(r);if(e.FreeGridCell)for(n=e.FreeGridCell.length,o=0;n>o;o++)r=e.FreeGridCell[o],--r.row,--r.col,t=d[r.row][r.col]=new FreeGridCell(a,r.row,r.col),t.loadFromJSON(r);for(n=c.length,i=0,o=0;n>o;o++)t=c[o],t.pxWidth=l.size2Pixel(t.Width),i+=t.Width;for(a.Width=i+l.pixel2Size(s.getLeftWidth()+s.getRightWidth()),n=u.length,i=0,o=0;n>o;o++)t=u[o],t.pxHeight=l.size2Pixel(t.Height),i+=t.Height;a.Height=i+l.pixel2Size(s.getTopWidth()+s.getBottomWidth())},generateNormal:function(e){var t,r,o,n,i=this,a=i.report.viewer,l=new HtmlElement("table",e),s=new HtmlElement("colgroup",l),c=[];for(i.Font.font&&l.addClass(a.selectFont2(i.Font.font)),l.styles.addBorder(i.Border),l.addStyle("border-collapse","collapse"),l.addStyle("width","100%"),i.toFillBack()&&l.addBackColorStyle(i.BackColor),n=i.ColumnCount,r=0,o=0;n>o;o++)r+=i.columns[o].pxWidth;for(o=0;n>o;o++)t=new HtmlElement("col",s),t.skipend=1,t.addStyle("width",percentToHtml(100*i.columns[o].pxWidth/r));for(o=0;or;r++)for(t=e.col;n>t;t++)c[r][t]=1}(e.ColSpan>1||e.RowSpan>1)&&t(),c[e.row][e.col]=e})}),c.forEach(function(e,t){var r,o=i.ColumnCount,n=0,s=new HtmlElement("tr",l);s.addStyle("height",pixelsToHtml(i.rows[t].pxHeight));do{for(;o>n&&!e[n];)r=new HtmlElement("td",s),r.addClass(a._getCSSName(i.blankCellStyle)),n++;for(;o>n&&1===e[n];)n++;o>n&&(r=e[n])&&(r.generate(s),n=r.col+r.ColSpan)}while(o>n)})},get AsFreeGrid(){return this},ColumnAt:function(e){var t=this,r=t.columns;return r.length!==t.ColumnCount&&t.prepareChildren(),r[e-1]},RowAt:function(e){var t=this,r=t.rows;return r.length!==t.RowCount&&t.prepareChildren(),r[e-1]},CellAt:function(e,t){var r,o=this,n=o.cells,i=undefined;return n.length!==o.RowCount&&o.prepareChildren(),e>0&&e<=o.RowCount&&t>0&&t<=o.ColumnCount&&(r=n[--e],i=r[--t],i||(i=r[t]=new FreeGridCell(o,e,t))),i},CellByDataName:function(e){for(var t,r,o,n=this,i=n.cells,a=i.length,l=a;l--;)for(t=i[l],r=t.length;r--;)if(o=t[r],o&&o.DataName===e)return o;return undefined}},prototypeCopyExtend(FreeGrid,Control);var SectionRoot=function(e){var t=this;Object.call(t,e),t.Visible=!0,t.CanGrow=!1,t.CanShrink=!1,t.Height=e.report.cm2Size(t.defaultHeight()),t.BackColor=e.BackColor,t.FormatScript="",t.Font=new FontWrapper(t.getParentFont())};SectionRoot.prototype={afterLoad:function(e){var t=this,r=t.report;e[t.getJsonMember("BackColor")]||(t.BackColor=t.getParentBackColor()),colorMemberValid(t,"BackColor",r.viewer.alpha.background),t.Font.loadFromJSON(e.Font,r.isWR)},prepare:function(){var e=this,t=e.Controls;e.registerEventClass(),e.pxHeight=e.report.size2Pixel(e.Height),e._singleHidableSR=e.getSingleHidableSR(),e._singleSR=t&&1===t.Count&&9===t.Item(1).ControlType},unprepare:function(){var e=this;e.resetEventClass(),delete e.defaultStyle},generate:function(e){var t=this;t.report.fireSectionFormat(t),t.pxHeight=t.report.size2Pixel(t.Height),t.isToGenerate()&&(t._singleSR&&!t.Group?t.Controls.Item(1).generateContent(e):t.doGenerate(e))},defaultHeight:function(){return 3},getParentBackColor:function(e){return this.owner.BackColor},getParentFont:function(){return this.owner.Font},getSingleHidableSR:function(){return undefined},isControlPositionClass:function(){return 0},isToGenerate:function(){function e(){var e,r=t._singleHidableSR,o=r._relations;return o&&r.attachRelationTable(),e=!r.Report.DetailGrid||r.Report.DetailGrid.Recordset.RecordCount,o&&r.detachRelationTable(),e}var t=this,r=t.Visible&&t.Height>0;return r&&t._singleHidableSR&&(r=e()),r}},prototypeCopyExtend(SectionRoot,Object);var Section=function(e){var t=this;SectionRoot.call(t,e),t.Controls=new Controls(t)};Section.prototype={children:["Controls"],afterLoad:function(e){var t=this;SectionRoot.prototype.afterLoad.call(t,e),t.Controls.loadFromJSON(e.Control)},attachData:function(){this.Controls.attachData()},prepare:function(){var e=this,t=e.Controls,r=e.report,o=r.viewer;SectionRoot.prototype.prepare.call(e),t.layout(),2!==r.controlPosition&&e.buildTableLayout(r.getReportWidth(),e.pxHeight),t.prepare(),e.tableRows&&(r.reportFitHeight&&calcSectionsHeightPercent(e.tableRows),e.tableRows.forEach(function(e){e.defaultStyle=o.selectSectionItem(e)})),e.defaultStyle=o.selectSectionItem(e)},unprepare:function(){var e=this;SectionRoot.prototype.unprepare.call(e),e.Controls.unprepare()},buildTableLayout:function(e,t){function r(e,t,r,o){var n,i,a,l=[];for(s.forEach(function(r){l.push({pos:r.pxRect[e],count:1},{pos:r.pxRect[t],count:1})}),l.sort(function(e,t){return e.pos-t.pos}),n=l.length,i=0;n>i&&l[i].pos0&&(l.unshift({pos:r,count:i}),++n),!isNaN(o)){for(i=n-1;i>=0&&l[i]&&l[i].pos>o;)l[i--]=undefined;i=n-1-i,i>=0&&l.push({pos:o,count:i})}return a={pos:-98765},l.forEach(function(e,t){e&&(a.pos===e.pos?(a.count++,l[t]=undefined):a=e)}),a={pos:-98765},l.forEach(function(e,t){e&&(e.pos-a.pos<8?a.count>e.count?(a.count+=e.count,l[t]=undefined):(e.count+=a.count,l[i]=undefined,a=e,i=t):(a=e,i=t))}),l=l.filter(function(e){return e}),n=l.length,n>1&&(l[0].poso&&r[o].pos=n||o>0&&a-r[o-1].posl&&(d=r("left","right",0,e),d.length>2*l&&(u=d,p=undefined))):u=r("left","right",0,e),l=u.length-1,!p)for(f.tableCols=[],n=0;l>n;)f.tableCols.push(u[n+1].pos-u[n++].pos);for(o("top","beginRow",c),o("bottom","endRow",c),o("left","beginCol",u),o("right","endCol",u),s.forEach(function(e){var t=e.cell;t.endCol<=t.beginCol&&t.endColn;)m.push({section:f,pxHeight:c[n+1].pos-c[n++].pos,cells:[]});for(s=s.filter(function(e){var t=e.cell;return t.beginColn;n++)d=[],d.length=l,g.push(d);h&&h.forEach(function(e){var t=e.index;g.forEach(function(e){e[t]=1})}),n=0,s.forEach(function(e){for(var t,r,o=e.cell,i=g[o.beginRow];o.beginColo.beginCol&&i[o.endCol-1];)o.endCol--;if(o.beginCol.8?(g.forEach(function(e,t){var r,o,n,i=m[t].cells;r=o=0;do{for(;l>o&&!e[o];)o++;for(o>r&&(i.push({beginCol:r,endCol:o}),r=o);l>o&&1===e[o];)o++;r=o,l>r&&(n=e[r])&&(n=n.cell,i.push(n),r=o=n.endCol)}while(l>o)}),m.forEach(function(e){var t=0,r=0;e.cells.forEach(function(e){e.control&&(t++,e.control.CanShrink&&r++)}),t&&r===t&&delete e.pxHeight}),f.tableRows=m):(delete f.tableCols,delete f.tableRows,f.Controls.items.forEach(function(e){delete e.cell}))},doGenerate:function(e){var t=this,r=t.report.viewer;e=new HtmlElement(t.tableRows?"table":"div",e),t.addElementEventClass(e),e.addClass(r.selectSection(t)),t.tableRows?(e.addStyle("border-collapse","collapse"),t.generateTableRows(e)):(e.addStyle("position","relative"),t.Controls.generate(e))},generateTableRows:function(e,t){function r(){var e,t,r=h.length-1,o=h[r],n=s.showingColumns,i=s.Border;n.length>0&&(e=n[n.length-1],t=i.getLeftWidth()+i.getRightWidth(),Math.abs(o-e.pxWidth-t)<.5&&(h[r]-=t))}var o,n,i=this,a=i.owner,l=i.report,s=a.RunningDetailGrid,c=l.viewer,u=i.owner!==l||l.reportFitWidth,d=l.fixedColsLeft,f=a.Header?a.owner:0,p=f&&f.hasMergedColumn&&d>0,h=i.tableCols,g=0;h&&(h.forEach(function(e){g+=e}),a==i.report&&s&&r(),o=new HtmlElement("colgroup",e),e.addStyle("width",u?"100%":pixelsToHtml(g)),h.forEach(function(e){n=new HtmlElement("col",o),n.skipend=1,n.addStyle("width",percentToHtml(100*e/g))})),i.tableRows.forEach(function(r,o){function n(e){e.ContentCell.generate(a),e.mergingElement=a.children[a.children.length-1],e.ContentCell.getUsingFont()!==i.getUsingFont()&&e.mergingElement.addClass(c.selectFont2(e.ContentCell.getUsingFont())), +e.ContentCell.BackColor!==i.BackColor&&e.mergingElement.addBackColorStyle(e.ContentCell.BackColor),e.mergedHeader.todoMerge=0}var a;a=new HtmlElement("tr",e),a.addClass(c.selectSection(r)),r.cells.forEach(function(e){var r,l,s,u=e.control;if(!o&&t)for(;(s=t[0])&&s.index<=e.beginCol;)n(s),t.shift();r=new HtmlElement("td",a),l=e.endCol-e.beginCol,l>1&&r.addAttribute("colspan",l+""),u?(l=e.endRow-e.beginRow,l>1&&r.addAttribute("rowspan",l+""),u.Visible&&(13!==u.ControlType?(r.addClass(c.selectControl(u)),u.Font.font&&r.addClass(c.selectFont(u.Font.font,u.defaultFontStyle))):r.addStyle("padding","0px"),u.generateInCell(r))):e.fixed&&r.addStyle("background-color",colorValue2Html(i.BackColor)),p&&e.endCol<=d&&(r.addAttribute("id",f.showingColumns[e.beginCol].idMGC),r.addStyle("background-color",colorValue2Html(i.BackColor)))}),!o&&t&&t.forEach(function(e){n(e)})})},getRect:function(){var e=this;return new Rect(0,0,e.report.getReportWidth(),e.pxHeight)},getSingleHidableSR:function(){var e=this,t=e.Controls.items,r=t[0];return 1===t.length&&9===r.ControlType&&r.HideOnRecordsetEmpty?r:undefined},_runningNo:function(){return 0}},prototypeCopyExtend(Section,SectionRoot);var ReportHeader=function(e){var t=this;Section.call(t,e),t.Name=""};ReportHeader.prototype={toFillBack:function(){return 1}},prototypeCopyExtend(ReportHeader,Section);var ReportFooter=function(e){var t=this;Section.call(t,e),t.Name=""};ReportFooter.prototype={toFillBack:function(){return 1}},prototypeCopyExtend(ReportFooter,Section);var ColumnSection=function(e){var t=this;SectionRoot.call(t,e),t.Height=t.report.cm2Size(.58)};ColumnSection.prototype={defaultHeight:function(){return.58}},prototypeCopyExtend(ColumnSection,SectionRoot);var ColumnContent=function(e){var t=this;ColumnSection.call(t,e),t.ContentCells=new ColumnContentCells(t),t.AlternatingBackColor=t.BackColor};ColumnContent.prototype={afterLoad:function(e){var t=this;ColumnSection.prototype.afterLoad.call(t,e),e[t.getJsonMember("AlternatingBackColor")]?colorMemberValid(t,"AlternatingBackColor",t.report.viewer.alpha.background):t.AlternatingBackColor=t.BackColor,t.ContentCells.loadFromJSON(e.ColumnContentCell)},prepare:function(){var e,t=this,r=t.report.viewer;ColumnSection.prototype.prepare.call(t),t.defaultStyle=r.selectSectionItem(t),t.AlternatingBackColor!==t.BackColor&&(e=t.BackColor,t.BackColor=t.AlternatingBackColor,t.altSectionStyle=r.selectSectionItem(t),t.BackColor=e),t.ContentCells.prepare()},unprepare:function(){var e=this;ColumnSection.prototype.unprepare.call(e),e.ContentCells.unprepare()},generate:function(e,t){var r=this;r.owner.Recordset.moveTo(t),ColumnSection.prototype.generate.call(r,e)},doGenerate:function(e){var t=this,r=t.report.viewer,o=t.owner,n=o.showingColumns,i=o.Recordset.RecordNo;e=new HtmlElement("tr",e),t.addElementEventClass(e),e.addAttribute(grconst.ATTR_CONTENT_RECNO,i+""),t.altSectionStyle&&i%2?(t._BackColor=t.BackColor,t._defaultStyle=t.defaultStyle,t.BackColor=t.AlternatingBackColor,t.defaultStyle=t.altSectionStyle,e.addClass(r.selectSection(t)),t.BackColor=t._BackColor,t.defaultStyle=t._defaultStyle):e.addClass(r.selectSection(t)),o.hasMergedColumn?n.forEach(function(t){var r,o,n,i=t.ContentCell,a=t.mergedHeader;if(a){if(a.todoMerge){if(a.SameAsColumn)i.generate(e);else for(r=a.Controls,o=r.Count;o>0;)if(n=r.Item(o--),n.oAlignColumn===t){i.generateMerge(e,n);break}e.children&&(t.mergingElement=e.children[e.children.length-1])}}else i.generate(e)}):n.forEach(function(t){t.ContentCell.generate(e)}),o.hasMergedColumn&&o.Groups.forEach(function(e){e.Header.todoMerge&&(e.Header.todoMerge=0)})},getSingleHidableSR:function(){for(var e,t,r=this,o=r.ContentCells.items,n=o.length,i=0,a=0,l=0;n>i&&(e=o[i++],e._freeCell);)if(l=e.Controls.Count,l>0)if(a+=l,1===a)t=e.Controls.Item(1);else if(a>1)break;return 1===a&&9===t.ControlType&&t.HideOnRecordsetEmpty?t:undefined},SetCellsBackColor:function(e){this.ContentCells.forEach(function(t){t.BackColor=e})},SetCellsForeColor:function(e){this.ContentCells.forEach(function(t){t.setForeColor(e)})}},prototypeCopyExtend(ColumnContent,ColumnSection);var ColumnTitle=function(e){var t=this;ColumnSection.call(t,e),t.TitleCells=new ColumnTitleCells(t)};ColumnTitle.prototype={afterLoad:function(e){var t=this;ColumnSection.prototype.afterLoad.call(t,e),t.TitleCells.loadFromJSON(e.ColumnTitleCell)},prepare:function(){function e(t){var n=1;return t.showingItems=[],t.forEach(function(i){var a,l,s;i.layer=o,i.GroupTitle?i.Visible&&(o++,a=e(i.SubTitles)+1,o--,l=i.SubTitles.showingItems,l.length>0&&(t.showingItems.push(i),a>n&&(n=a),i.colspan=0,l.forEach(function(e){i.colspan+=e.colspan}))):(s=i.Column,s.Visible&&s.pxWidth>0&&(t.showingItems.push(i),i.colspan=1,s.pxLeft=r.columnsTotalWidth,s.index=r.showingColumns.length,r.columnsTotalWidth+=s.pxWidth,r.showingColumns.push(s)))}),n}var t=this,r=t.owner,o=0;ColumnSection.prototype.prepare.call(t),r.columnsTotalWidth=0,r.showingColumns=[],t.layerCount=e(t.TitleCells),t.pxHeight/=t.layerCount,t.TitleCells.prepare(),t.defaultStyle=t.report.viewer.selectSectionItem(t)},unprepare:function(){var e=this;ColumnSection.prototype.unprepare.call(e),e.TitleCells.unprepare()},doGenerate:function(e){function t(e){e.forEach(function(e){e.layer===o.generatingLayer&&e.generate(r),e.GroupTitle&&t(e.SubTitles.showingItems)})}var r,o=this,n=o.report.viewer;for(o.pxHeight/=o.layerCount,o.generatingLayer=0;o.generatingLayero;o++){if(n=e.items[o],n===r)return e;if(n.GroupTitle&&(i=t(n.SubTitles,r)))return i}return undefined}return t(this.TitleCells,e)},SetCellsBackColor:function(e){function t(r){r.forEach(function(r){r.BackColor=e,r.GroupTitle&&t(r.SubTitles)})}t(this.TitleCells)},SetCellsForeColor:function(e){function t(r){r.forEach(function(r){r.setForeColor(e),r.GroupTitle&&t(r.SubTitles)})}t(this.TitleCells)}},prototypeCopyExtend(ColumnTitle,ColumnSection);var GroupSection=function(e){var t=this;Section.call(t,e),t.Height=t.report.cm2Size(1.2)};GroupSection.prototype={generate:function(e,t){var r=this,o=r.owner;o.recordset.moveTo(t),o.owner.Recordset.moveTo((r===o.Header?o.beginNoField:o.endNoField).Value),Section.prototype.generate.call(r,e)},doGenerate:function(e){function t(e){var t=o.owner,r=o===t.Header?grconst.ATTR_GROUPH_INDEX:grconst.ATTR_GROUPF_INDEX;e.addAttribute(grconst.ATTR_GROUP_RECNO,t.recordset.recordNo),e.addAttribute(r,t.index)}var r,o=this,n=o.report.viewer,i=o.owner.owner,a=i.showingColumns,l=a.length;i.hasMergedColumn&&(r=[],a.forEach(function(e){e.mergedHeader&&e.mergedHeader.todoMerge&&(o.owner.Footer!==o||o.owner===i.Groups.Item(i.Groups.Count)&&e.mergedHeader!==o.owner.Header)&&r.push(e)}),r.length||(r=undefined)),o.tableRows&&!o.tableCols?(o.generateTableRows(e,r),e.children&&e.children.slice(-o.tableRows.length).forEach(function(e){o.addElementEventClass(e),t(e)})):(e=new HtmlElement("tr",e),e.addClass(n.selectSection(o)),o.addElementEventClass(e),t(e),e=new HtmlElement("td",e),l>1&&e.addAttribute("colspan",l),o.tableCols?(e=new HtmlElement("table",e),e.addClass(n.selectSection(o)),e.addStyle("border-collapse","collapse"),o.generateTableRows(e)):(e.addStyle("position","relative"),o.Controls.generate(e)))},getTableColEdges:function(){var e,t=this.owner.owner.showingColumns;return e=t.map(function(e){return{pos:e.pxLeft,count:1}}),e.push({pos:t[t.length-1].pxRight,count:1}),e},getMergedColumns:function(){var e,t,r=this;return e=r.owner.index,t=r.owner.owner.showingColumns.filter(function(t){var o,n,i=t.mergedHeader;return i&&((n=(o=i.owner).index)0&&(t.groupMergedColumns=e,e.forEach(function(e){e.mergedHeader=t,t.owner.owner.hasMergedColumn=1}))),!(t.groupMergedColumns&&t.SameAsColumn)&&GroupSection.prototype.prepare.call(t)},unprepare:function(){},generate:function(e,t){var r=this;r.groupMergedColumns?(r.owner.recordset.moveTo(t),r.curItemBegin=e.children?e.children.length:0,r.todoMerge=1):GroupSection.prototype.generate.call(r,e,t)},get Group(){return this.owner}},prototypeCopyExtend(GroupHeader,GroupSection);var GroupFooter=function(e){GroupSection.call(this,e)};GroupFooter.prototype={generate:function(e,t){var r,o=this,n=o.owner.Header,i=n.groupMergedColumns;GroupSection.prototype.generate.call(o,e,t),i&&e.children&&(r=e.children.length-n.curItemBegin,!n.IncludeFooter&&o.isToGenerate()&&r--,i.forEach(function(e){var t=e.mergingElement;t&&r>1&&t.addAttribute("rowspan",r+"")}))},get Group(){return this.owner}},prototypeCopyExtend(GroupFooter,GroupSection);var Field=function(e){var t=this;Object.call(t,e),t.FieldType=1,t._name="",t._dbFieldName="",t._tableMember=undefined,t.Format="",t.Length=0,t.RTrimBlankChars=!1,t.GetDisplayTextScript=""};Field.prototype={afterLoad:function(e){var t,r=this;t=e[r.getJsonMember("Type")],t&&(r.FieldType=t),t=e[r.getJsonMember("Name")],t&&(r.Name=t),t=e[r.getJsonMember("DBFieldName")],t&&(r.DBFieldName=t),enumMemberValid(r,"FieldType",grenum.FieldType)},prepare:function(){var e=this,t=e.FieldType,r=e.Format;6===t?e.formater=new DateTimeFormatter(r):5===t?e.formater=new BooleanFormatter(r):2!==t&&3!==t&&4!==t||(e.formater=new NumericFormatter(r))},unprepare:function(){var e=this;delete e.formater,delete e._fts},isNumeric:function(){var e=this.FieldType;return 2===e||3===e||4===e},valUeq:function(e){var t=this.Value,r=e.Value;return r>t||t>r||(t===undefined||r===undefined)&&t!==r},get Value(){var e=this,t=e.owner.curRecord;return t?t[e._tableMember]:undefined},set Value(e){var t=this,r=t.owner.curRecord;if(r){if(e!==undefined&&null!==e)switch(t.FieldType){case 2:case 3:case 4:e=+e;break;case 5:e=confirmBooleanValue(e);break;case 6:e=confirmCloneDateValue(e);break;default:e+=""}else e=undefined;r[t._tableMember]=e}},get AsBoolean(){return confirmBooleanValue(this.Value)},set AsBoolean(e){this.Value=e},get AsCurrency(){return+this.Value},set AsCurrency(e){this.Value=e},get AsDateTime(){return cloneDate(this.Value)},set AsDateTime(e){this.Value=e},get AsFloat(){var e,t=this;if(t.IsNull)e=0;else if(6===t.FieldType){var r=new DateTime;r.value=t.Value,e=r.AsFloat}else e=+t.Value;return isNaN(e)?0:e},set AsFloat(e){var t,r=this;6===r.FieldType&&(t=new DateTime,t.AsFloat=e,e=t.value),r.Value=e},get AsInteger(){return Math.floor(this.AsFloat)},set AsInteger(e){this.AsFloat=Math.floor(+e)},get AsString(){var e=this;return e.IsNull?"":e.Value+""},set AsString(e){this.Value=e},get DisplayText(){var e=this,t=e.FieldType,r=e.Value;return e.displayTextAssigned=0,e.doingGetDisplayText||(e.doingGetDisplayText=1,e.report.fireFieldGetDisplayText(e),e.doingGetDisplayText=0),e.displayTextAssigned?e.assignedDisplayText:e.IsNull?"":1===t||7===t?r+"":e.formater.format(r)},set DisplayText(e){var t=this;t.assignedDisplayText=e+"",t.displayTextAssigned=1},get IsNull(){var e=this.Value;return e===undefined||null===e},get DataSize(){return 0},get DataBuffer(){return undefined},get Name(){return this._name},set Name(e){var t=this;t._tableMember&&t._tableMember!==t._name||(t._tableMember=e),t._name=e},get DBFieldName(){return this._name},set DBFieldName(e){var t=this;t._dbFieldName=e,e&&(t._tableMember=e)},get RunningDBField(){var e=this;return e._dbFieldName||e._name},Clear:function(){this.Value=undefined}},prototypeCopyExtend(Field,Object);var Recordset=function(e){var t=this;Object.call(t,e),t.QuerySQL="",t.Fields=new Fields(t),t.SortAsc=!0,t.SortCaseSensitive=!1,t.SortFields="",t.XmlTableName="",t.FetchRecordScript="",t.BeforePostRecordScript="",t.ProcessRecordScript="",t._dataTable=[],t.recordNo=0};Recordset.prototype={children:["Fields"],afterLoad:function(e){this.Fields.loadFromJSON(e.Field)},attachDataTable:function(e){var t=this;t._dataTable=e,t.moveTo(0)},appendRefRecord:function(e){this._dataTable.push(e)},prepareTableData:function(){function e(e,t){if(e.hasOwnProperty(t))return t;t=t.toUpperCase();for(var r in e)if(t===r.toUpperCase())return r;return undefined}var t,r,o,n,i,a=this,l=a._dataTable,s=a.Fields.items,c=l.length,u=s.length;if(DEBUG)for(;u--;)r=s[u];for(;c--;)for(t=l[c],u=s.length;u--;){if(r=s[u],i=r._fts,n=r._tableMember,i===undefined&&(n=e(t,n))){switch(r.FieldType){case 1:i="string"!=typeof t[n];break;case 2:case 3:case 4:i="number"!=typeof t[n];break;case 5:i="boolean"!=typeof t[n];break;case 6:i=!Date.prototype.isPrototypeOf(t[n]);break;default:i=!1}r._fts=i,r._tableMember=n}if(n&&(o=t[n],null!==o&&""!==o||(o=undefined,t[n]=o),i&&o!==undefined))switch(r.FieldType){case 1:t[n]=o+"";break;case 2:case 3:case 4:t[n]=+o;break;case 5:t[n]=confirmBooleanValue(o);break;case 6:t[n]=confirmDateValue(o)}}a.SortFields&&a.Resort(a.SortFields,a.SortAsc,a.SortCaseSensitive)},prepare:function(){var e=this,t=e._dataTable.length,r=0;for(e.Fields.prepare(),e.prepareTableData();t>r;)e.moveTo(r++),e.beforePost();e.moveTo(0)},unprepare:function(){var e=this;e.Fields.unprepare(),e._dataTable=[]},beforePost:function(){var e=this,t=e.report,r=e.BeforePostRecordScript;r&&t.executeEventScript(r,script.names.BeforePostRecord,e),e.isDetailGridRecordset()&&t.fireBeforePostRecord()},decodeFields:function(e){return this.Fields.decodeItems(e)},isDetailGridRecordset:function(){var e=this;return e.owner===e.report.DetailGrid},isAppendingRecord:function(){return 1===this.editStatus},keepValue:function(){var e=this;e.keepedRecord=e.curRecord},fieldsKeepedValueChanged:function(e){var t=this;return e.some(function(e){var r=e._tableMember,o=t.keepedRecord[r],n=t.curRecord[r];return n>o||o>n||(o===undefined||n===undefined)&&o!==n})},sortRecords:function(e,t){for(var r=this,o=r._dataTable,n=o.length,i=0,a=[];n>i;)a.push(i++);return e.length>0&&(e.forEach(function(e){e.fieldName=e.field._tableMember}),a.sort(function(t,r){var n;return e.some(function(e){var i=o[t][e.fieldName],a=o[r][e.fieldName];return i!==undefined&&a!==undefined?1===e.field.FieldType?(e["case"]||(i=i.toUpperCase(),a=a.toUpperCase()),n=i.localeCompare(a)):n=a>i?-1:i>a?1:0:n=i===undefined&&a===undefined?0:i===undefined?-1:1,e.asc||(n=-n),n}),n})),t?a:(i=[],a.forEach(function(e){i.push(o[e])}),r._dataTable=i,void r.moveTo(0))},get RecordCount(){var e=this;return e._dataTable?e._dataTable.length:0},get RecordNo(){return this.recordNo},FieldByName:function(e){return this.Fields.itemByName(e)},FieldByDBName:function(e){var t,r=this.Fields.items,o=r.length;for(e=e.toUpperCase(),t=0;o>t;t++)if(r[t].RunningDBField.toUpperCase()===e)return r[t];return undefined},Append:function(){var e=this;return e.editStatus=1,e.recordNo=e._dataTable.length,e.curRecord={},1},Duplicate:function(){},Edit:function(){var e,t,r=this,o=r.Fields.items,n=o.length,i=r.curRecord,a={};if(r.recordNo>=0&&r.recordNo=e._dataTable.length},moveTo:function(e){var t=this,r=t._dataTable;t.recordNo=e,t.curRecord=r[Math.min(Math.max(0,e),r.length-1)]},Empty:function(){this.attachDataTable([])},AddField:function(e,t){var r=this.Fields.Add();return r.Name=e,r.FieldType=t,r},RemoveAllFields:function(){this.Fields.RemoveAll()},Resort:function(e,t,r){function o(){var o=[],n=e.split(";");return n.forEach(function(e){function n(e){e=e.trim().toUpperCase(),"ASC"===e?l.asc=!0:"DESC"===e?l.asc=!1:"CASE"===e?l["case"]=!0:"CASEIN"===e&&(l["case"]=!1)}var i,l={};e=e.trim(),i=e.split(" "),i[0]=i[0].trim(),l.field=a.Fields.Item(i[0]),l.field&&(l.asc=t,l["case"]=r,i.length>1&&n(i[1]),i.length>2&&n(i[2]),o.push(l))}),o}var n,i,a=this,l=[];e&&(n=o(),i=n.length,i>0&&(a.report.fireBeforeSort(e,a),a.isDetailGridRecordset()&&(a.owner.Groups.forEach(function(e){a.decodeFields(e.ByFields).forEach(function(e){l.indexOf(e)<0&&l.push(e)})}),n.forEach(function(e){var t=l.indexOf(e.field);0>t?l.push(e):l[t]=e}),l.forEach(function(e,o){e.field||(l[o]={field:e,asc:t,"case":r})}),n=l,i=n.length)),a.sortRecords(n,0))},FilterByStep:function(e,t){var r=this,o=r.RecordCount(),n=0;if(r._dataTable&&!r.isAppendingRecord()&&e>0){for(r.FilterBegin();o>n;)r.moveTo(n),r.FilterSelect(),n+=e;t&&n-e+1!=o&&(r.Last(),r.FilterSelect()),r.FilterEnd()}},FilterByCount:function(e){var t,r,o=this,n=o.RecordCount(),i=0;if(o._dataTable&&!o.isAppendingRecord()&&e>1&n>e){for(t=(n-1)/(e-1),o.FilterBegin();n>i;)r=Math.round(i),o.moveTo(r),o.FilterSelect(),i+=t;o.FilterEnd()}},FilterBegin:function(){this._dtf=[]},FilterEnd:function(){var e=this,t=e._dataTable;e._dtf&&(e._dataTable=e._dtf,e._dtf=t)},FilterSelect:function(){var e=this;!(!e._dtf||e.isAppendingRecord()||e.Bof()||e.Eof())&&e._dtf.push(e.curRecord)},FilterReset:function(){var e=this;e._dtf&&(e._dataTable=e._dtf,delete e._dtf)}},prototypeCopyExtend(Recordset,Object);var Column=function(e){var t=this;Object.call(t,e),t.Name="",t.Visible=!0,t.FixedWidth=!1,t.Width=t.report.cm2Size(3)};Column.prototype={prepare:function(){var e=this;e.pxWidth=e.report.size2Pixel(e.Width)},get pxRight(){var e=this;return e.pxLeft+e.pxWidth},get idMGC(){var e=this;return"_mgc"+e.owner.index+"_"+e.index}},prototypeCopyExtend(Column,Object);var Group=function(e){var t=this;Object.call(t,e),t.Header=new GroupHeader(t),t.Footer=new GroupFooter(t),t.Name="",t.ByFields="",t.PageGroup=!1,t.SortSummaryBox="",t.SortAsc=!1,t.GroupBeginScript="",t.GroupEndScript=""};Group.prototype={children:["Header","Footer"],afterLoad:function(e){var t=this;t.Header.loadFromJSON(e.GroupHeader),t.Footer.loadFromJSON(e.GroupFooter)},prepare:function(e){function t(e){e.forEach(function(e){function o(e){function n(e){var t,o,n,a=i.length+"",l="";i.push(e),e.SummaryFun?(e.value=0,28===e.SummaryFun&&(t=r.owner.Groups.indexOf(r),t>0&&(o=new SummaryItem,o.SummaryFun=-1,o.init(),r.owner.Groups.items[t-1].summaryItems.push(o))),e.displayField?(n=e.displayField.FieldType,l=e.displayField.Format):(n=18===e.SummaryFun||19===e.SummaryFun||29===e.SummaryFun||30===e.SummaryFun?1:3,e.paramExp&&(o=e.paramExp.varItems,1===o.length&&(o=o[0].varField.object,o&&o.Format&&(l=o.Format))))):e.oParameter?(n=e.oParameter.DataType,l=e.oParameter.Format):n=3,e.valueField=r.recordset.AddField(a,n),e.valueField.Format=l}var i=r.summaryItems,a=e.ControlType;5===a?n(e):8===a?MemoBox.prototype.getGroupVars.call(e).forEach(function(e){n(e)}):1===a?e.oParameter&&n(e):13===a&&e.cells.forEach(function(e){e.forEach(function(e){e.FreeCell?t(e.Controls):o(e)})})}o(e)})}var r=this;r.index=e,r.Header.prepare(),r.Footer.prepare(),r.oByFields=r.owner.Recordset.decodeFields(r.ByFields),r.SortSummaryBox&&(r.sortSummaryBoxObj=r.Footer.Controls.itemByName(r.SortSummaryBox),r.sortSummaryBoxObj||(r.sortSummaryBoxObj=r.Header.Controls.itemByName(r.SortSummaryBox)),r.sortSummaryBoxObj&&5!==r.sortSummaryBoxObj.ControlType&&delete r.sortSummaryBoxObj),r.recordset=new Recordset(r),r.beginNoField=r.recordset.AddField("b",2),r.endNoField=r.recordset.AddField("e",2),r.groupItemNo=0,r.summaryItems=[],t(r.Header.Controls),t(r.Footer.Controls),r.recordset.prepare()},unprepare:function(){},inphaseByDetail:function(e){var t=this,r=t.recordset;if(!r.isAppendingRecord()&&r.RecordCount){for(;t.beginNoField.Value>e&&!r.Bof();)r.Prior();for(;t.endNoField.Value0;)t=a[l],r=t.SummaryFun,r?-1!==r&&(t.displayField||29===r||30===r||18===r||19===r?(o=t.stringValue,o&&(t.valueField.AsString=o)):(o=t.value,o!==undefined&&(t.valueField.AsFloat=o))):t.MathFun?t.valueField.Value=t.getAsFloat():t.valueField.Value=t.oParameter.Value;i.DetailGrid.CrossTab&&i.DetailGrid.CrossTab.GroupEndProcess(e),e.endNoField.Value=Math.min(n.Recordset.recordNo,n.Recordset.RecordCount-1),e.recordset.Post()}},prepare:function(){function e(r){r.forEach(function(r){function o(r){var n=t.globalSummaryItems,i=r.ControlType;5===i?n.push(r):8===i?MemoBox.prototype.getGroupVars.call(r).forEach(function(e){e.SummaryFun&&n.push(e)}):13===i&&r.cells.forEach(function(t){t.forEach(function(t){t.FreeCell?e(t.Controls):o(t)})})}o(r)})}var t=this,r=t.report;t.CrossTab?(t.buildColumnsOrder(),t.CrossTab.prepare()):(t.Columns.prepare(),t.ColumnContent.prepare(),t.ColumnTitle.prepare(),t.Groups.forEach(function(e,t){e.prepare(t)}),t.globalSummaryItems=[],r.ReportHeaders.items.concat(r.ReportFooters.items).forEach(function(t){e(t.Controls)}))},unprepare:function(){var e=this;e.CrossTab&&e.CrossTab.unprepare(),e.Recordset.unprepare(),e.ColumnContent.unprepare(),e.ColumnTitle.unprepare(),e.Groups.forEach(function(e,t){e.unprepare(t)})},generate:function(e){function t(){var e=n.ownerReport,t=n.detailgridResize;return a||1===t||2===t&&window.innerWidth&&o.width=0&&s.addAttribute("_grrelTblIndex",i._relTblIndex),r=new HtmlElement("tbody",s),o.generatingRowNo=1,o.rows.forEach(function(e,t){var n;e.tfoot&&(r=new HtmlElement("tfoot",s)),e.header?(n=e.header,n.generate(r,e.recordNo)):e.footer?(n=e.footer,n.generate(r,e.recordNo)):(n=o.ColumnContent,n.generate(r,e)),n.Visible&&n.Height>0&&++o.generatingRowNo})},buildRows:function(){function e(e){e.summaryItems.forEach(function(e){e.SummaryFun&&e.summaryCurRecord()}),e.groupRecordCount++}function t(e){function t(){var t,r=l.length,o=e.owner.rows,n=[];1>=r||(t=l[0].beginRowNo,l.forEach(function(t){e.recordset.moveTo(t.groupItemNo),t.sortValue=e.sortSummaryBoxObj.valueField.AsFloat}),l.sort(function(t,r){return e.SortAsc?t.sortValue-r.sortValue:r.sortValue-t.sortValue}),l.forEach(function(e){n=n.concat(o.slice(e.beginRowNo,e.endRowNo))}),n.forEach(function(e){o[t++]=e}))}var r,o,n,i=e.owner.rows,a=i.length,l=[];for(r=0;a>r;){for(n={},o=i[r],o.footer&&o.footer.owner.indexr;)if(o=i[r++],o.header&&o.header.owner.index===e.index){n.beginRowNo=r-1,n.groupItemNo=o.recordNo;break}for(;a>r;)if(o=i[r++],o.footer&&o.footer.owner.index===e.index){n.endRowNo=r,l.push(n);break}}t()}var r,o,n,i=this,a=i.report,l=i.Recordset,s=i.Groups.items,c=s.length,u=0;for(i.rows=[],i.globalSummaryItems.forEach(function(e){e.beginSummaryValue()}),l.First(),l.keepValue(),s.forEach(function(e){i.beginGroupItem(e)});!l.Eof();){for(r=0;c>r&&!l.fieldsKeepedValueChanged(s[r].oByFields);r++);if(c>r){for(l.Prior(),o=c-1;o>=r;o--)i.endGroupItem(s[o]);for(l.Next(),o=r;c>o;o++)i.beginGroupItem(s[o])}a.fireProcessRecord(l),s.forEach(function(t){e(t)}),i.globalSummaryItems.forEach(function(e){e.summaryCurRecord()}),i.rows.push(l.recordNo),l.keepValue(),l.Next()}for(a.fireProcessEnd(),o=c-1;o>=0;o--)i.endGroupItem(s[o]);i.globalSummaryItems.forEach(function(e){e.endSummaryValue()}),s.forEach(function(e){e.sortSummaryBoxObj&&(t(e),u=1)}),u&&(n=[],o=0,i.sortedTable=[],s.forEach(function(e){e.sortedTable=[],e.groupItemNo=0}),i.rows.forEach(function(e){var t;e.header?(t=e.header.owner,t.recordset.moveTo(e.recordNo),t.recordset.Edit(),t.beginNoField.Value=o,e.recordNo=t.groupItemNo):e.footer?(t=e.footer.owner,t.endNoField.Value=o,t.recordset.Post(),t.sortedTable.push(t.recordset._dataTable[e.recordNo]),e.recordNo=t.groupItemNo++):(i.sortedTable.push(l._dataTable[e]),e=o++),n.push(e)}),i.rows=n,l.attachDataTable(i.sortedTable),delete i.sortedTable,s.forEach(function(e){e.recordset.attachDataTable(e.sortedTable),delete e.sortedTable}))},syncTRData:function(e){function t(t){var r=e.parentNode.parentNode;r.hasAttribute("_grrelTblIndex")&&i.report.ownerSR.attachRelationTable2(r.getAttribute("_grrelTblIndex")),i.Recordset.moveTo(t)}var r,o,n,i=this,a=1,l={};return(o=e.getAttribute(grconst.ATTR_CONTENT_RECNO))?(t(o),l.content=i.ColumnContent):(o=e.getAttribute(grconst.ATTR_GROUP_RECNO))&&(n=e.getAttribute(grconst.ATTR_GROUPH_INDEX),n||(n=e.getAttribute(grconst.ATTR_GROUPF_INDEX),a=0),r=l.group=i.Groups.items[n],r.recordset.moveTo(o),t((a?r.beginNoField:r.endNoField).Value)),l},syncElementData:function(e,t){function r(e,t){var r,o=e.tagName.toUpperCase();if("TH"===o||"TABLE"===o)return 0;do{if(r&&(e=r.parentNode,o=e.tagName.toUpperCase(),"TH"===o))return 0;for(;"TD"!==o;){if(e=e.parentNode,!e)return 0;if(o=e.tagName.toUpperCase(),"TH"===o)return 0}if(r=e.parentNode.parentNode.parentNode,!t&&r.classList.contains(grconst.CSS_DG))break}while(t!==r);return e}var o=this,n=r(e,t);n&&o.syncTRData(n.parentNode)},onmousedown:function(e){function t(){var t,r,o,n=e.target;if("TABLE"===n.tagName.toUpperCase())return 0;for(;!o;){if(r&&(n=r.parentNode.parentNode.parentNode),t=n.tagName.toUpperCase(),"TH"===t)return 0;for(;"TD"!==t;)if(n=n.parentNode,t=n.tagName.toUpperCase(),"TH"===t)return 0;r=n.parentNode,o=r.hasAttribute(grconst.ATTR_CONTENT_RECNO)||r.hasAttribute(grconst.ATTR_GROUP_RECNO)}return n}function r(t){var r=o.curSelObj,n=o.report.viewer.options,i=t.parentNode,a=o.selectionCell?t:i;i.parentNode.parentNode===e.currentTarget&&n.selectionHighlight&&(r===a?a.classList.toggle(grconst.CSS_SH):(r&&r.classList.remove(grconst.CSS_SH),a.classList.add(grconst.CSS_SH),o.curSelObj=a)),o.clickedCell=t}var o=this,n=t();n&&r(n),o.syncElementData(e.target,e.currentTarget)},getClickedRow:function(){var e,t,r,o=this,n=o.clickedCell,i={};return n&&(e=n.parentNode,(r=e.getAttribute(grconst.ATTR_CONTENT_RECNO))?(i.type="content",i.recordNo=r):(r=e.getAttribute(grconst.ATTR_GROUP_RECNO))&&(t=e.hasAttribute(grconst.ATTR_GROUPH_INDEX),i.type=t?"groupheader":"groupfooter",i.groupIndex=+e.getAttribute(t?grconst.ATTR_GROUPH_INDEX:grconst.ATTR_GROUPF_INDEX))),i},clearColumns:function(){var e=this;e.ColumnContent.ContentCells.RemoveAll(),e.ColumnTitle.TitleCells.RemoveAll(),e.Columns.RemoveAll()},get width(){var e=this,t=e.Border;return e.columnsTotalWidth+t.getLeftWidth()+t.getRightWidth()},get selectionCell(){var e=this,t=e.report;return t.fixed&&(t.fixedColsLeft>0||t.fixedColsRight>0)?1:t.viewer.options.selectionCell},_ColumnMoveTo:function(e,t){var r=this,o=r.ColumnTitle.FindColumnTitlesOfTitleCell(e.TitleCell),n=t?t.SubTitles:r.ColumnTitle.TitleCells;r.MoveTitleTo(e.TitleCell,o,n,-1)},MoveTitleTo:function(e,t,r,o){t.items.splice(t.items.indexOf(e),1),0>o?r.items.push(e):r.items.splice(o,0,e)},get IsCrossTab(){return!!this.CrossTab},set IsCrossTab(e){var t=this;e?t.CrossTab||(t.CrossTab=new CrossTab(t)):delete t.CrossTab},AddColumn:function(e,t,r,o){var n=this.Columns.Add();return n.Name=e,n.Width=o,n.ContentCell.DataField=r,n.TitleCell.Text=t,n},ClearColumns:function(){this.Columns.RemoveAll()},FindGroupTitleCell:function(e){function t(r){var o,n=r.items.length,i=r.itemByName(e);if(!i)for(o=0;n>o;o++)if(i=r.items[o],i.GroupTitle&&(i=t(i.SubTitles,e)))return i;return i}return t(this.ColumnTitle.TitleCells,e)},AddGroupTitle:function(e,t){return this.ColumnTitle.TitleCells.AddGroup(e,t)},ClearGroupTitles:function(){var e,t,r=this,o=r.Columns.Count,n=r.ColumnTitle.TitleCells,i=[];for(e=1;o>=e;e++)ColumnMoveToEnd(e);for(o=n.Count,e=0;o>e;e++)t=n.items[e],!t.GroupTitle&&i.push(t);n.items=i},ColumnMoveToEnd:function(e){var t=this,r=t.Columns.Item(e);r&&sel._ColumnMoveTo(r,undefined)},ColumnMoveTo:function(e,t,r){var o,n=this,i=n.Columns.Item(e),a=i?i.TitleCell:n.FindGroupTitleCell(e),l=t?n.FindGroupTitleCell(t):undefined,s=t?l?l.SubTitles:undefined:n.ColumnTitle.TitleCells;a&&s&&(o=n.ColumnTitle.FindColumnTitlesOfTitleCell(a), +n.MoveTitleTo(a,o,s,r))},StartNewGroup:function(e){var t,r=this,o=r.Recordset,n=r.Groups.items,i=n.length,a=r.Groups.Item(e);if(a){for(o.Prior(),t=i-1;t>=a.index;t--)r.endGroupItem(n[t]);for(o.Next(),t=a.index;i>t;t++)r.beginGroupItem(n[t])}},ColumnByShowOrder:function(e){function t(e,r,o){var n,i,a=e.Count;for(n=0;a>n;n++)if(i=e.items[n],i.GroupTitle){if(i=t(i.SubTitles,r,o))return i}else{if(r==o)return i;r++}return undefined}var r=0;return t(this.ColumnTitle.TitleCells,r,e)}},prototypeCopyExtend(DetailGrid,Object);var Parameter=function(e){var t=this;Object.call(t,e),t.DataType=1,t.Name="",t.Format="",t.Value=undefined};Parameter.prototype={afterLoad:function(e){var t=this;enumMemberValid(t,"DataType",grenum.ParameterDataType),t.Value!==undefined&&t.setValue(t.Value)},prepare:function(){var e=this,t=e.DataType,r=e.Format;2===t||3===t?e.formater=new NumericFormatter(r):6===t?e.formater=new DateTimeFormatter(r):5===t&&(e.formater=new BooleanFormatter(r))},unprepare:function(){delete this.formater},setValue:function(e){var t=this;if(null!==e&&e!==undefined)switch(t.DataType){case 2:e=Math.floor(+e);break;case 3:e=+e;break;case 5:e=confirmBooleanValue(e);break;case 6:e=confirmDateValue(e);break;default:e+=""}else e=undefined;t.Value=e},get AsBoolean(){return!!this.Value},set AsBoolean(e){this.setValue(e)},get AsDateTime(){return confirmCloneDateValue(this.Value)},set AsDateTime(e){this.setValue(e)},get AsFloat(){var e=this,t=e.Value;if(6===e.DataType){var r=new DateTime;r.value=t,t=r.AsFloat}else t=+t;return isNaN(t)?0:t},set AsFloat(e){var t,r=this;6===r.DataType&&(t=new DateTime,t.AsFloat=e,e=t.value),r.setValue(e)},get AsInteger(){return Math.floor(this.AsFloat)},set AsInteger(e){this.AsFloat=Math.floor(+e)},get AsString(){var e=this.Value;return e===undefined?"":e+""},set AsString(e){this.setValue(e)},get DisplayText(){var e=this,t=e.Value;if(t===undefined)return"";switch(e.DataType){case 1:return t+"";default:return e.formater.format(t)}},get IsNull(){return this.Value===undefined},Clear:function(){this.Value=undefined}},prototypeCopyExtend(Parameter,Object);var Report=function(e,t){var r=this;e=e||{alpha:{background:1}},r.viewer=e,r.report=r,t&&(r.ownerSR=t),r.Font=new FontWrapper(undefined),r.Font.font=new Font,r.Parameters=new Parameters(r),r.ReportHeaders=new ReportHeaders(r),r.ReportFooters=new ReportFooters(r),r.XmlTableName="",r.Unit=1,r.CodePage=65001,r.BackColor=16777215,r.QuerySQL="",r.Title="",r.Author="",r.Description="",r.GlobalScript="",r.InitializeScript="",r.ProcessBeginScript="",r.ProcessEndScript="",r.BeforeSortScript="",r.Printer={DesignPaperWidth:21,DesignPaperLength:29.7,DesignPaperLeftMargin:2.5,DesignPaperRightMargin:2.5,DesignPaperOrientation:1}};Report.prototype={load:function(e){var t=this;"string"==typeof e&&(t.isWR="_WR"===e.substr(0,3),t.isWR&&(e=gr.wr.decodeWR(e)),e=JSON.parse(e)),t.loadFromJSON(e)},loadFromJSON:function(e){function t(e){return a[n?wrPropNameEncode(e):e]}var r,o=this,n=o.isWR,i=o.Printer,a=e.Printer;o.Clear(),assignJSONMembers(o,e),colorMemberValid(o,"BackColor",o.viewer.alpha.background),o.Font.loadFromJSON(e.Font,n),e.ImageList&&(o._imageList=new ImageList,o._imageList.loadFromJSON(e.ImageList,n)),o.Parameters.loadFromJSON(e.Parameter),o.ReportHeaders.loadFromJSON(e.ReportHeader),o.ReportFooters.loadFromJSON(e.ReportFooter),e.DetailGrid&&o.InsertDetailGrid().loadFromJSON(e.DetailGrid),a&&(r=t("Width"),r&&(i.DesignPaperWidth=r),r=t("Height"),r&&(i.DesignPaperLength=r),r=t("LeftMargin"),r!==undefined&&(i.DesignPaperLeftMargin=r),r=t("RightMargin"),r!==undefined&&(i.DesignPaperRightMargin=r),r=t("Oriention"),r&&(i.DesignPaperOrientation=2),o.Printer=i)},generateHtml:function(){function e(t,r){r&&(t.push(r.beginText()),r.children?(DEBUG&&t.push("\r\n"),r.children.forEach(function(r){e(t,r)})):t.push(r.innerText),t.push(r.endText()),DEBUG&&t.push("\r\n"))}var t=this,r=[],o={htmlElements:[],addChild:function(e){o.htmlElements.push(e)}};return t.generate(o),o.htmlElements.forEach(function(t){e(r,t)}),r.join("")},generate:function(e){function t(){function e(){function e(){function e(e){f=e?tagCSSChild(u):tagCSSChildR(u),p=[],p.push(grconst.CSS_DG_F+" th:"+f),f=C.hasMergedColumn?"#"+l.idMGC:grconst.CSS_DG_F+" td:"+f,p.push(f),n=new HtmlStyles,n.add("position","sticky"),n.add(e?"left":"right",pixelsToHtml(d)),e||u==y&&C.ShowColLine&&n.add("border-left",penStyleText(C.ColLinePen)),a.addCustomStyle(n,p),d+=l.pxWidth}for(d=0,r=0,u=1;T>=u;u++)l=v[u-1],e(1);for(d=0,u=1;y>=u;u++)l=v[S-u],e(0);for(p=[],u=1;T>=u;u++)p.push(grconst.CSS_DG_F+" th:"+tagCSSChild(u));for(u=1;y>=u;u++)p.push(grconst.CSS_DG_F+" th:"+tagCSSChildR(u));n=new HtmlStyles,n.add("z-index","3"),a.addCustomStyle(n,p)}function t(){function e(e,t){function r(o,n){var a,s,c,u=o.items,f=u.length,p=0;for(i.length<=n?(s=[],i.push(s)):s=i[n];e>l&&f>p;)a=u[t?p:f-1-p],++p,c={},c.pos=d,s.push(c),a.GroupTitle?r(a.SubTitles,n+1):(++l,d+=a.Column.pxWidth)}var i=[],l=0;d=0,r(o,0),i.forEach(function(e,r){var o=e.length,i=tagCSSChild(++r);e.forEach(function(e,r){var l;++r,l=t?tagCSSChild(r):tagCSSChildR(r),n=new HtmlStyles,n.add("position","sticky"),n.add(t?"left":"right",pixelsToHtml(e.pos)),n.add("z-index","3"),t||r==o&&C.ShowColLine&&n.add("border-left",penStyleText(C.ColLinePen)),a.addCustomStyle(n,grconst.CSS_DG_F+" thead tr:"+i+" th:"+l)})})}for(d=0,u=1;T>=u;u++)n=new HtmlStyles,n.add("position","sticky"),n.add("left",pixelsToHtml(d)),l=v[u-1],f=C.hasMergedColumn?"#"+l.idMGC:grconst.CSS_DG_F+" td:"+tagCSSChild(u),a.addCustomStyle(n,f),d+=v[u-1].pxWidth;for(d=0,u=1;y>=u;u++)n=new HtmlStyles,n.add("position","sticky"),n.add("right",pixelsToHtml(d)),u==y&&C.ShowColLine&&n.add("border-left",penStyleText(C.ColLinePen)),a.addCustomStyle(n,grconst.CSS_DG_F+" td:"+tagCSSChildR(u)),d+=v[S-u].pxWidth;e(T,1),e(y,0)}var r,o=b.TitleCells;for(u=0;T>u;u++)v[u].ContentCell.fixed=1;for(u=S-y;S>u;u++)v[u].ContentCell.fixed=1;x>1?t():e()}function t(){for(var e,t,r,o,i,s,c,u,f,p,h=C.Groups,g=h.Count,m=C.rows,b=m.length,x=S-1,F=x-y,w=v[x],D=w.pxLeft+w.pxWidth,P=1;g>=P;){if(e=h.Item(P++),!e.PageGroup&&(t=e.Footer,t.pxHeight&&t.Visible)){e.ByFields&&(t=0);break}t=0}if(t&&!t.tableCols&&(r=t.tableRows))for(m[b-1].tfoot=1,d=0,u=r.length,P=u-1;P>=0;){if(o=r[P--],f=P+2,n=new HtmlStyles,n.add("position","sticky"),n.add("bottom",pixelsToHtml(d)),1==f&&C.ShowRowLine&&n.add("border-top",penStyleText(C.RowLinePen)),f==u&&C.ShowRowLine&&n.add("border-bottom",penStyleText(C.RowLinePen)),a.addCustomStyle(n,grconst.CSS_DG_F+" tfoot tr"+(u>1?":"+tagCSSChild(f):"")+" td"),d+=o.pxHeight,i=o.cells,c=i.length,i.forEach(function(e){e.control&&(e=e.control),e.fixed=1}),T)for(p=0;c>p&&(s=i[p++],!(s.endCol>T));)l=v[s.beginCol],n=new HtmlStyles,n.add("position","sticky"),n.add("z-index","3"),n.add("left",pixelsToHtml(l.pxLeft)),a.addCustomStyle(n,grconst.CSS_DG_F+" tfoot tr"+(u>1?":"+tagCSSChild(f):"")+" td:"+tagCSSChild(p));if(y)for(p=0;y>p&&(s=i[c-1-p++],!(s.beginCol1?":"+tagCSSChild(f):"")+" td:"+tagCSSChildR(p))}}var n,l,u,d,f,p,h=a.holder,g=h.clientWidth,m=h.clientHeight,C=r,b=C.ColumnTitle,x=b.layerCount,v=C.showingColumns,S=v.length,T=Math.min(i.fixedColsLeft,S-1),y=Math.min(i.fixedColsRight,S-1-T);if(!g){for(f=h;!g&&(f=f.parentElement);)g=f.clientWidth;m||(g=document.body.clientWidth,f=window.offsetLeft,f>0&&window.innerWidth<=g+3*f&&(g-=2*f))}if(!m){for(f=h;!m&&(f=f.parentElement,f&&f.parentElement);)m=f.clientHeight;m||(m=window.innerHeight,m-=18)}for(m-=calcSectionsPixelTotalHeight(s),m-=calcSectionsPixelTotalHeight(c),n=new HtmlStyles,n.add("overflow","auto"),n.addBorder(C.Border),n.add("width",pixelsToHtml(g)),n.add("height",pixelsToHtml(m)),o=a.addCustomStyle(n),a.dgDivClass=o,d=0,u=1;x>=u;++u)n=new HtmlStyles,n.add("position","sticky"),n.add("top",pixelsToHtml(d)),a.addCustomStyle(n,grconst.CSS_DG_F+" thead tr"+(x>1?":"+tagCSSChild(u):"")+" th"),d+=b.pxHeight;(T>0||y>0)&&e(),a.options.fixedFooter&&t()}var r,o,n,i=this,a=i.viewer,l=i.fixed,s=i.ReportHeaders,c=i.ReportFooters;i.fireInitialize(),!i.ownerSR&&i.matchTables(),i.DetailGrid&&i.fireFetchRecord(),i.fireProcessBegin(),!i.ownerSR&&i.prepare(),i.singleChart?i.singleChart.generateContent(e):(r=i.RunningDetailGrid,i.reportFitHeight&&calcSectionsHeightPercent(s.items.concat(c.items)),r&&(r.index=a._detailgrids.length,r.buildRows(i)),l?(t(),s.Count&&(n=new HtmlElement("div",e),s.generate(n)),n=new HtmlElement("div",e),n.addClass(a._getCSSName(o)),r.generate(n),c.Count&&(n=new HtmlElement("div",e),c.generate(n))):(s.generate(e),r&&r.generate(e),c.generate(e))),!r&&i.fireProcessEnd()},matchTables:function(){function e(e,t){function r(e){function t(e,t){var r=this;r.isRecordset=e,r.tableName=t.XmlTableName,r.object=t}var o,n,i;if(e)for(v.push(new t(!1,e)),e.DetailGrid&&v.push(new t(!0,e.DetailGrid.Recordset)),n=e.FindFirstControl();n;)i=n.ControlType,11===i?(o=n.Recordset,o.Fields.Count>0&&v.push(new t(!0,o))):9===i&&r(n.Report),n=e.FindNextControl()}function o(e,t,r){function o(e,t){var r,o=e.length,n=0;for(r=0;o>r;r++)t.indexOf(e[r])>=0&&n++;return 100*n/o}var n=t.recordCount,i=t.fieldNames,a=0;if(e.length&&t.fieldNames.length){if(a=o(e,i),!a)return 0;100===a&&e.length===i.length?a=200:a+=o(i,e),a+=r&&n>1||!r&&1===n?5:-5}return a}function n(e){return e>=200}function i(e){var t,r,o,n=[];for(t=e.Parameters.items.length;t--;)n.push(e.Parameters.items[t].Name.toUpperCase());for(r=e.FindFirstControl();r;)o=r.ControlType,1===o||8===o||7===o||12===o?n.push(r.Name.toUpperCase()):13===o&&r.cells.forEach(function(e){e.forEach(function(e){e.DataName&&n.push(e.DataName.toUpperCase())})}),r=e.FindNextControl();return n}function a(e,t){for(var r,o,n=0,i=e.length;i>n;)o=e[n++],(!r||o.weight>r.weight)&&o.dataTableInfo.matchWeight<50&&(r=o);r&&l(r.weight,t,r.dataTableInfo)}function l(e,t,r){r.matchWeights;++s){if(f=v[s],h=f.tableName){for(c=0;d>c&&t[c].tableName!=h;)++c;if(d>c){t[c].turnToFullMatched(f);continue}f.tableName=""}if(f.isRecordset){for(b=f.object.Fields.items,x=[],c=0,d=b.length;d>c;)x.push(b[c++].RunningDBField.toUpperCase());for(m=[],d=t.length,c=0;d>c;)if(g=t[c++],!g.isFullMatched()){if(C=o(x,g,!0),n(C)){l(C,f,g),m=[];break}C>0&&m.push({dataTableInfo:g,weight:C})}m.length&&a(m,f)}}if(t.some(function(e){return!e.isMatched()}))for(u=v.length,s=0;u>s;++s)if(f=v[s],!f.isRecordset&&!f.tableName){for(x=i(f.object),m=[],d=t.length,c=0;d>c;)if(g=t[c++],!g.isFullMatched()){if(C=o(x,g,!1),n(C)){l(C,f,g),m=[];break}p=g.reportTableInfo,C>0&&(!p||p.isRecordset&&p.weight+50s;)g=t[s++],f=g.reportTableInfo,f&&f.object.attachDataTable(g.table)}var t=this,r=t.viewer.tables,o=[],n=function(e,t){var r,o=this,n=o.fieldNames=[],i=Math.min(t.length,50);for(o.tableName=e,o.table=t,o.matchWeight=0;i--;){r=t[i];for(var a in r)a=a.toUpperCase(),n.indexOf(a)<0&&n.push(a)}};n.prototype={isFullMatched:function(){return this.matchWeight>=200},turnToFullMatched:function(e){var t=this;t.matchWeight=200,t.reportTableInfo=e},isMatched:function(){return this.matchWeight>=40},get recordCount(){return this.table.length}};for(var i in r){var a=r[i];a&&a instanceof Array&&o.push(new n(i,a))}1===o.length&&o[0].recordCount>1&&t.DetailGrid?t.DetailGrid.Recordset.attachDataTable(o[0].table):e(t,o)},attachDataTable:function(e){var t,r,o=this,n=e[0];for(var i in n)t=n[i],r=o.ParameterByName(i),r?r.setValue(t):(r=o.ControlByName(i),r&&(r.hasOwnProperty("Text")||7===r.ControlType)&&(r.Text=t+""))},prepare:function(){var e=this,t=e.Printer,r=e.ReportHeaders,o=e.ReportFooters;e._scriptFailed=0,e.designWidth=e.size2Pixel((1===t.DesignPaperOrientation?t.DesignPaperWidth:t.DesignPaperLength)-t.DesignPaperLeftMargin-t.DesignPaperRightMargin),e.DetailGrid&&e.DetailGrid.attachData(),r.attachData(),o.attachData(),e.Parameters.prepare(),e.DetailGrid&&e.DetailGrid.prepare(),r.prepare(),o.prepare(),e._srChildren&&e._srChildren.forEach(function(e){e.Report.prepare()}),e.ownerSR||!e.viewer.options.singleChartFill||e.DetailGrid||r.Count+o.Count!==1||1!==(r.items[0]||o.items[0]).Controls.Count||(e.singleChart=(r.items[0]||o.items[0]).Controls.items[0],11!==e.singleChart.ControlType&&delete e.singleChart)},unprepare:function(){var e=this;e.Parameters.unprepare(),e.DetailGrid&&(e.DetailGrid.unprepare(),e.RunningDetailGrid=e.DetailGrid),e.ReportHeaders.unprepare(),e.ReportFooters.unprepare(),delete e._rsns,delete e._srChildren,delete e._has_cb,delete e.singleChart,delete e.canvasControls},allControls:function(e,t){function r(t){t.FreeCell?u=u.concat(t.Controls.items):e&&u.push(t)}function o(e){e.forEach(function(e){r(e),e.GroupTitle&&o(e.SubTitles)})}function n(){var e=[];return u.forEach(function(t){var r=t.AsFreeGrid;r&&r.cells.forEach(function(t){t&&t.forEach(function(t){t.FreeCell&&(e=e.concat(t.Controls.items))})})}),e}var i,a,l,s=this,c=s.DetailGrid,u=[];for(a=s.ReportHeaders.Count,i=0;a>i;i++)u=u.concat(s.ReportHeaders.items[i].Controls.items);for(a=s.ReportFooters.Count,i=0;a>i;i++)u=u.concat(s.ReportFooters.items[i].Controls.items);if(c){for(t&&s.RunningDetailGrid&&(c=s.RunningDetailGrid),a=c.Groups.Count,i=0;a>i;i++)l=c.Groups.items[i],u=u.concat(l.Header.Controls.items),u=u.concat(l.Footer.Controls.items);c.ColumnContent.ContentCells.forEach(function(e){r(e)}),o(c.ColumnTitle.TitleCells)}return u=u.concat(n())},getUsingFont:function(){return this.Font.font},getRunningColumn:function(e){var t,r,o=this.RunningDetailGrid;return e&&o&&(t=o.Columns,r=t.itemByName(e),r||("(GridLeft)"===e?r=t.Item(1):"(GridRight)"===e&&(r=t.Item(t.Count)))),r},getReportWidth:function(){var e=this,t=e.designWidth,r=this.RunningDetailGrid,o=r?r.width:0;return 2*o>t?o:t},get reportFitHeight(){var e=this;return!e.RunningDetailGrid&&e.viewer.options.reportFitHeight},get reportFitWidth(){var e=this.viewer.options;return e.reportFitWidth||e.fixed},get detailgridResize(){return this.viewer.options.detailgridResize},get fixed(){var e=this;return e.DetailGrid&&e.viewer.options.fixed},get fixedColsLeft(){var e=this,t=e.DetailGrid,r=e.viewer.options.fixedColsLeft;return t&&0==r?t.FixCols:r},get fixedColsRight(){var e=this,t=e.DetailGrid,r=t?t.CrossTab:undefined,o=e.viewer.options.fixedColsRight;return r&&0==o?r.TotalCols:o},get controlPosition(){var e=this;return e.fixed?gr.enum_.controlLayout.table:e.viewer.controlPosition},get toFillHolder(){var e=this;return e.reportFitHeight||e.singleChart},get isDynamicSize(){var e=this;return e.toFillHolder||e.reportFitWidth||e.detailgridResize},pixel2Size:function(e){return e/=96,1===this.Unit&&(e*=2.54),e},size2Pixel:function(e){return 1===this.Unit&&(e/=2.54),96*e},cm2Size:function(e){return 1===this.Unit?e:e/2.54},controlDynamicSize:function(e){return this.isDynamicSize&&e.AsChart},registerCanvas:function(e,t){var r=this,o=new HtmlElement("canvas",t),n=uidCanvas();o.addAttribute("id",n),r.controlDynamicSize(e)||!e.pxRect?(o.addStyle("width","100%"),o.addStyle("height","100%"),o.addStyle("display","block")):(o.addAttribute("width",Math.round(e.pxRect.Width())+""),o.addAttribute("height",Math.round(e.pxRect.Height())+"")),e.canvasID=n,r.canvasControls||(r.canvasControls=[]),r.canvasControls.push(e)},prepareCanvas:function(){for(var e,t=this,r=t.canvasControls,o=r.length;o-- >0;)e=r[o],e.canvas=document.getElementById(e.canvasID),e.prepareCanvas&&e.prepareCanvas()},resizeCanvas:function(){for(var e=this,t=e.canvasControls,r=t.length;r-- >0;)t[r].resizeCanvas()},renderCanvas:function(){for(var e=this,t=e.canvasControls,r=t.length;r-- >0;)t[r].drawCanvas(1)},startCanvas:function(){var e=this;e.canvasControls&&(e.prepareCanvas(),e.isDynamicSize?(addEvent(window,"resize",function(t){e.resizeCanvas()}),e.resizeCanvas()):e.renderCanvas()),e._srChildren&&e._srChildren.forEach(function(e){e.Report.startCanvas()})},executeEventScript:function(scriptText,scriptName,Sender){function prepareScript(){function addFun(e,t,r){var o;r&&(o="f"+no++,code+="_rsns."+o+"=function(Sender){\n"+r+"\n}\n",e[script.nameMember(t)]=o)}function addRecordset(e){addFun(e,script.names.BeforePostRecord,e.BeforePostRecordScript),addFun(e,script.names.FetchRecord,e.FetchRecordScript),addFun(e,script.names.PageProcessRecord,e.PageProcessRecordScript),addFun(e,script.names.ProcessRecord,e.ProcessRecordScript),e.Fields.items.forEach(function(e){addFun(e,script.names.FieldGetDisplayText,e.GetDisplayTextScript)})}function addSection(e){addFun(e,script.names.Format,e.FormatScript)}var detailGridRunning=self.RunningDetailGrid,detailGrid=detailGridRunning?detailGridRunning:self.DetailGrid,no=1,code="(function(Report) {\nvar _rsns=Report._rsns={};\n";code+=self.GlobalScript+"\n",addFun(self,script.names.Initialize,self.InitializeScript),addFun(self,script.names.ProcessBegin,self.ProcessBeginScript),addFun(self,script.names.ProcessEnd,self.ProcessEndScript),addFun(self,script.names.BeforeSort,self.BeforeSortScript),self.allControls(1,1).forEach(function(e){var t;addFun(e,script.names.CustomDraw,e.CustomDrawScript),e.AsTextBox?addFun(e,script.names.TextBoxGetDisplayText,e.GetDisplayTextScript):e.AsChart&&(addRecordset(e.Recordset),t=e._clones,t&&t.forEach(function(e){addRecordset(e.Recordset)}))}),self.ReportHeaders.items.concat(self.ReportFooters.items).forEach(function(e){addSection(e)}),detailGrid&&(addSection(detailGrid.ColumnTitle),addSection(detailGrid.ColumnContent),addRecordset(detailGrid.Recordset),detailGrid.Groups.forEach(function(e){addFun(e,script.names.GroupBegin,e.GroupBeginScript),addFun(e,script.names.GroupEnd,e.GroupEndScript),addSection(e.Header),addSection(e.Footer)})),code+="})(self);",eval(code)}var self=this,detailgrid=self.DetailGrid,x;!self._rsns&&prepareScript(),detailgrid&&detailgrid.IsCrossTab&&!self.rsRedo&&(x=Sender[script.nameMember(scriptName)],x||(delete self._rsns,prepareScript(),self.rsRedo=1)),script.execEvent(self,Sender,scriptName)},fireInitialize:function(){var e=this,t=e.InitializeScript;t&&e.executeEventScript(t,script.names.Initialize,e),e.OnInitialize&&e.OnInitialize()},fireFetchRecord:function(){var e=this,t=e.FetchRecordScript;t&&e.executeEventScript(t,script.names.FetchRecord,e.DetailGrid.Recordset),e.OnFetchRecord&&e.OnFetchRecord()},fireBeforePostRecord:function(){var e=this;e.OnBeforePostRecord&&e.OnBeforePostRecord()},fireBeforeSort:function(e,t){var r=this,o=r.BeforeSortScrip;o&&r.executeEventScript(o,script.names.BeforeSort,t),r.OnBeforeSort&&t.isDetailGridRecordset()&&r.OnBeforeSort()},fireProcessBegin:function(){var e=this,t=e.ProcessBeginScript;t&&e.executeEventScript(t,script.names.ProcessBegin,e),e.OnProcessBegin&&e.OnProcessBegin()},fireProcessEnd:function(){var e=this,t=e.ProcessEndScript;t&&e.executeEventScript(t,script.names.ProcessEnd,e),e.OnProcessEnd&&e.OnProcessEnd()},fireProcessRecord:function(e){var t=this,r=e.ProcessRecordScript;r&&t.executeEventScript(r,script.names.ProcessRecord,e),t.OnProcessRecord&&t.OnProcessRecord()},fireGroupBegin:function(e){var t=this,r=e.GroupBeginScript;r&&t.executeEventScript(r,script.names.GroupBegin,e),t.OnGroupBegin&&t.OnGroupBegin(e)},fireGroupEnd:function(e){var t=this,r=e.GroupEndScript;r&&t.executeEventScript(r,script.names.GroupEnd,e),t.OnGroupEnd&&t.OnGroupEnd(e)},fireSectionFormat:function(e){var t=this,r=e.FormatScript;r&&t.executeEventScript(r,script.names.Format,e),t.OnSectionFormat&&t.OnSectionFormat(e)},fireFieldGetDisplayText:function(e){var t=this,r=e.GetDisplayTextScript;r&&t.executeEventScript(r,script.names.FieldGetDisplayText,e),t.OnFieldGetDisplayText&&t.DetailGrid&&t.RunningDetailGrid.Recordset===e.owner&&t.OnFieldGetDisplayText(e)},fireTextBoxGetDisplayText:function(e){var t=this,r=e.GetDisplayTextScript;r&&t.executeEventScript(r,script.names.TextBoxGetDisplayText,e),t.OnTextBoxGetDisplayText&&t.OnTextBoxGetDisplayText(e)},fireControlCustomDraw:function(e){var t=this,r=e.CustomDrawScript;context=t.Graphics,fontWrapper=e.Font,font=fontWrapper?fontWrapper.UsingFont():0,context=new Context(e.canvas.getContext("2d")),!context&&(context=t.Graphics=new Graphics(t)),context.attach(context),font&&context.selectFont(font),r&&t.executeEventScript(r,script.names.CustomDraw,e),t.OnControlCustomDraw&&t.OnControlCustomDraw(e,context),font&&context.restoreFont()},fireChartRequestData:function(e){var t=this;t.OnChartRequestData&&t.OnChartRequestData(e)},onCheckBoxClick:function(e){var t,r,o,n,i,a,l,s,c,u,d=this,f=d.DetailGrid,p=f.Recordset,h=e.target,g=h.id,m=h.checked,C=m?-1:-2,b=h.parentNode.parentNode;if(f&&(t=f.syncTRData(b),i=h.getAttribute(grconst.ATTR_FIELD),i?(p.Edit(),p.FieldByName(i).AsInteger=C,p.Post()):(s=d.report.ControlByName(g),s&&(s.ImageIndex=C)),r=t.group))for(o=r.beginNoField.AsInteger,n=r.endNoField.AsInteger,l=document.querySelectorAll("."+grconst.CSS_CB),a=l.length;a-- >0;)s=l[a],g=s.id,c="CBC"===g.substr(0,3),c&&(u=+g.substr(3),u>=o&&n>=u&&(s.checked=m,i=s.getAttribute(grconst.ATTR_FIELD),i&&(p.moveTo(u),p.Edit(),p.FieldByName(i).AsInteger=C,p.Post())))},get root(){for(var e=this;e.ownerSR;)e=e.ownerSR.report;return e},get Version(){return grconst.VERSION},get FloatControls(){var e=this;return e._floatControls||(e._floatControls=new Controls(e)),e._floatControls},get ImageList(){var e=this;return e._imageList||(e._imageList=new ImageList),e._imageList},get ParentReport(){var e=this.ownerSR;return e?e.report:undefined},get Utility(){return gr.Utility},get Running(){return!!this.viewer.running},get DisplayMode(){return 1},get IsBlank(){var e=this;return e.DetailGrid&&e.PageHeader&&e.PageFooter&&!e.ReportHeaders.Count&&!e.ReportFooters.Count},ControlByName:function(e){function t(r){function o(r){function o(r){return r.FreeCell?t(r.Controls):r.DataName.toUpperCase()===e?r:undefined}for(var n,i,a,l,s=r.length;s--;)if(a=r[s])for(n=a.length;n--;)if(l=a[n],i=l?o(l):0)return i;return undefined}for(var n,i=r.items,a=i.length;a--;){if(n=i[a],n.Name.toUpperCase()===e)return n;if(13===n.ControlType&&(n=o(n.cells)))return n}return undefined}function r(e){return e.FreeCell?t(e.Controls):undefined}function o(e){for(var t,n,i=e.items.length;i--;)if(t=e.items[i],n=r(t),t.GroupTitle&&!n&&(n=o(t.SubTitles)),n)return n;return undefined}function n(e){for(var r=e.length;r--;)if(s=t(e[r].Controls))return s}var i,a,l,s,c=this;if(e=e.toUpperCase(),s=n(c.ReportHeaders.items)||n(c.ReportFooters.items))return s;if(c.DetailGrid){for(i=c.DetailGrid.Groups.items,a=i.length;a--;)if(l=i[a],s=t(l.Header.Controls)||t(l.Footer.Controls))return s;for(i=c.DetailGrid.ColumnContent.ContentCells.items,a=i.length;a--;)if(s=r(i[a]))return s;return o(c.DetailGrid.ColumnTitle.TitleCells)}return undefined},ColumnByName:function(e){var t=this;return t.DetailGrid?t.DetailGrid.Columns.itemByName(e):undefined},FieldByName:function(e){return this.RunningFieldByName(e)},FieldByDBName:function(e){var t=this.RunningDetailGrid;return t?t.Recordset.FieldByDBName(e):undefined},ParameterByName:function(e){return this.Parameters.itemByName(e)},FindFirstControl:function(){var e=this,t=e.allControls();return t.length>0&&(e.findControls=t,e.findControlIndex=-1),e.FindNextControl()},FindNextControl:function(){var e=this;return e.findControls&&(++e.findControlIndex,e.findControlIndex>=e.findControls.length&&(delete e.findControls,delete e.findControlIndex)),e.findControls?e.findControls[e.findControlIndex]:undefined},RunningFieldByName:function(e){var t=this.RunningDetailGrid;return t?t.Recordset.FieldByName(e):undefined},PixelsToUnit:function(e){return this.pixel2Size(e)},UnitToPixels:function(e){return this.size2Pixel(e)},SystemVarValue:function(e){return this.SystemVarValue2(e,0)},SystemVarValue2:function(e,t){var r,o,n,i=this,a=i.RunningDetailGrid;if(1===e)r=new Date;else if(a)switch(o=a.Recordset,e){case 4:r=o.RecordNo+1;break;case 19:r=o.RecordCount;break;case 8:r=a.generatingRowNo;break;default:n=a.Groups.items[t-1],n&&(21===e?r=n.recordset.RecordCount:(n.inphaseByDetail(o.RecordNo),r=20===e?n.recordset.isAppendingRecord()?n.recordset.RecordCount:n.recordset.RecordNo:22===e?o.RecordNo-n.beginNoField.Value:n.endNoField.Value-n.beginNoField.Value,r++))}return r},AddParameter:function(e,t){var r=this.Parameters.Add();return r.Name=e,r.DataType=t,r},Clear:function(){var e=this;Report.call(e,e.viewer,e.ownerSR),e.DeleteDetailGrid()},InsertDetailGrid:function(){var e=this;return e.DetailGrid||(e.DetailGrid=new DetailGrid(e),e.RunningDetailGrid=e.DetailGrid),e.DetailGrid},InsertPageHeader:function(){},InsertPageFooter:function(){},DeleteDetailGrid:function(){var e=this;e.DetailGrid=undefined,e.RunningDetailGrid=undefined},DeletePageHeader:function(){},DeletePageFooter:function(){},CancelLoadData:function(){},PrepareLoadData:function(){return 1}};var csz_HTotal_Suffix="",csz_HAverage_Suffix="_hta",csz_HAverageA_Suffix="_htaa",csz_HCount_Suffix="_htc",csz_HCountA_Suffix="_htca",csz_HMin_Suffix="_htn",csz_HMax_Suffix="_htx",csz_HPercent_Suffix="_hp",csz_VPercent_Suffix="_vp",csz_HTotalPercent_Suffix="_htp",csz_VTotalPercent_Suffix="_vtp",HSummaryFlag=function(){this.Flag=0};HSummaryFlag.prototype={get CalcHTotal(){return 1&this.Flag},set CalcHTotal(e){var t=this;e?t.Flag|=1:t.Flag&=-2},get CalcHAverage(){return 2&this.Flag},set CalcHAverage(e){var t=this;e?t.Flag|=2:t.Flag&=-3},get CalcHAverageA(){return 4&this.Flag},set CalcHAverageA(e){var t=this;e?t.Flag|=4:t.Flag&=-5},get CalcHCount(){return 8&this.Flag},set CalcHCount(e){var t=this;e?t.Flag|=8:t.Flag&=-9},get CalcHCountA(){return 16&this.Flag},set CalcHCountA(e){var t=this;e?t.Flag|=16:t.Flag&=-17},get CalcHMin(){return 32&this.Flag},set CalcHMin(e){var t=this;e?t.Flag|=32:t.Flag&=-33},get CalcHMax(){return 64&self.Flag},set CalcHMax(e){var t=this;e?t.Flag|=64:t.Flag&=-65},onBySummaryFun:function(e){var t=this;t.CalcHTotal=!0,2===e?t.CalcHAverage=!0:15===e?t.CalcHAverageA=!0:3===e?t.CalcHCount=!0:13===e?t.CalcHCountA=!0:4===e?t.CalcHMin=!0:5===e&&(t.CalcHMax=!0)}};var DataFieldInfo=function(e){var t=this;t.originObject=undefined,t.HTotalFlag=new HSummaryFlag,t.HSubtotalFlag=new HSummaryFlag,t.MiscFlag=0,t.DatafieldIndex=-1,t.TotalValue=0,t.EntireTotalValue=0,e&&assignObjectEx(t,e,0)};DataFieldInfo.prototype={CopyAddField:function(e,t){var r=this,o=e.Fields.Add();return r.originObject.FieldType?(o.assign(r.originObject),r.originObject.isNumeric()&&(o.FieldType=3)):r.NumericField&&(o.FieldType=3,o.Format=r.originObject.textBuilder.items[0].format),o.Name=t,o.DBFieldName="",o._tableMember=t,o},CombineCalcFlag:function(e){var t=this;t.HTotalFlag.Flag|=e.HTotalFlag.Flag,t.HSubtotalFlag.Flag|=e.HSubtotalFlag.Flag,t.MiscFlag|=e.MiscFlag},SetFlagBySummaryFun:function(e,t){var r=this;3===t?r.HSubtotalFlag.onBySummaryFun(e):r.HTotalFlag.onBySummaryFun(e)},get CalcHPercent(){return 1&this.MiscFlag},set CalcHPercent(e){var t=this;e?t.MiscFlag|=1:t.MiscFlag&=-2},get CalcVPercent(){return 2&this.MiscFlag},set CalcVPercent(e){var t=this;e?t.MiscFlag|=2:t.MiscFlag&=-3},get CalcTotalHPercent(){return 4&this.MiscFlag},set CalcTotalHPercent(e){var t=this;e?t.MiscFlag|=4:t.MiscFlag&=-5},get CalcTotalVPercent(){return 8&this.MiscFlag},set CalcTotalVPercent(e){var t=this;e?t.MiscFlag|=8:t.MiscFlag&=-9},get CalcHTotalExclude(){return 16&this.MiscFlag},set CalcHTotalExclude(e){var t=this;e?t.MiscFlag|=16:t.MiscFlag&=-17},get NumericField(){return 32&this.MiscFlag},set NumericField(e){var t=this;e?t.MiscFlag|=32:t.MiscFlag&=-33}};var HPercentSum=function(e,t){var r=this;r.Group=e,r.DataFieldInfo=t,r.DataSumBoxes=[]},DynamicTitleCellInfo=function(e){var t=this;t.PriorCrossTitleCell=undefined,t.OriginTextBox=e,t.PriorTitleText=""},CrossTab=function(e){var t=this;Object.call(t,e),t.HCrossFields="",t.VCrossFields="",t.ListCols=1,t.TotalCols=0,t.SubtotalCols=0,t.HResort=!0,t.HSortAsc=!0,t.VResort=!0,t.VSortAsc=!0,t.HTotalAtFirst=!1,t.HPercentColumns="",t.VPercentColumns="",t.TotalExcludeColumns="",t.TotalHPercentColumns="",t.TotalVPercentColumns="",t.DisabledSumFields="",t.PercentFormat="0.##%",t.HCrossPeriodType=0,t.BeginDateParameter="",t.EndDateParameter="",t.GroupAutoSum=!0};CrossTab.prototype={loadFromJSON:function(e){var t=this;e&&(Object.prototype.loadFromJSON.call(t,e),enumMemberValid(t,"HCrossPeriodType",grenum.PeriodType))},prepare:function(){function e(){var e=l.ParameterByName(a.BeginDateParameter),t=l.ParameterByName(a.EndDateParameter),r={};return e&&(r.begin=e.AsDateTime),t?(r.end=t.AsDateTime,e||(r.begin=cloneDate(r.end))):r.end=cloneDate(r.begin),r.endt?1:Z>t?2:q>t?3:4}function t(t,r){function o(e){se.indexOf(e.originObject)<0&&(S.some(function(t){return t.originObject!==e.originObject?!1:(t.CombineCalcFlag(e),!0)})||(e.NumericField=e.originObject.isNumeric(),S.push(e)))}function n(e,t){e?e.varItems.forEach(function(e){var r=e.varField;1===r.type&&(t.originObject=r.object,o(t))}):(t.originObject=S[0].originObject,o(t))}var i,a=new DataFieldInfo,l=e(r),s=1!==l,c=t.ControlType;4===l?(a.HTotalFlag.CalcHTotal=1,le.indexOf(r)>=0&&(a.CalcHTotalExclude=1),ie.indexOf(r)>=0&&(a.CalcTotalHPercent=1),ae.indexOf(r)>=0&&(a.CalcTotalVPercent=1)):3===l?a.HSubtotalFlag.CalcHTotal=1:2===l&&(oe.indexOf(r)>=0&&(a.CalcHPercent=1),ne.indexOf(r)>=0&&(a.CalcVPercent=1),a.CalcHPercent&&(a.HTotalFlag.CalcHTotal=1)),4===c||7===c?s&&(a.originObject=t.oDataField,a.originObject&&o(a)):5===c?(a.SetFlagBySummaryFun(t.SummaryFun,l),n(t.paramExp,a)):12===c?s&&(a.originObject=t,S.push(a)):8===c&&(i=!1,t.textBuilder.forEach(function(e){e.varItems&&e.varItems.forEach(function(e){var t,r=e.varField;4===r.type?(t=new DataFieldInfo(a),t.SetFlagBySummaryFun(r.SummaryFun,l),n(r.paramExp,t),i=!0):1===r.type&&s&&(t=new DataFieldInfo(a),t.originObject=r.object,o(t))}),!i&&s&&(a.originObject=t,a.NumericField=t.isPureNumericExpression(),S.push(a))}))}function r(e){e.forEach(function(e){e.GroupTitle?r(e.SubTitles.items):e.Column.ContentCell.getControls().forEach(function(r){t(r,e.Column)})})}function o(r){r.Controls.forEach(function(r){var o=g.itemByName(r.AlignColumn)||g.itemByName(r.AlignColumnEx);o&&1!==e(o)&&t(r,o)})}r(C),m.forEach(function(e){o(e.Footer),o(e.Header)}),i=0,S.forEach(function(e){e.DatafieldIndex=i,++i,e.CalcHPercent&&++i,e.CalcVPercent&&++i})}function r(){S.forEach(function(e){e.originObject.FieldType&&u.Fields.Remove(e.originObject.Name)})}function P(e){var t=u.Fields.Add();return t.Name=e,t.FieldType=3,t}function R(){function e(e){e.Controls.items=e.Controls.items.filter(function(e){var t=f.itemByName(e.AlignColumn)||f.itemByName(e.AlignColumnEx);return!t||t.orderNon?"上":21>n?"中":"下",t=o+"月"+t+"旬";break;case 4:t=16>n?"上":"下",t=o+"月"+t+"半月";break;case 6:t=intFixed(r,2)+"-"+(o+2)/3+"季度";break;case 7:t=7>o?"上":"下",t=intFixed(r,"00")+"-"+t+"半年"}return t}var s,c;r=e.ControlType,e.TextFormat?(o=t.Controls.Add(1),o.assign(e),s=n()&&4===r&&e.DataField===a.HCrossFields?i():e.getDisplayText(),o.Text=s):(o=t.Controls.Add(r),o.assign(e),7===r?e.DataField&&(o.DataField="",c=l.FieldByName(e.DataField),c&&(7===c.FieldType?o.Picture=c.Value:(s=c.DisplayText,o.ImageIndex=parseInt(s),isNaN(o.ImageIndex)&&(o.ImageIndex=0,o.ImageFile=s)))):12===r&&(o.Text=e.get_DisplayText())); +})):(t.assign(e),t.Text=e.getDisplayText())}function b(){var n,a,l,s=0;2===r?(n=ce[g-Q],a=n?n[i]:0,s=a&&a.PriorCrossTitleCell&&a.PriorTitleText===a.OriginTextBox.getDisplayText()&&!h):3===r?(s=K==$,a=ce[ce.length-1],a=i=Z||3==r&&Z>E)||ee===re&&(3==r&&E>=q||4==r&&q>E)||(F=f.addTo(t?t.SubTitles:c.ColumnTitle.TitleCells),F.assign(B),F.Name=y,2!==r&&3!==r||(F._crossOrderNo=v),C(e,F.TitleCell),F.ContentCell.assign(B.ContentCell),w=F.ContentCell.getControls(),D=1===w.length,R?(ie.indexOf(B)>=0&&(G.CalcHPercent=1),ae.indexOf(B)>=0&&(G.CalcVPercent=1)):(oe.indexOf(B)>=0&&(G.CalcHPercent=1),ne.indexOf(B)>=0&&(G.CalcVPercent=1)),w.forEach(function(e,t){function l(t){n.forEach(function(r,n){function l(e,t){return e.isToGenerate()&&e.Controls.items.some(function(e){return e.AlignColumn===t.Name||e.AlignColumnEx===t.Name})}function s(r){var o,n,i,a;return r.isToGenerate()&&(o=r.Controls.Add(5),o.assign(e),o.Dock=0,o.TextAlign=e.TextAlign,o.AlignColumn=F.Name,o.DataField=t.summaryDataField,p.ShowColLine&&(n=o.Border,n.Styles|=4,i=p.ColLinePen.Width,a=e.Border,a&&4&a.Styles&&a.Pen.Width>0?i+=a.Pen.Width:n.Pen.Color=p.ColLinePen.Color,n.Pen.Width=i),D?(o.Top=0,o.Height=r.Height,16777215!==F.ContentCell.BackColor&&16777215===r.BackColor&&(o.BackStyle=1,o.BackColor=F.ContentCell.BackColor)):(o.Top=e.Top,o.Height=e.Height,1===e.BackStyle()&&(o.BackStyle=1,o.BackColor=e.BackColor))),o}var c,u,d,f=m[n];l(f.Footer,B)||l(f.Header,B)||(c=s(r.Footer),c||(c=s(r.Header)),c&&(c.Format=t.format,G.CalcHPercent&&(c.Format=a.PercentFormat,u=o(t.originDataField),d=i(r,u),d||(d=new HPercentSum(r,u),a.HPercentSums.push(d)),R?d.TotalSumBox=c:d.DataSumBoxes.push(c))))})}function c(){var r,o,n=e.DataField,i=e.SummaryFun;5===C&&(n||(n=S[0].originObject.Name),o=e.Format,F.ContentCell.Controls?(e=new FieldBox(F.ContentCell),e.assign(w[t]),F.ContentCell.Controls.items[t]=e):F.ContentCell.assign(w[t])),h=d(i,n),e.DataField=h.DataField,7!==C&&(o&&(r=u.Fields.Item(h.DataField),r&&(r.Format=o)),A&&l({summaryDataField:h.SummaryDataField,originDataField:n,format:""}))}function f(){var r,o,n,i,a=!1;a=s&&I(e,T),a||(o=B.ContentCell.getControls()[t],r=(n=o.textBuilder.items[0])&&n.varItems&&n.varItems.some(function(e){return 4===e.varField.type}),r?(i=b(n,!1),e.Text=g(i,n.format),A&&l({summaryDataField:ensureNameText(i),originDataField:"",format:n.format})):P&&(h=d(0,e.Name),e.Text="[#"+h.DataField+"#]",A&&l({summaryDataField:h.SummaryDataField,originDataField:e.Name,format:""})))}var h,C=e.ControlType;4===C||5===C||7===C?c():12===C?1!==r&&(h=d(e.Name,0),e.Text="[#"+h.DataField+"#]"):8===C&&f()}),P&&x())}var T=o?(2===r?"_":"_S")+o:"",y=e.Name+T;e.GroupTitle?b():e.Column.Visible&&x()}function B(){var e=h.Fields;u.Fields.forEach(function(t){T.push({crossField:t,originField:e.itemByName(t.Name)})})}function E(){S.forEach(function(e){function t(e,t,o){r=e.CopyAddField(u,t+o),r.GetDisplayTextScript="",y.push(r)}var r,o=e.HTotalFlag,n=e.originObject.Name;o.CalcHTotal&&t(e,n,csz_HTotal_Suffix),o.CalcHAverage&&t(e,n,csz_HAverage_Suffix),o.CalcHAverageA&&t(e,n,csz_HAverageA_Suffix),o.CalcHCount&&t(e,n,csz_HCount_Suffix),o.CalcHCountA&&t(e,n,csz_HCountA_Suffix),o.CalcHMin&&t(e,n,csz_HMin_Suffix),o.CalcHMax&&t(e,n,csz_HMax_Suffix),e.CalcTotalHPercent&&(r=P(n+csz_HTotalPercent_Suffix),r.Format=a.PercentFormat,y.push(r)),e.CalcTotalVPercent&&(r=P(n+csz_VTotalPercent_Suffix),r.Format=a.PercentFormat,y.push(r))})}function G(){var e=C.length;for(o=re;e>o;o++)A(C[o],undefined,4,undefined,0,!0,-1)}function L(e){var t,r=e.getControls();e.GroupTitle&&1===r.length&&(t=r[0],t.TextFormat&&(ce[ce.length-1].push(new DynamicTitleCellInfo(t)),e.SubTitles.items.length>=1&&L(e.SubTitles.items[0])))}function N(){var e=[];a.HResort&&b.forEach(function(t){e.push({field:t,asc:a.HSortAsc,"case":1})}),W=h.sortRecords(e,1)}function M(e){var t,r;for(t="_"+e,b.forEach(function(e){r=u.Fields.Add(),r.assign(e),r._tableMember="",r.DBFieldName="",r.Name+=t,r._hfval=e.Value,F.push(r)}),S.forEach(function(e){var o=e.originObject.Name+t;y.push(e.CopyAddField(u,o)),e.CalcHPercent&&(r=P(o+csz_HPercent_Suffix),r.Format=a.PercentFormat,y.push(r)),e.CalcVPercent&&(r=P(o+csz_VPercent_Suffix),r.Format=a.PercentFormat,y.push(r))}),o=Q;K>=o;o++)A(C[o],undefined,2,e,0,!1,o)}function O(){function e(){var e,t,r=!0,o=ce[ce.length-1],n=C[Q],i=0;if(ce.length>1)return!1;for(;n.GroupTitle&&!(i>=o.length)&&(e=o[i],r=!!e.PriorCrossTitleCell&&e.PriorTitleText==e.OriginTextBox.getDisplayText())&&(t=n.SubTitles,!(t.Count<=0))&&!(++i>=te);)n=t.Item(1);return!r}function t(){var e,t,r;for(w.push(v),e=w.length,t="_S"+e,S.forEach(function(e){r=e.originObject.Name+t,D.push(e.CopyAddField(u,r+csz_HTotal_Suffix)),e.HSubtotalFlag.CalcHAverage&&D.push(e.CopyAddField(u,r+csz_HAverage_Suffix)),e.HSubtotalFlag.CalcHAverageA&&D.push(e.CopyAddField(u,r+csz_HAverageA_Suffix)),e.HSubtotalFlag.CalcHCount&&D.push(e.CopyAddField(u,r+csz_HCount_Suffix)),e.HSubtotalFlag.CalcHCountA&&D.push(e.CopyAddField(u,r+csz_HCountA_Suffix)),e.HSubtotalFlag.CalcHMin&&D.push(e.CopyAddField(u,r+csz_HMin_Suffix)),e.HSubtotalFlag.IsCalcHMax&&D.push(e.CopyAddField(u,r+csz_HMax_Suffix))}),o=$;ee>=o;o++)A(C[o],undefined,3,e,0,!1,-1)}W.forEach(function(r,o){(r=a.VAddedItemRecordOffsetEnd)&&(h.MoveTo(r),o&&!h.fieldsKeepedValueChanged(b)||(a.SubtotalCols>0&&o&&e()&&(h.MoveTo(W[o-1]),t(),h.MoveTo(r)),h.keepValue(),M(++v)),h.curRecord._CrossNo_=v)}),a.SubtotalCols>0&&t()}function _(){function t(){return z?a.curPeriod=grhelper.periodRangeNext(a.curPeriod,a.HCrossPeriodType):(z=e(),strimDate(z.begin),strimDate(z.end),incDate(z.end),a.curPeriod=grhelper.periodRangeBy(z.begin,a.HCrossPeriodType)),a.curPeriod.begini&&(h.MoveTo(W[i]),o=b[0].AsDateTime,!(o=a.curPeriod.end));)h.curRecord._CrossNo_=v,i++}function V(){a.HPercentSums.forEach(function(e){function t(e){e.isToGenerate()&&(r=e.Controls.Add(5),r.DataField=o)}var r,o;e.TotalSumBox||(o=e.DataFieldInfo.originObject.Name+csz_HTotal_Suffix,t(e.Group.Footer),!r&&t(e.Group.Header),r.Visible=!1,e.TotalSumBox=r)})}function H(){var e=y.splice(0,j);y=y.concat(e)}function I(e,t){var r,o=e.Text,n=o.indexOf(":");return n>0&&o.substring(2,n)===b[0].Name?(r="G"===t?"[#"+Y:o.substr(0,n)+t,r+=o.substr(n),e.Text=r,1):0}function k(){function e(){var e,t=b[0],r=new Date(2009,7,30),o=t.AsDateTime;for(e=0;7>e;++e)t&&(t.AsDateTime=r),M(++v),incDate(r);t.AsDateTime=o}function t(e){var t=b[0];e.Controls.forEach(function(e){4===e.ControlType?t.Name===e.DataField&&(e.DataField=Y):8===e.ControlType&&I(e,"G")})}var r,o,n=c.Groups.items[0];e(),n&&(r=u.Fields.Add(),r.FieldType=2,r.Name=J,x.push(r),n.ByFields=J,r=u.Fields.Add(),r.FieldType=6,r.Format=b[0].Format,r.Name=Y,x.push(r),t(n.Header),t(n.Footer)),g.Count>1&&(o=g.items[1].ContentCell.BackColor,f.Item(1).ContentCell.BackColor=o,f.Item(7).ContentCell.BackColor=o)}var j,W,U,z,J="_Month_",Y="_Date_",X=0,Z=1,q=1,Q=-1,K=-1,$=-1,ee=-1,te=0,re=-1,oe=[],ne=[],ie=[],ae=[],le=[],se=[],ce=[];if(s){if(a.GroupAutoSum=!1,h.Fields.forEach(function(e){6===e.FieldType&&b.push(e)}),!b.length)throw new Error("None date field!")}else{if(oe=g.decodeItems(a.HPercentColumns),ne=g.decodeItems(a.VPercentColumns),ie=g.decodeItems(a.TotalHPercentColumns),ae=g.decodeItems(a.TotalVPercentColumns),le=g.decodeItems(a.TotalExcludeColumns),b=h.decodeFields(a.HCrossFields),!b.length)throw new Error("生成交叉表不成功,没有定义'横向交叉数据字段'!");x=h.decodeFields(a.VCrossFields),se=h.decodeFields(a.DisabledSumFields),X=a.ListCols,q=g.Count-a.TotalCols,Z=q-a.SubtotalCols}if(C.forEach(function(e,t){var r,o=e.findLastColumn();o&&(r=o.orderNo,0>Q&&r>=X&&(Q=t),0>K&&r+1>=Z&&(K=t),0>$&&r>=Z&&($=t),0>ee&&r+1>=q&&(ee=t),0>re&&r>=q&&(re=t))}),0>$&&($=K+1),0>re&&(re=ee+1),q>Z&&K>=$)for(U=g.items[Z].TitleCell;U.SupCell;)++te,U=U.SupCell;if(t(),r(),R(),c.clearColumns(),s)k();else{if(!S.length)throw new Error("生成交叉表不成功,不存在交叉数据项!");if(0>=Q)throw new Error("生成交叉表不成功,不存在交叉数据列!");for(N(),o=0;Q>o;++o)A(C[o],undefined,1,undefined,0,!0,-1);for(B(),E(),j=y.length,a.HTotalAtFirst&&G(),o=Q;K>=o;o++)ce.push([]),L(C[o]);0===a.HCrossPeriodType?O():_(),!a.HTotalAtFirst&&G(),j>0&&H(),V()}}function r(){function t(){var e=[];a.VResort&&(x.forEach(function(t){e.push({field:t,asc:a.VSortAsc,"case":1})}),b.forEach(function(t){e.push({field:t,asc:a.HSortAsc,"case":1})})),d=h.sortRecords(e,1)}function r(e,t){S.forEach(function(r,o){var n,a=i*e+r.DatafieldIndex;r.NumericField?(n=r.originObject.AsFloat,y[a].AsFloat+=n,r.CalcHTotalExclude&&r.TotalValue||(r.TotalValue+=n,t&&(t[e*S.length+o]+=n))):y[a].IsNull&&(y[a].Value=r.originObject.Value)})}function o(){u.Append(),T.forEach(function(e){e.crossField.Value=e.originField.Value}),F.forEach(function(e){e.Value=e._hfval}),S.forEach(function(e){e.TotalValue=0})}function n(){function e(e,t,a,l,s){function c(){var s,c,u=Number.MAX_VALUE,d=-Number.MAX_VALUE;for(r=e;t>r;++r)s=y[i*r+a.DatafieldIndex],s.IsNull||(c=s.AsFloat,isNaN(c)||(u>c&&(u=c),c>d&&(d=c)));l.CalcHMin&&(u-Number.MAX_VALUE&&(n[o].AsFloat=d),o++)}var u=t-e,d=u;if(l.CalcHAverageA||l.CalcHCountA)for(r=e;t>r;r++)y[i*r+a.DatafieldIndex].IsNull&&--d;n[o].AsFloat=s,o++,l.CalcHAverage&&(u>0&&(n[o].AsFloat=s/u),o++),l.CalcHAverageA&&(d>0&&(n[o].AsFloat=s/d),o++),l.CalcHCount&&(n[o].AsFloat=u,o++),l.CalcHCountA&&(n[o].AsFloat=d,o++),(l.CalcHMin||l.CalcHMax)&&c()}function t(){var t=D.length/w.length,a=0;w.forEach(function(l,s){var c=createArray(S.length,0);for(r=a;l>r;++r)S.forEach(function(e,t){c[t]+=y[i*r+e.DatafieldIndex].AsFloat});n=D,o=t*s,S.forEach(function(t,r){e(a,l,t,t.HSubtotalFlag,c[r])}),a=l})}var r,o=i*v,n=y;for(S.forEach(function(t){t.HTotalFlag.Flag&&(e(0,v,t,t.HTotalFlag,t.TotalValue,y),t.CalcTotalHPercent&&(n[o].AsFloat=1,o++),t.CalcTotalVPercent&&o++,t.EntireTotalValue+=t.TotalValue)}),r=0;v>r;++r)S.forEach(function(e){e.CalcHPercent&&(o=i*r+e.DatafieldIndex,0!=e.TotalValue?y[o+1].AsFloat=y[o].AsFloat/e.TotalValue:y[o+1].Clear())});w.length&&t(),u.Post()}function l(){var e,t,r,o,n,a,l;for(u.First();!u.Eof();){for(u.Edit(),e=0,t=0,r=0,o=0;v>o;++o)S.forEach(function(r,s){r.CalcVPercent&&(n=i*o+r.DatafieldIndex,a=y[n].AsFloat,l=f[o*S.length+s],r.CalcHPercent&&++n,++n,l&&(y[n].AsFloat=a/l),e+=a,t+=l)}),o===w[r]&&(S.forEach(function(o){o.CalcVPercent&&(n=i*r+o.DatafieldIndex+1+(o.CalcHPercent?1:0),t&&(D[n].AsFloat=e/t))}),r++);n=i*v,S.forEach(function(e){var t=e.HTotalFlag;nu&&(h.MoveTo(d[u]),!(b[0].AsDateTime>=s.begin));)u++;for(i=s.begin.getDay(),a=s.begin.getMonth(),o(),x.length&&(x[0].AsInteger=a+1,x[1].AsDateTime=s.begin),l=i;7>l;++l){for(F[l].AsDateTime=s.begin;t>u&&(h.MoveTo(d[u]),!(b[0].AsDateTime>s.begin));)r(l,undefined),u++;if(incDate(s.begin),s.begin>s.end||x.length&&a!==s.begin.getMonth())break}n()}}var d,f=createArray(v*S.length,0);s?c():(t(),d.forEach(function(e,t){(e=a.HAddedItemRecordOffsetEnd)&&(h.MoveTo(e),t&&!h.fieldsKeepedValueChanged(x)||(h.keepValue(),t&&n(),o())),h.curRecord._CrossNo_&&r(h.curRecord._CrossNo_-1,f)}),n(),S.some(function(e){return e.CalcVPercent||e.CalcTotalVPercent})&&l())}var o,n,i,a=this,l=a.report,s=9===a.HCrossPeriodType,c=new DetailGrid(l),u=c.Recordset,d=c.ColumnTitle.TitleCells,f=c.Columns,p=a.owner,h=p.Recordset,g=p.Columns,m=p.Groups.items,C=p.ColumnTitle.TitleCells.items,b=[],x=[],v=0,S=[],T=[],y=[],F=[],w=[],D=[];a.HAddedItemRecordOffsetBegin=-1,a.HAddedItemRecordOffsetEnd=-1,a.VAddedItemRecordOffsetBegin=-1,a.VAddedItemRecordOffsetEnd=-1,a.HPercentSums=[],c.assign(p),c.IsCrossTab=0,c.Recordset.BeforePostRecordScript="",n=c.Groups.items,t(),r(),l.RunningDetailGrid=c,c.attachData(),c.prepare()},unprepare:function(){},GroupEndProcess:function(e){var t=this;t.HPercentSums.forEach(function(t){var r;t.Group===e&&(r=t.TotalSumBox.Value,t.DataSumBoxes.forEach(function(e){r?e.Value/=r:e.Value=0}),t.TotalSumBox.Value=1)})},getClickedCellHCrossFieldValue:function(e){var t,r,o=this.report.RunningDetailGrid,n=o.clickedCell,i=n?n.getAttribute("_grcrossno"):0;return i&&(t=o.Recordset.Fields.Item(e+"_"+i),t&&(r=t.Value)),r},getClickedCellHCrossPeriod:function(){var e,t=this,r=t.report.RunningDetailGrid,o=r.clickedCell,n=o?"_"+o.getAttribute("_grcrossno"):0;return n&&(e=grhelper.periodRangeBy(r.Recordset.Fields.Item(t.HCrossFields+n).AsDateTime,t.HCrossPeriodType)),e},get Recordset(){var e=this.report,t=e.RunningDetailGrid;return t!=e.DetailGrid?t.Recordset:undefined},HBeginAddItem:function(){var e=this;e.HAddedItemRecordOffsetBegin=e.report.RunningDetailGrid.Recordset.RecordCount},HEndAddItem:function(){var e=this;e.HAddedItemRecordOffsetEnd=e.report.RunningDetailGrid.Recordset.RecordCount},VBeginAddItem:function(){var e=this;e.VAddedItemRecordOffsetBegin=e.report.RunningDetailGrid.Recordset.RecordCount},VEndAddItem:function(){var e=this;e.VAddedItemRecordOffsetEnd=e.report.RunningDetailGrid.Recordset.RecordCount},GetCurPeriodBeginDate:function(){return this.curPeriod.begin},GetCurPeriodEndDate:function(){return this.curPeriod.end}},prototypeCopyExtend(CrossTab,Object);var BubbleCircleCoverRatio=.25,PixelsPerCm=96/2.54,ChartSeriesColorArray=[3381606,39423,16737996,3394764,10079232,6737100,10040319,3407718,16737843,13421568,6723993,14535833,3342489,8965273,13369497,14535833,13395507,10092424,10066278,14522879],ChartSeriesColorCount=ChartSeriesColorArray.length,SCALELINE_WEIGHT=4,BIG_GAP=8,SMALL_GAP=4,CalcNearestAlignInt=function(e,t){var r,o,n=0;for(e>=1&&(t=0),r=0;t>r;r++)e*=10;for(o=e;o>1;)++n,o/=10;if(o=.15>o?.1:.3>o?.2:.75>o?.5:1,t>0&&(n-=t),n>0)for(r=0;n>r;r++)o*=10;else for(r=n;0>r;r++)o/=10;return o},ChartSeriesCollection=function(e){Collection.call(this,e)};prototypeLinkExtend(ChartSeriesCollection,Collection),ChartSeriesCollection.prototype._createItem=function(){return new ChartSeries(this.owner)};var ChartShape=function(e,t){var r=this;r.series=e,r.group=t},ChartRect=function(e,t,r){var o=this;ChartShape.call(o,e,t),o.rect=r};ChartRect.prototype={inRange:function(e,t){var r=this.rect,o=r.Width(),n=r.Height();return(4>o||4>n)&&(r=r.clone(),4>o&&(r.left-=2,r.right+=2),4>n&&(r.top-=2,r.bottom+=2)),r.PtInRect(e,t)},tooltipPos:function(){var e=this.rect;return{x:(e.left+e.right)/2,y:(e.top+e.bottom)/2}}};var ChartPie=function(e,t,r,o,n,i,a){var l=this;ChartShape.call(l,e,t),l.x=r,l.y=o,l.r=n,l.beginAngle=i,l.endAngle=a};ChartPie.prototype={inRange:function(e,t){function r(){var r=e-o.x,n=t-o.y,i=Math.sqrt(r*r+n*n),a=Math.atan2(n,r);return 0>n&&(a+=2*Math.PI),{angle:360-toDegree(a),distance:i}}var o=this,n=r();return o.beginAngle<=n.angle&&n.angle0?o.BarWidthPercent:0),a=i?e*r.MarginBeginWeight/i:0,l=i?e*r.MarginEndWeight/i:0,s=e-a-l,c=n>0?o.BarWidthPercent:0,u=100*t,d=u+c,f={};return i-=r.MarginBeginWeight+r.MarginEndWeight,f.BeginPos=a,f.EndPos=a,i>0&&(f.BeginPos+=s*u/i,f.EndPos+=s*d/i),f.LabelMiddlePos=(f.BeginPos+f.EndPos)/2,f},DrawHorzAxisLabel:function(e){var t=this,r=t.owner.context;!e.IsRectEmpty()&&t.Label&&r.drawText(t.Label,(e.left+e.right-r.measureTextWidth(t.Label))/2,(e.top+e.bottom-t.owner.fontHeight)/2)},DrawVertAxisLabel:function(e){var t=this,r=t.owner.context;!e.IsRectEmpty()&&t.Label&&r.drawTextRotate(t.Label,(e.left+e.right-t.owner.fontHeight)/2,(e.top+e.bottom+r.measureTextWidth(t.Label))/2,90)},DrawAxisTextsPrepare:function(e){var t,r=this,o=r.owner,n=[];if(r.IsValueAxis)if(r.Scales.length)r.Scales.forEach(function(t){n.push({Text:t.Text,PosVal:r.CalcValuePos(t.PosVal,e)})});else for(t=r.DrawMin;tl;l++)c.drawText(r.charAt(l),i,a),a+=d;else s=c.measureTextWidth(r),i=e.left+t-Math.abs(s/2*Math.cos(toRadians(f.TextAngle)))+Math.abs(d/2*Math.sin(toRadians(f.TextAngle))),o&&(a-=d),n||(a-=d+2*SCALELINE_WEIGHT-1),c.drawTextRotate(r,i,a,f.TextAngle)}var n,i,a,l,s,c,u,d,f=this,p=f.owner,h=p.context,g=e.Width()-p.xView3DDepth,m=p.IsDrawNegativeGraph(),C=f.IsValueAxis,b=f.LinePen,x=b.clone(),v=b.clone();f.Used&&!e.IsRectEmpty()&&(x.Width=.5,v.Width=.5,v.Color=colorGradientLight(b.Color),f.LineVisible&&(h.selectPen(b),i=e.left,a=i+p.xView3DDepth,l=i+g,s=l+p.xView3DDepth,c=r?e.bottom:e.top,u=c-p.yView3DDepth,d=r?t.bottom-2*p.yView3DDepth:t.top,h.DrawHorzLine(c,i,l),p.Chart3DReal&&(p.YAxis.Used||!p.Y2Axis.Used||p.IsHorzGraph()?(h.drawPolyLine([l,c,s,u,a,u],0),h.drawPolyLine([s,u,s,d,a,d],0)):(h.drawPolyLine([s,u,a,u,i,c],0),h.drawPolyLine([s,d,a,d,a,u],0))),h.restorePen()),n=f.DrawAxisTextsPrepare(g),h.selectPen(x),n.forEach(function(n,i){var a,l,s=p.IsScatterGraph()||!m||(f.IsValueAxis?n.PosVal>=0:p.Value(0,i)>=0),c=e.left+n.PosVal;f.LineVisible&&(r?(c+=p.xView3DDepth,l=e.bottom-p.yView3DDepth,a=l-SCALELINE_WEIGHT):(a=e.top+1,l=s?e.top+1+SCALELINE_WEIGHT:e.top+1-SCALELINE_WEIGHT),h.DrawVertLine(c,a,l),p.Chart3DReal&&!C&&(r&&(c-=p.xView3DDepth,a+=p.yView3DDepth+SCALELINE_WEIGHT),h.selectPen(v),h.drawLine(c,a,c+p.xView3DDepth,a-p.yView3DDepth),h.restorePen())),f.TextVisible&&o(e,n.PosVal,n.Text,r,s),f.CoordLineVisible&&!C&&f.IsValueAxis&&(h.selectPen(f.CoordLinePen),a=t.top+1,l=t.bottom-p.yView3DDepth,r&&(a-=p.yView3DDepth,l-=p.yView3DDepth),h.DrawVertLine(c+p.xView3DDepth,a,l),h.restorePen()),C=!1}),h.restorePen(),f.CustomCoordLines.forEach(function(n){var i=f.CalcValuePos(n.PosVal,g);n.Text&&o(e,i,n.Text,r,!0),h.selectPen(n.LinePen),h.DrawVertLine(e.left+i,t.top+1,t.bottom),h.restorePen()}))},DrawVertAxis:function(e,t,r,o){function n(e,t,r,o){var n=d.owner.context;n.drawText(r,o?e.left+(d.LineVisible?SCALELINE_WEIGHT:0):e.right-n.measureTextWidth(r)-(d.LineVisible?SCALELINE_WEIGHT:0),t-d.owner.fontHeight/2)}var i,a,l,s,c,u,d=this,f=d.owner,p=f.context,h=e.Height()-f.yView3DDepth,g=d.LinePen,m=g.clone(),C=g.clone();!e.IsRectEmpty()&&d.Used&&(m.Width=.5,C.Width=.5,C.Color=colorGradientLight(g.Color),d.LineVisible&&(p.selectPen(g),i=r?e.left-f.xView3DDepth:e.right,a=e.bottom,o&&(a-=f.yView3DDepth),l=a-h,p.DrawVertLine(i,l,a+1),f.Chart3DReal&&(s=i+f.xView3DDepth,c=a-f.yView3DDepth,p.drawPolyLine([i,a,s,c,s,c-h,i,l],0)),p.restorePen()),p.selectPen(m),u=d.DrawAxisTextsPrepare(h),u.forEach(function(i,a){var l,s,c=d.IsValueAxis?e.bottom-i.PosVal:e.top+i.PosVal+(o?0:f.yView3DDepth),u=c;d.LineVisible&&i.PosVal!=d.DrawMin&&(l=e.right-SCALELINE_WEIGHT,s=e.right,r&&(l=e.left,s=e.left+SCALELINE_WEIGHT),p.DrawHorzLine(u,l,s),f.Chart3DReal&&(p.selectPen(C),r?(l=e.left-f.xView3DDepth,u+=f.yView3DDepth):l=e.right,p.drawLine(l,u,l+f.xView3DDepth,u-f.yView3DDepth),p.restorePen())),d.TextVisible&&n(e,c,i.Text,r),d.CoordLineVisible&&d.IsValueAxis&&(p.selectPen(d.CoordLinePen),u-=f.yView3DDepth,p.DrawHorzLine(u,t.left+f.xView3DDepth+g.Width,t.right),p.restorePen())}),p.restorePen(),d.CustomCoordLines.forEach(function(o){var i=e.bottom-d.CalcValuePos(o.PosVal,h);o.Text&&n(e,i,o.Text,r),p.selectPen(o.LinePen),p.DrawHorzLine(i-f.yView3DDepth,t.left+f.xView3DDepth+g.Width,t.right),p.restorePen()}))},PrepareDraw:function(){var e=this;e.Used=!1,e.IsValueAxis=!1,e.DrawMax=-Number.MAX_VALUE,e.DrawMin=Number.MAX_VALUE,e.DrawSpace=0,e.ScaleFormatParser=new NumericFormatter(e.TextFormat)},PrepareDrawRange:function(){var e=this;e.Max?e.DrawMax=e.Max:e.Scales.forEach(function(t){e.DrawMax=Math.max(e.DrawMax,t.PosVal)}),e.Min?e.DrawMin=e.Min:e.DrawMin>0&&e.DrawMax>0?e.DrawMin=0:e.DrawMin<0&&e.DrawMax<0?e.DrawMax=0:e.owner.NegativeAsZero&&(e.DrawMin=0),e.DrawMax<=e.DrawMin&&(e.DrawMin=0,e.DrawMax=100)},PrepareDrawSpace:function(e,t,r){var o,n=this;n.Space?n.DrawSpace=n.Space:(o=n.owner.fontHeight*t,n.DrawSpace=(n.DrawMax-n.DrawMin)*o/e,n.DrawSpace=CalcNearestAlignInt(n.DrawSpace,n.ScaleFormatParser.positiveAnalyser.precision)),n.DrawMax<=n.DrawMin&&(n.DrawMax=n.DrawMin+1,n.DrawSpace=.2),n.DrawSpace<2*n.GetScaleTiny()&&(n.DrawSpace=2*n.GetScaleTiny()),n.Max||n.Min||r||(n.DrawMax>0&&(n.DrawMax=n.DrawSpace*Math.ceil(n.DrawMax/n.DrawSpace)),n.DrawMin<0&&(n.DrawMin=n.DrawSpace*Math.floor(n.DrawMin/n.DrawSpace)))},GetScaleTiny:function(){var e=this;return(e.DrawMax-e.DrawMin)/1e4},CalcValuePos:function(e,t){var r=this;return(e-r.DrawMin)*t/(r.DrawMax-r.DrawMin)},get ScaleCount(){return this.Scales.length},AddCustomScale:function(e,t){this.Scales.push({PosVal:e,Text:t})},EmptyCustomScale:function(){this.Scales=[]},GetScaleText:function(e){return this.Scales[e].Text},GetScaleValue:function(e){return this.Scales[e].PosVal},AddCustomCoordLine:function(e,t,r,o,n){var i=new Pen;i.Width=r,i.Style=n,i.Color=o,this.CustomCoordLines.push({PosVal:e,Text:t,LinePen:i})},EmptyCustomCoordLine:function(){this.CustomCoordLines=[]}},prototypeCopyExtend(ChartAxis,Object);var ChartSeries=function(e){var t=this;Object.call(t,e),t.ChartType=1,t.ByY2Axis=!1,t.SeriesName="",t.XValueField="",t.YValueField="",t.ZValueField="",t.FillColor=0,t.FillColorAuto=!0,t.LabelText="",t.LabelAsGroup=!1,t.LabelInBar=!1,t.LabelTextAngle=0,t.TooltipText="",t.ValueFormat="0.##",t.MarkerStyle=3,t.MarkerSize=4,t.MarkerColor=16777215,t.MarkerColorAuto=!0,t.MarkerLegendShow=!0,t.BorderPen=new Pen,t.MarkerPen=new Pen};ChartSeries.prototype={afterLoad:function(e){var t=this,r=t.report.viewer.alpha,o=r.chartGraph,n=r.chartStroke,i=t.report.isWR;"StepBarChart"===t.ChartType?t.ChartType=4:enumMemberValid(t,"ChartType",grenum.ChartType),enumMemberValid(t,"MarkerStyle",grenum.PointMarkerStyle),colorMemberValid(t,"FillColor",o),colorMemberValid(t,"MarkerColor",o),t.BorderPen.loadFromJSON(e.BorderPen,n,i),t.MarkerPen.loadFromJSON(e.MarkerPen,n,i)},PrepareDraw:function(){var e=this;e.ValueFormatParser=new NumericFormatter(e.ValueFormat)},IsHorzGraph:function(){var e=this;return 11===e.ChartType||12===e.ChartType||13===e.ChartType},IsPercent100Graph:function(){var e=this;return 2===e.ChartType||10===e.ChartType||13===e.ChartType},IsBarChart:function(){var e=this;return 1===e.ChartType||4===e.ChartType||10===e.ChartType||11===e.ChartType||12===e.ChartType||13===e.ChartType},IsScatterGraph:function(){var e=this;return 5===e.ChartType||6===e.ChartType||8===e.ChartType||9===e.ChartType},CanGroupLabelChart:function(){var e=this;return 1===e.ChartType||11===e.ChartType},HasPointMarker:function(){var e=this;return 3===e.ChartType||7===e.ChartType||5===e.ChartType||6===e.ChartType||8===e.ChartType||9===e.ChartType},Support3D:function(){var e=this;return 1===e.ChartType||4===e.ChartType||10===e.ChartType||11===e.ChartType||12===e.ChartType||13===e.ChartType||2===e.ChartType}},prototypeCopyExtend(ChartSeries,Object);var Chart=function(e){var t=this;CanvasBox.call(t,e),t.XAxis=new ChartAxis(t),t.YAxis=new ChartAxis(t),t.Y2Axis=new ChartAxis(t),t.Series=new ChartSeriesCollection(t),t.Recordset=new Recordset(t),t.TitleFont=new FontWrapper(t.Font),t.ValueFont=new FontWrapper(t.Font),t.Title="",t.Chart3D=!1,t.Chart3DViewAngle=35,t.LegendVisible=!0,t.LegendValueVisible=!1,t.LegendShowSum=!1,t.LegendAtBottom=!1,t.LegendColumnCount=0,t.LegendSumLabel="",t.SeriesField="",t.SeriesAuto=!0,t.GroupField="",t.GroupAuto=!0,t.GroupCount=5,t.SeriesCount=2,t.AbsNegativeValue=!1,t.NegativeBarColor=255,t.BarWidthPercent=70,t.NegativeAsZero=!1,t.SingleSeriesColored=!0,t.Bar3DAxisDepth=25,t.Pie3DHeightDepth=25,t.BubbleScalePerCm=0,t.SeriesLabels=[],t.GroupLabels=[],t.values=[]};Chart.prototype={ControlType:11,children:["Border","XAxis","YAxis","Y2Axis","Series"],options:{tooltipBoxColor:"rgba(0,0,0,.8)",tooltipBoxCorner:4,tooltipFontSize:14,tooltipFontStyle:"normal",tooltipTitleFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",tooltipFontColor:"#fff"},afterLoad:function(e){var t,r,o=this,n=o.report.viewer.alpha,i=n.chartGraph,a=n.chartStroke,l=o.report.isWR;if(CanvasBox.prototype.afterLoad.call(o,e),colorMemberValid(o,"NegativeBarColor",i),o.XAxis.loadFromJSON(e.XAxis,a),o.YAxis.loadFromJSON(e.YAxis,a),o.Y2Axis.loadFromJSON(e.Y2Axis,a),o.Series.loadFromJSON(e.ChartSeries),o.Recordset.loadFromJSON(e.Recordset),o.TitleFont.loadFromJSON(e.TitleFont,l),o.ValueFont.loadFromJSON(e.ValueFont,l),o.prepareValues(),t=e[o.getJsonMember("GroupLabel")],t&&(o.GroupLabels=t.split("\r")),t=e[o.getJsonMember("SeriesLabel")],t&&(o.SeriesLabels=t.split("\r")),t=e[o.getJsonMember("Value")]){for(t=t.split(","),r=t.length;r-- >0;)t[r]=+t[r];for(o.values=[],r=0;ro;)e.MoveTo(o++),u.appendRefRecord(e.curRecord)}function r(e,t){var r,n=h.Groups.items,i=[],l="",s=[];for(e.recordset.MoveTo(t-1),a.MoveTo(e.beginNoField.AsInteger),r=e.index;r>=0;r--)l&&(l+=";"),l+=n[r].ByFields;for(o.decodeFields(l).forEach(function(e){i.indexOf(e)<0&&i.push(e)}),i.forEach(function(e){var t=a.Fields.itemByName(e.Name);t&&s.push({origin:e,detail:t})}),o.First();!o.Eof();)s.every(function(e){return!e.detail.valUeq(e.origin)})&&u.appendRefRecord(o.curRecord),o.Next()}var o,n,i,a,l,s=this,c=s.origin,u=s.Recordset,d=s.owner.owner,f=s.runningNo,p=s.report,h=p.DetailGrid;s.bindRecordset(),h&&(a=h.Recordset),e()?(u.RecordCount||(d.ByFields&&(l=d),f>=1e4&&(f=Math.floor(f/1e4),n=p.ownerSR,d=n.owner.owner,p=d.report,l||h?n._relations&&(--f,d.ByFields&&(d.recordset.MoveTo(f),f=d.beginNoField.AsInteger),p.DetailGrid.Recordset.MoveTo(f),i=1,n.attachRelationTable()):d.ByFields&&(l=d)),c&&(o=c.Recordset),o&&o.RecordCount?l?r(l,f):u._dataTable=c._dataTable:a&&t(a,l,f), +i&&n.detachRelationTable()),s.applyDataFromRecordset()):s.values||s.EmptyValues()},applyDataFromRecordset:function(){var e,t,r,o,n,i,a=this,l=a.Recordset,s=a.Series.items,c=s[0].IsScatterGraph();for(a.SeriesAuto&&a.oSeriesField&&(a.SeriesCount=0,a.SeriesLabels=[]),!c&&a.GroupAuto&&a.oGroupField&&(a.GroupCount=0,a.GroupLabels=[]),a.EmptyValues(),l.First();!l.Eof();)e=0,a.oSeriesField&&(t=a.oSeriesField.DisplayText,e=a.GetSeriesIndexByLabel(t,!1),0>e&&a.SeriesAuto&&(e=a.SeriesCount,a.SeriesLabels.push(t),++a.SeriesCount)),r=0,a.oGroupField&&!c&&(o=a.oGroupField.DisplayText,r=a.GetGroupIndexByLabel(o,!1),0>r&&a.GroupAuto&&(r=a.GroupCount,a.GroupLabels.push(o),++a.GroupCount)),s.forEach(function(t,o){o&&(e=o),c?a.AddXYZValue(e,t.oXValueField.AsFloat,t.oYValueField.AsFloat,t.oZValueField?t.oZValueField.AsFloat:undefined):(n=t.oYValueField.AsFloat,i=a.Value(e,r),i&&(n+=i),a.SetValue(e,r,n))}),l.Next()},showTooltip:function(e,t){var r=this,o=r.options,n=r.context;if(e||(e=undefined),r.activeShape!==e&&(r.activeShape=e,n.ctx.clearRect(0,0,r.canvas.width,r.canvas.height),r.draw(),e)){var i=r.graphSerieses[e.series];if(i.TooltipTextBuilder){var a,l=n.ctx.font,s=i.TooltipTextBuilder.generateChartDisplayText(e.series,e.group),c=e.tooltipPos(),u=o.tooltipFontSize+2,d=0;if(n.ctx.font=fontString(o.tooltipFontSize,o.tooltipFontStyle,o.tooltipTitleFontFamily),i.tooltipLines>1){a=[];for(var f,p,h=0;(f=s.indexOf("\n",h))>=0;)p=s.substring(h,f-("\r"===s.charAt(f-1)?1:0)),a.push(p),d=Math.max(d,n.measureTextWidth(p)),h=f+1;p=s.substr(h),a.push(p),d=Math.max(d,n.measureTextWidth(p))}else d=n.measureTextWidth(s);var g=new Rect(c.x,c.y,c.x+d,c.y+u*i.tooltipLines-2);if(g.InflateRect(o.tooltipBoxCorner,o.tooltipBoxCorner),g.OffsetRect(Math.min(r.canvas.width-g.right,0),Math.min(r.canvas.height-g.bottom,0)),n.selectFillStyle(o.tooltipBoxColor),n.roundRectangle(g.left,g.top,g.Width(),g.Height(),o.tooltipBoxCorner,o.tooltipBoxCorner,1),n.restoreFillStyle(),n.selectFillStyle(o.tooltipFontColor),a){var m=g.left+o.tooltipBoxCorner,C=g.top+o.tooltipBoxCorner;a.forEach(function(e){n.drawText(e,m,C),C+=u})}else n.drawTextCenter(s,(g.left+g.right)/2,(g.top+g.bottom-o.tooltipFontSize)/2);n.restoreFillStyle(),n.ctx.font=l}}},prepareCanvas:function(){function e(){var e,t,r=i.Series.items,o=r.length;for(e=0;o>e;e++)if(t=r[e],t.LabelText&&(t.LabelTextBuilder=new TextBuilder(i,t.LabelText)),t.TooltipText){t.TooltipTextBuilder=new TextBuilder(i,t.TooltipText),t.tooltipLines=1;for(var n=0;(n=t.TooltipText.indexOf("\n",n))>=0;)++n,++t.tooltipLines;u=1}}function t(){var e=toRadians(i.Chart3DViewAngle);i.xView3DDepth=Math.cos(e)*i.Bar3DAxisDepth,i.yView3DDepth=Math.sin(e)*i.Bar3DAxisDepth}function r(){var e,t,r=i.Series.items,o=i.graphs=[],n=i.graphSerieses=[];for(e=0;e1)for(t=0;o>t;t++)if(r=i.shapes[t],!r.rect&&r.inRange(e.x,e.y))return r;for(t=0;o>t;t++)if(r=i.shapes[t],r.inRange(e.x,e.y))return r;return 0}function n(e){for(var t,r=i.shapesL.length;r-- >0;)if(t=i.shapesL[r],t.inRange(e.x,e.y))return t;return 0}var i=this,a=i.onclickGraph,l=i.onclickLegend,s=i.ondblclickGraph,c=i.ondblclickLegend,u=0;i.Chart3DReal=i.Chart3D&&i.Support3D(),i.xView3DDepth=0,i.yView3DDepth=0,i.Chart3DReal&&t(),i.loadDrawingData(),e(),r(),u&&bindEvents(i,["mousemove","touchstart","touchmove","mouseout"],function(e){var t,r;"mouseout"!==e.type&&(t=getRelativePosition(e),r=o(t)),i.showTooltip(r,t)}),(a||l)&&addEvent(i.canvas,"click",function(e){var t,r=getRelativePosition(e);a&&(t=o(r),t&&a(e,i,t.series,t.group)),l&&(t=n(r),t&&l(e,i,t.series,t.group))}),(s||c)&&addEvent(i.canvas,"dblclick",function(e){var t,r=getRelativePosition(e);s&&(t=o(r),t&&s(e,i,t.series,t.group)),c&&(t=n(r),t&&c(e,i,t.series,t.group))})},draw:function(e){function t(){function e(){var e,o,n;c.DrawBubbleScalePerCm=c.BubbleScalePerCm,c.DrawBubbleScalePerCm<=0&&(e=r*t*BubbleCircleCoverRatio,o=e/(PixelsPerCm*PixelsPerCm),c.DrawBubbleScalePerCm=h/o),n=Math.sqrt(p/c.DrawBubbleScalePerCm)*PixelsPerCm,0===b.Max&&(b.DrawMax+=(b.DrawMax-b.DrawMin)*(n/2)/t),0==C.Max&&(C.DrawMax+=(C.DrawMax-C.DrawMin)*(n/2)/r)}var t,r,o=c.IsScatterGraph(),n=c.Series.items,i=n[0].ChartType,u=10===i||13===i,p=0,h=0;for(C.PrepareDraw(),b.PrepareDraw(),x.PrepareDraw(),l=n.length,a=0;l>a;a++)n[a].PrepareDraw(this);C.Used=!0,C.IsValueAxis=!1,b.IsValueAxis=!0,x.IsValueAxis=!0,c.Title&&(v=fontHeight(c.getUsingTitleFont())+BIG_GAP),o?(C.IsValueAxis=!0,g.forEach(function(e){e.forEach(function(e){var t,r=c.values[e],o=r?r.length:0,n=C,i=c.YAxisOfSeries(m[e]);for(i.Used=!0,a=0;o>a;a++)t=r[a],t.yi.DrawMax&&(i.DrawMax=t.y),t.xn.DrawMax&&(n.DrawMax=t.x),pi.DrawMax&&(i.DrawMax=o))}),n){var i=m[e[0]],a=c.YAxisOfSeries(i);a.Used=!0,u?(a.DrawMin=0,a.DrawMax=100):(ra.DrawMax&&(a.DrawMax=r))}}),b.Used||x.Used||(b.Used=!0),b.Used&&(s=b.DrawMax),x.Used&&s0?x:999,S=t.Width(),T=2*e.fontHeight,y=3*T/4,F=n(),w=S,D=[],P={Count:0,Width:0};if(!F)return 0;for(u=0;F>u;++u)o=BIG_GAP+y+SMALL_GAP,o+=C.measureTextWidth(a(u)),b&&(o+=SMALL_GAP+C.measureTextWidth(l(u))),(o>w||P.Count>=v)&&P.Width>0&&(D.push(P),P={Count:0,Width:0},w=S),P.Count++,P.Width+=o,w-=o;for(P.Count>0&&D.push(P),f=SMALL_GAP+D.length*T,d=0,P=D[d],u=t.bottom-f+SMALL_GAP,p=new Rect((t.left+t.right-P.Width)/2,t.bottom-f+SMALL_GAP,0,u+T),u=0;F>u;++u)P.Count<=0&&(d++,P=D[d],p.left=(t.left+t.right-P.Width)/2,p.top=p.bottom,p.bottom+=T),P.Count--,p.left+=BIG_GAP,S=p.left,(F-1>u||!i(u))&&(o=(p.top+p.bottom-y)/2,h=new Rect(p.left,o,p.left+y,o+y),c(u,h)),p.left+=y,g=(p.top+p.bottom-e.fontHeight)/2,m=a(u),p.left+=SMALL_GAP,p.right=p.left+C.measureTextWidth(m),C.drawText(m,p.left,g),p.left=p.right,b&&(m=l(u),p.left+=SMALL_GAP,p.right=p.left+C.measureTextWidth(m),C.drawText(m,p.left,g),p.left=p.right),e.toUpdateShapes&&(v=s(u),e.shapesL.push(new ChartRect(v.series,v.group,new Rect(S,p.top,p.right,p.bottom))));return f}function d(t){var i,u,d,f,p,h,g,m,C,b,x,v,S,T,y=e.context,F=r(),w=o(),D=n(),P=e.LegendColumnCount,R=3*e.fontHeight/4*2,A=3*R/4,B=t.Height()-2*SMALL_GAP,E=0,G=0,L=2*SMALL_GAP,N=[],M=[];if(!D)return 0;for(f=0;D>f;++f)h=y.measureTextWidth(a(f)),h>E&&(E=h),g=R,L+=g,N.push(g),F&&(h=y.measureTextWidth(l(f)),h>G&&(G=h));if(i=E+SMALL_GAP,G>0&&(i+=G+SMALL_GAP),0>=P&&(P=1,L>t.Height()))for(h=0,f=0;D>f;++f)h>B&&(P++,h=N[f]);if(Bh;h++)for(g=B;D>f&&g>N[f];)g-=N[f++];for(u=D>f,u&&(h=D,w&&(f--,h--),N.splice(f,h-f)),h=N.length,g=P,m=Math.ceil(h/P),M=createArray(P,0),w&&(M[P-1]=m,h-=m,g--,g>0&&(m=Math.ceil(h/g))),f=0;g>f;f++)M[f]=Math.min(m,h),h-=M[f];for(g=0,f=0,h=0;P>h;h++){for(m=0,p=0;pg&&(g=m)}for(L=Math.min(t.Height(),g+2*SMALL_GAP),C=SMALL_GAP+A+SMALL_GAP+i,b=C*P+SMALL_GAP,b>2*t.Width()/3&&(b=2*t.Width()/3,g=C,C=(b-SMALL_GAP)/P,E-=g-C),h=t.right-b,g=(t.top+t.bottom-L)/2,x=new Rect(h,g,h+b,g+L),y.rectangle2(x),h=x.left+SMALL_GAP,g=C-SMALL_GAP,v=new Rect(h,0,h+A,0),h+=v.Width()+SMALL_GAP,g-=v.Width()+SMALL_GAP,S=new Rect(h,x.top+SMALL_GAP,h+Math.min(g,E+SMALL_GAP),0),h+=S.Width()+SMALL_GAP,g-=S.Width()+SMALL_GAP,T=new Rect(h,0,h+g,0),h=N.length-1,w&&h--,g=0,f=0;P>f;f++){for(S.top=x.top+SMALL_GAP,p=0;pt;t++)if(o=d[t],o.LabelTextBuilder&&!o.LabelInBar){D=!0;break}if(!D&&c.Title||(F=fontHeight(c.getUsingValueFont())/(o.LabelTextBuilder?1:2),c.IsDrawNegativeGraph()&&(w=F),o.LabelTextAngle&&(p.selectFont(c.getUsingValueFont()),F=Math.max(Math.abs(p.measureTextWidth(o.ValueFormatParser.format(s))*Math.sin(toRadians(o.LabelTextAngle))),F),p.restoreFont())),i=new Rect(e.left+v+S,e.top+F,e.right-T-y,e.bottom-f-h-w),l=new Rect(e.left,e.top+F,e.left+v,e.bottom-f-h-w),b.DrawVertAxisLabel(l),u=new Rect(e.right-T,l.top,e.right,l.bottom),x.DrawVertAxisLabel(u),l.left=l.right,l.right+=S,b.DrawVertAxis(l,i,!1),u.right=u.left,u.left-=y,x.DrawVertAxis(u,i,!0),a=new Rect(e.left+v+S,e.bottom-f,e.right-T-y,e.bottom),C.DrawHorzAxisLabel(a),c.IsDrawNegativeGraph()?(a.top=l.bottom-b.CalcValuePos(0,l.Height()),a.bottom=a.top+h):(a.top=i.bottom,a.bottom=a.top+h),c.Chart3D&&C.DrawHorzAxis(a,i,!1),!i.IsRectEmpty()&&(c.IsScatterGraph()||c.GroupCount)){for(p.selectFont(c.getUsingValueFont()),r=g.length,t=0;r>t;t++)switch(o=g[t],n=m[o[0]].ChartType){case 1:c.DrawBar(o,i);break;case 4:case 10:c.DrawStackedBar(o,n,i);break;case 3:case 7:c.DrawLine(o,n,i);break;case 9:c.DrawBubble(o,i);break;default:c.DrawXYScatterGraph(o,n,i)}p.restoreFont()}!c.Chart3D&&C.DrawHorzAxis(a,i,!1)}function i(e){var t,r,o,n,i,a,l,u,d=c.Series.items,f=C.CalcLabelHeight(),h=C.CalcVertAxisWidth(),v=b.CalcLabelHeight(),S=b.CalcHorzAxisHeight(!1),T=x.CalcLabelHeight(),y=x.CalcHorzAxisHeight(!1),F=0,w=0,D=!1;for(r=d.length,t=0;r>t;t++)if(o=d[t],o.LabelTextBuilder&&!o.LabelInBar){D=!0;break}if(D&&(p.selectFont(c.getUsingValueFont()),F=p.measureTextWidth(o.ValueFormatParser.format(s)),c.IsDrawNegativeGraph()&&(w=F),p.restoreFont()),i=new Rect(e.left+f+h+w,e.top+v+S,e.right-F,e.bottom-T-y),l=new Rect(i.left,e.top,i.right,e.top+v),b.DrawHorzAxisLabel(l),u=new Rect(l.left,e.bottom-T,l.right,e.bottom),x.DrawHorzAxisLabel(u),l.top=l.bottom,l.bottom+=S,b.DrawHorzAxis(l,i,!0),u.bottom=u.top,u.top-=y,x.DrawHorzAxis(u,i,!1),a=new Rect(e.left,i.top,e.left+f,i.bottom),C.DrawVertAxisLabel(a),a.left=a.right,a.right+=h,c.Chart3D&&C.DrawVertAxis(a,i,!1,b.Used),!i.IsRectEmpty()&&c.GroupCount){for(p.selectFont(c.getUsingValueFont()),r=g.length,t=0;r>t;t++)switch(o=g[t],n=m[o[0]].ChartType){case 11:c.DrawColumnBar(o,i);break;case 12:case 13:c.DrawStackedColumnBar(o,n,i)}p.restoreFont()}!c.Chart3D&&C.DrawVertAxis(a,i,!1)}var a,l,s,c=this,u=c.canvas,d=u.width,f=u.height,p=c.context=new Context(u.getContext("2d")),h=new Rect(0,0,d,f),g=c.graphs,m=c.graphSerieses,C=c.XAxis,b=c.YAxis,x=c.Y2Axis,v=0;c.fontHeight=fontHeight(c.getUsingFont()),c.valueFontHeight=fontHeight(c.getUsingValueFont()),p.selectFont(c.getUsingFont()),p.selectFillColor(c.ForeColor),c.report.singleChart&&(h.left+=c.PaddingLeft,h.top+=c.PaddingTop,h.right-=c.PaddingRight,h.bottom-=c.PaddingBottom),c.toUpdateShapes=e,e&&(c.shapes=[],c.shapesL=[]),t(),c.LegendVisible&&c.SeriesCount&&r(c),c.Title&&o(),h.top+=v,h.IsRectEmpty()||(2===c.Series.items[0].ChartType?c.DrawPie(h):c.IsHorzGraph()?i(h):n(h)),p.restoreFillStyle(),p.restoreFont()},DrawPie:function(e){var t,r,o,n,i=this,a=i.context,l=0,s=0,c=e.Height(),u=e.Width(),d=u*c,f=Number.MAX_VALUE,p=-1,h=-1;for(t=0;to&&(f=o,p=r,h=t);for(a.selectFont(i.getUsingValueFont()),o=0;on;n++)s=toRadians(C[n]),o=a+Math.cos(s)*i,h=l-Math.sin(s)*i,x=u.GetDrawValueText(n,e),b=d.measureTextWidth(x)+S,r=new Rect(o,h,o+b,h+p),st.left?r.OffsetRect(-m,0):r.OffsetRect(0,T.bottom-r.top),d.drawLine(o,h,r.right,r.bottom-b.cy/3)),c=2):s<1.5*Math.PI?(r.OffsetRect(-b-2,0),3===c&&r.bottom>T.top&&(m=r.right-T.left+2,r.left-m>t.left?r.OffsetRect(-m,0):r.OffsetRect(0,r.bottom-T.top),d.drawLine(o,h,r.right,r.top+b.cy/3)),c=3):(r.OffsetRect(2,0),4===c&&r.bottom>T.top&&(m=T.right-r.left+2,r.right-mn;n++)m+=u.Value(n,e),u.graphSerieses[n].LabelTextBuilder&&(h=!0);if(!(0>=m||(a=t.Width()-2*r-2*SMALL_GAP,l=t.Height()-f-2*p-2*SMALL_GAP,0>=a||0>=l||(i=Math.min(a,l)/2,h?t.InflateRect(-SMALL_GAP,-SMALL_GAP):t.InflateRect(i-a/2,i-l/2),t.bottom-=f,t.IsRectEmpty())))){for(u.DrawLabelText(u.GroupLabel(e),(t.left+t.right)/2,t.bottom),a=(t.left+t.right)/2,l=(t.top+t.bottom)/2,s=0,n=0;g>n;n++)c=s+360*u.Value(n,e)/m,u.toUpdateShapes&&u.shapes.push(new ChartPie(n,e,a,l,i,s,c)),d.selectFillColor(u.GetGraphFillColor(n)),d.pie(a,l,i,s,c,1),d.restoreFillStyle(),h&&C.push((s+c)/2),s=c;h&&o()}},DrawColumnBar:function(e,t){var r,o,n,i,a,l,s,c,u,d,f,p,h,g=this,m=g.context,C=g.graphSerieses,b=g.CalcColumnBarCount(),x=g.CalcFirtColumnBarIndex(e),v=t.Width()-g.xView3DDepth,S=t.Height()-g.yView3DDepth,T=g.IsDrawNegativeGraph()?g.YAxis.CalcValuePos(0,v):t.left+1,y=e.length,F=g.IsDrawNegativeGraph(),w=[];for(i=0;i0;)a=e[o],l=C[a],n=g.Value(a,i),d=g.AbsNegativeValue&&0>n,d&&(n=-n),h=new Rect(0,r+u*(x+o)/b,0,r+u*(x+o+1)/b),p=g.YAxisOfSeries(l).CalcValuePos(n,v),F?(h.left=t.left+T,h.right=Math.min(t.left+p,t.right),T>p&&(p=h.left,h.left=h.right,h.right=p)):(h.left=t.left,h.right=Math.min(t.left+Math.max(0,p),t.right)),w.push(h),g.toUpdateShapes&&g.shapes.push(new ChartRect(a,i,h)),f=d?g.NegativeBarColor:g.GetGraphFillColor(g.IsColorSingleBar()?i:a),g.DrawRectBar(h,f,l.BorderPen,!0,!0,n>=0);for(i=0;io;o++)h=w[i*y+o],po;o++)a=e[o],l=C[a],l.LabelTextBuilder&&(s=g.GetDrawValueText(a,i),h=w[i*y+o],l.LabelInBar?g.DrawHorzBarText(s,h):g.DrawValueText(l,s,h.right+SMALL_GAP+m.measureTextWidth(s)/2,(h.top+h.bottom+g.valueFontHeight)/2))},DrawStackedColumnBar:function(e,t,r){var o,n,i,a,l,s,c,u,d,f,p,h,g,m,C=this,b=C.context,x=C.graphSerieses,v=C.CalcColumnBarCount(),S=C.CalcFirtColumnBarIndex(e),T=r.Width()-C.xView3DDepth,y=r.Height()-C.yView3DDepth,F=e.length,w=[];for(i=0;io;o++)a=e[o],l=x[a],n=C.Value(a,i),13===t&&(n*=100/C.CalcGroupTotalValue(e,i)),p=Math.max(0,C.YAxisOfSeries(l).CalcValuePos(n,T)),m=new Rect(f,h,Math.min(r.right,f+p),g),w.push(m),C.toUpdateShapes&&C.shapes.push(new ChartRect(a,i,m)),u=C.GetGraphFillColor(a),C.DrawRectBar(m,u,l.BorderPen,!0,o===F-1,!0),f+=p;if(l.LabelInBar)for(o=0;F>o;o++)a=e[o],l=x[a],l.LabelTextBuilder&&C.DrawHorzBarText(C.GetDrawValueText(a,i),w[i*F+o]);else a=e[0],l=x[a],n=C.GetDrawValueText(a,i),C.DrawValueText(l,n,f+SMALL_GAP+b.measureTextWidth(n)/2,(h+g+C.valueFontHeight)/2)}},DrawBar:function(e,t){var r,o,n,i,a,l,s,c,u,d,f,p,h,g=this,m=g.graphSerieses,C=g.CalcBarCount(),b=g.CalcFirtBarIndex(e),x=g.IsDrawNegativeGraph(),v=t.Height()-g.yView3DDepth,S=x?g.YAxis.CalcValuePos(0,v):t.bottom+1,T=e.length,y=[];for(i=0;ir;r++)a=e[r],l=m[a],o=g.Value(a,i),d=g.AbsNegativeValue&&0>o,d&&(o=-o),h=new Rect(t.left+c.BeginPos+u*n/C,0,t.left+c.BeginPos+u*(n+1)/C,0),p=g.YAxisOfSeries(l).CalcValuePos(o,v),x?(h.top=Math.max(t.bottom-p,t.top),h.bottom=t.bottom-S,S>p&&(p=h.top,h.top=h.bottom,h.bottom=p)):(h.top=t.bottom-p,h.bottom=t.bottom),y.push(h),g.toUpdateShapes&&g.shapes.push(new ChartRect(a,i,h)),f=d?g.NegativeBarColor:g.GetGraphFillColor(g.IsColorSingleBar()?i:a),g.DrawRectBar(h,f,l.BorderPen,!1,!0,o>=0),++n;for(i=0;ir;r++)h=y[i*T+r],p>h.top&&(p=h.top);g.DrawValueText(l,s,(y[i*T].left+y[i*T+T-1].right+g.xView3DDepth)/2,p-SMALL_GAP/2-g.yView3DDepth)}else for(r=0;T>r;r++)a=e[r],l=m[a],l.LabelTextBuilder&&(s=g.GetDrawValueText(a,i),h=y[i*T+r],l.LabelInBar?g.DrawVertBarText(s,h):(p=h.top-SMALL_GAP/2-g.yView3DDepth,g.IsDrawNegativeGraph()&&t.bottom-So;o++)a=e[o],l=C[a],n=m.Value(a,i),10===t&&(n*=100/m.CalcGroupTotalValue(e,i)),d=m.YAxisOfSeries(l).CalcValuePos(n,S),g=new Rect(f,Math.min(Math.max(h-d,r.top),h),p,h),y.push(g),m.toUpdateShapes&&m.shapes.push(new ChartRect(a,i,g)),u=m.GetGraphFillColor(a),m.DrawRectBar(g,u,l.BorderPen,!1,o===T-1,!0),h-=d;if(l.LabelInBar)for(o=0;T>o;o++)a=e[o],l=C[a],l.LabelTextBuilder&&m.DrawVertBarText(m.GetDrawValueText(a,i),y[i*T+o]);else a=e[0],l=C[a],m.DrawValueText(l,m.GetDrawValueText(a,i),(f+p+m.xView3DDepth)/2,h-SMALL_GAP/2-m.yView3DDepth)}},DrawRectBar:function(e,t,r,o,n,i){function a(e,t){s.selectFillColor(e),s.selectPen(new Pen(1,e,1)),s.drawPolyLine(t,1,1),s.restorePen(),s.restoreFillStyle()}var l=this,s=l.context,c=colorGradientLight(t),u=colorGradientDark(t),d=[];l.Chart3DReal&&(d.length=8,d[0]=e.right-1,d[1]=e.top,d[2]=d[0]+l.xView3DDepth,d[3]=d[1]-l.yView3DDepth,(n||o)&&(d[4]=d[2]-e.Width(),d[5]=d[3],d[6]=e.left,d[7]=d[1],a(c,d)),!n&&o||(d[4]=d[2],d[5]=e.bottom-l.yView3DDepth,d[6]=e.right-1,d[7]=e.bottom,a(u,d))),l.Chart3DReal?(s.selectFillColor(t),s.rectangle2(e,1),s.restoreFillStyle()):s.drawBar(e,r,t,o?i?1:3:i?0:2)},DrawLine:function(e,t,r){var o,n,i,a,l,s,c,u,d=this,f=d.context,p=e.length,h=[];for(o=0;p>o;o++)h.push([]);for(n=0;no;o++){i=e[o],a=d.graphSerieses[i];var g={x:c,y:r.bottom-d.YAxisOfSeries(a).CalcValuePos(d.Value(i,n),r.Height())};h[o].push(g),d.toUpdateShapes&&d.shapes.push(new ChartPoint(i,n,g.x,g.y,a.MarkerSize))}for(o=0;p>o;o++)(7===t?d.DrawCurveLine:d.DrawPolyLine).call(d,h[o],e[o]);for(o=0;p>o;o++)d.DrawPointsMarker(h[o],e[o]);for(n=0;no;o++)i=e[o],a=d.graphSerieses[i],a.LabelTextBuilder&&(u=r.bottom-d.YAxisOfSeries(a).CalcValuePos(d.Value(i,n),r.Height()),-1!==a.MarkerStyle&&(u-=a.MarkerSize/2),l=d.GetDrawValueText(i,n),s=f.measureTextWidth(l),c=h[o][n].x+s/2,d.GroupCount>1&&(c-=s*n/(d.GroupCount-1)),d.DrawValueText(a,l,c,u))},DrawBubble:function(e,t){var r,o,n,i,a,l,s,c,u,d=this,f=e.length,p=[],h=[];for(r=0;f>r;r++)for(i=e[r],a=d.graphSerieses[i],c=[],u=[],p.push(c),h.push(u),s=d.ValueCount(i),n=0;s>n;n++){o=d.Value(i,n),l=Math.sqrt(o.z/d.DrawBubbleScalePerCm)*PixelsPerCm/2;var g={x:t.left+d.XAxis.CalcValuePos(o.x,t.Width()),y:t.bottom-d.YAxisOfSeries(a).CalcValuePos(o.y,t.Height())};c.push(g),u.push(l),d.toUpdateShapes&&d.shapes.push(new ChartPoint(i,n,g.x,g.y,l)),a.LabelTextBuilder&&d.DrawValueText(a,d.GetDrawValueText(i,n),g.x,g.y-l)}for(r=0;f>r;r++)i=e[r],d.DrawPointsMarker(p[i],i,h[i])},DrawXYScatterGraph:function(e,t,r){var o,n,i,a,l,s,c,u=this,d=e.length,f=[];for(o=0;d>o;o++)for(a=e[o],l=u.graphSerieses[a],c=[],f.push(c),s=u.ValueCount(a),i=0;s>i;i++){n=u.Value(a,i);var p={x:r.left+u.XAxis.CalcValuePos(n.x,r.Width()),y:r.bottom-u.YAxisOfSeries(l).CalcValuePos(n.y,r.Height())};c.push(p),u.toUpdateShapes&&u.shapes.push(new ChartPoint(a,i,p.x,p.y,l.MarkerSize)),l.LabelTextBuilder&&u.DrawValueText(l,u.GetDrawValueText(a,i),p.x,p.y-l.MarkerSize)}if(6===t||8===t)for(o=0;d>o;o++)a=e[o],l=u.graphSerieses[a],(8===t?u.DrawCurveLine:u.DrawPolyLine).call(u,f[a],a);for(o=0;d>o;o++)a=e[o],u.DrawPointsMarker(f[a],a)},DrawPolyLine:function(e,t){var r,o,n=this,i=n.context,a=e.length,l=[];for(r=0;a>r;r++)l.push(e[r].x,e[r].y);o=n.graphSerieses[t].BorderPen.clone(),o.Color=n.GetGraphFillColor(t),i.selectPen(o),i.drawPolyLine(l),i.restorePen()},DrawCurveLine:function(e,t){function r(e,t,r){var o=.4,n=Math.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2)),i=Math.sqrt(Math.pow(r.x-t.x,2)+Math.pow(r.y-t.y,2)),a=n+i,l=a>0?o*n/a:1,s=a>0?o*i/a:1;return{inner:{x:t.x-l*(r.x-e.x),y:t.y-l*(r.y-e.y)},outer:{x:t.x+s*(r.x-e.x),y:t.y+s*(r.y-e.y)}}}var o,n,i,a,l=this,s=l.context,c=e.length,u=[];if(c>1){for(i=e[0],a=r(i,i,e[1]),u.push(i,a.outer),o=1;c-1>o;o++)i=e[o],a=r(e[o-1],i,e[o+1]),u.push(a.inner,i,a.outer);i=e[o],a=r(e[o-1],i,i),u.push(a.inner,i),n=l.graphSerieses[t].BorderPen.clone(),n.Color=l.GetGraphFillColor(t),s.selectPen(n),s.polyBezier(u),s.restorePen()}},DrawPointsMarker:function(e,t,r){var o,n=this,i=n.context,a=n.graphSerieses[t],l=e.length;if(-1!==a.MarkerStyle)for(o=0;l>o;o++)i.DrawPointMarker(CalcPointMarkerRect(e[o].x,e[o].y,r?r[o]:a.MarkerSize),a.MarkerStyle,a.MarkerPen,a.MarkerColorAuto?n.GetGraphFillColor(t):a.MarkerColor)},DrawValueText:function(e,t,r,o){var n,i=this,a=i.context,l=i.valueFontHeight;e.LabelTextAngle?(n=toRadians(e.LabelTextAngle),r-=(a.measureTextWidth(t)*Math.cos(n)+Math.abs(l*Math.sin(n)))/2,o-=Math.abs(l*Math.cos(n)),a.drawTextRotate(t,r,o,e.LabelTextAngle)):a.drawTextCenter(t,r,o-l)},DrawLabelText:function(e,t,r){var o=this.context;o.drawTextCenter(e,t,r)},DrawVertBarText:function(e,t){var r=this,o=r.context;o.drawTextRotate(e,(t.left+t.right-r.fontHeight)/2,(t.top+t.bottom+o.measureTextWidth(e))/2,90)},DrawHorzBarText:function(e,t){var r=this,o=r.context,n=o.measureTextWidth(e),i=r.fontHeight,a=(t.left+t.right-n)/2,l=(t.top+t.bottom-i)/2;o.drawText(e,a,l)},CalcBarCount:function(){var e,t,r,o=this,n=o.graphs.length,i=0;for(e=0;n>e;e++)t=o.graphs[e],r=o.graphSerieses[t[0]].ChartType,1===r?i+=t.length:4!==r&&10!==r||i++;return i},CalcColumnBarCount:function(){var e,t,r,o=this,n=o.graphs.length,i=0;for(e=0;n>e;e++)t=o.graphs[e],r=o.graphSerieses[t[0]].ChartType,11===r?i+=t.length:12!==r&&13!==r||i++;return i},CalcFirtBarIndex:function(e){var t,r,o,n=this,i=n.graphs,a=i.length,l=0;for(t=0;a>t&&(r=i[t],r!==e);t++)o=n.graphSerieses[r[0]].ChartType,1===o?l+=r.length:4!==o&&10!==o||++l;return l},CalcFirtColumnBarIndex:function(e){var t,r,o,n=this,i=n.graphs,a=i.length,l=0;for(t=0;a>t&&(r=i[t],r!==e);t++)o=n.graphSerieses[r[0]].ChartType,11===o?l+=r.length:12!==o&&13!==o||++l;return l},CalcGroupTotalValue:function(e,t){var r,o=this,n=e.length,i=0;for(r=0;n>r;r++)i+=o.Value(e[r],t);return i},IsScatterGraph:function(){return this.Series.items[0].IsScatterGraph()},IsHorzGraph:function(){return this.Series.items[0].IsHorzGraph()},IsColorSingleBar:function(){var e=this;return 1==e.SeriesCount&&e.SingleSeriesColored},IsDrawNegativeGraph:function(){var e=this;return!e.Series.items.some(function(e){return e.ByY2Axis})&&e.YAxis.DrawMax>=0&&e.YAxis.DrawMin<0&&!e.AbsNegativeValue},Support3D:function(){return this.Series.items.every(function(e){return e.Support3D()})},YAxisOfSeries:function(e){var t=this;return e.ByY2Axis?t.Y2Axis:t.YAxis},getSeriesShowLabel:function(e){var t=this;return t.SeriesLabels[e]||(t.graphSerieses[e]?t.graphSerieses[e].SeriesName:"")},getUsingTitleFont:function(){return this.TitleFont.UsingFont()},getUsingValueFont:function(){return this.ValueFont.UsingFont()},GetGraphFillColor:function(e){var t,r,o,n,i=this,a=i.graphSerieses[e],l=i.FillColors?i.FillColors.length:ChartSeriesColorCount;return i.IsColorSingleBar()||a.FillColorAuto?(t=Math.floor(e/l),r=e%l,o=i.FillColors?i.FillColors[r]:colorAlpha(ChartSeriesColorArray[r],i.report.viewer.alpha.chartGraph),t>0&&(n=color2rgba(o),o=rgba2color((n.r+73*r+196*t)%255,(n.g+197*r+120*t)%255,(n.b+117*r+90*t)%255,n.a)),o):a.FillColor},GetSeriesIndexByLabel:function(e,t){var r=this,o=r.SeriesLabels.indexOf(e);return 0>o&&t&&(o=+e,(!isNaN(o)||o>=r.SeriesCount)&&(o=-1)),o},GetGroupIndexByLabel:function(e,t){var r=this,o=r.GroupLabels.indexOf(e);return 0>o&&t&&(o=+e,(!isNaN(o)||o>=r.GroupCount)&&(o=-1)),o},GetDrawValueText:function(e,t){var r=this.graphSerieses[e];return r.LabelTextBuilder?r.LabelTextBuilder.generateChartDisplayText(e,t):""},GetGroupTotalValue:function(e,t){var r,o=this,n=e.length,i=0;if(!o.IsScatterGraph())for(r=0;n>r;r++)i+=o.Value(e[r],t);return i},GetSeriesTotalValue:function(e){var t,r,o=this,n=0;if(o.IsScatterGraph())for(r=o.ValueCount(e),t=0;r>t;t++)n+=o.Value(e,t).z;else for(t=0;tc&&r&&(c=d.SeriesCount,d.SeriesCount++,d.SeriesLabels.push(s)),0>c))throw 0;if(s=n(),a=a.nextSibling,u=d.GetGroupIndexByLabel(s,!o),0>u&&o&&(u=d.GroupCount,d.GroupCount++,d.GroupLabels.push(s)),0>u)throw 0;if(t)d.SetValue(c,u,+n()+d.Value(c,u));else for(c=0;a;)d.SetValue(c,u,+n()+d.Value(c,u)),c++,a=a.nextSibling;i=i.nextSibling}return f},DoLoadXYZDataFromXML:function(e,t,r){function o(){return a?i.text:i.textContent}var n,i,a,l,s,c,u,d,f=this,p=1,h=parseXML(e);if(!dom)return 0;for(t&&(f.SeriesCount=0,f.SeriesLabels=[]),f.values=[],n=h.childNodes[0].firstChild,nodeText=n.hasOwnProperty("text");n;){if(i=n.firstChild,FirstSeries?(l=o(),d=f.GetSeriesIndexByLabel(l,!t),0>d&&t&&(d=m_SeriesCount,++m_SeriesCount,m_SeriesLabels[d]=l),i=i.nextSibling):d=0,0>d)throw 0;s=+o(),i=i.nextSibling,c=+o(),r||(i=i.nextSibling,u=+o()),f.AddXYZValue(d,s,c,u),n=n.nextSibling}return p},LoadXYDataFromXML:function(e,t){return this.DoLoadXYZDataFromXML(e,t,1)},LoadXYZDataFromXML:function(e,t){return DoLoadXYZDataFromXML(e,t,0)},AddXYZValue:function(e,t,r,o){var n=this;n.prepareValues(),n.values[e].push({x:t,y:r,z:o})},EmptyValues:function(){var e=this;e.values=[],e.prepareValues()}},prototypeCopyExtend(Chart,CanvasBox),gr.dom.Report=Report}(),function(e,t){"use strict";var r=gr.helper,o=gr.common,n=(gr.enum_,gr.const_),i=r.penStyleText,a=r.fontCSSText,l=r.pixelsToHtml,s=r.colorValue2Html,c=r.assignObject,u=r.compareObject,d=r.parseXML,f=r.xmlToReportDataJSON,p=r.addEvent,h=o.HtmlStyles,g=function(e,t){var r,o,n=this;n.obj=e,n.index=t,e.FreeCell?e.isSingleDockControl()&&(o=e.Controls.items[0],o.TextFormat&&(n.TextFormat={},n.TextFormat.ForeColor=o.ForeColor,c(n.TextFormat,o.TextFormat)),r=n.padding={},r.Left=o.PaddingLeft,r.Right=o.PaddingRight,r.Top=o.PaddingTop,r.Bottom=o.PaddingBottom):(n.TextFormat={},c(n.TextFormat,e.TextFormat),n.TextFormat.ForeColor=e.ForeColor,r=n.padding={},r.Left=e.PaddingLeft,r.Right=e.PaddingRight,r.Top=e.PaddingTop,r.Bottom=e.PaddingBottom),e.BorderCustom?(n.border={},c(n.border,e.Border)):n.ownerGrid=e.ownerGrid(),n.toFillBack=e.toFillBack(),n.toFillBack&&(n.BackColor=e.BackColor)};g.prototype={compare:function(e){var t=this,r=new g(e);return u(t.padding,r.padding)&&u(t.TextFormat,r.TextFormat)&&u(t.border,r.border)&&t.toFillBack===r.toFillBack&&t.BackColor===r.BackColor&&t.ownerGrid===r.ownerGrid},get className(){return"-grcell"+this.index},getStyles:function(){var e=this,t=e.TextFormat,r=new h;return t&&(r.addTextFormat(e,1),r.addObjectPadding(e)),e.border?r.addBorder(e.border):r.addCellBorder(e.ownerGrid),e.toFillBack&&r.addBackColor(e.BackColor),r}};var m=function(e,t){var r=this,o=r.padding={},n=e.getUsingFont();r.obj=e,r.index=t,r.asCell=!!e.cell,e.TextFormat&&(r.TextFormat={},r.TextFormat.ForeColor=e.ForeColor,c(r.TextFormat,e.TextFormat)),o.Left=e.PaddingLeft,o.Right=e.PaddingRight,o.Top=e.PaddingTop,o.Bottom=e.PaddingBottom,r.border={},c(r.border,e.Border),r.toFillBack=e.toFillBack(), +r.toFillBack&&(r.BackColor=e.BackColor2()),r.textdecoration={Underline:n.Underline,Strikethrough:n.Strikethrough}};m.prototype={compare:function(e){var t=this,r=new m(e);return u(t.padding,r.padding)&&u(t.TextFormat,r.TextFormat)&&u(t.border,r.border)&&u(t.textdecoration,r.textdecoration)&&t.toFillBack===r.toFillBack&&t.BackColor===r.BackColor&&t.asCell===r.asCell},get className(){return"-grctrl"+this.index},getStyles:function(){var e=this,t=e.textdecoration,r=new h;return!e.asCell&&r.add("position","absolute"),r.addObjectPadding(e),r.addBorder(e.border),e.TextFormat&&r.addTextFormat(e,e.asCell),e.toFillBack&&r.addBackColor(e.BackColor),(t.Underline||t.Strikethrough)&&r.add("text-decoration",(t.Underline?"underline":"")+(t.Underline&&t.Strikethrough?" ":"")+(t.Strikethrough?"line-through":"")),r}};var C=function(e,t){var r=this;r.index=t,r.Left=e.pxRect.left,r.Top=e.pxRect.top,r.Width=e.getContentWidth(),r.Height=e.getContentHeight()};C.prototype={compare:function(e){var t=this;return t.Left===e.pxRect.left&&t.Top===e.pxRect.top&&t.Width===e.getContentWidth()&&t.Height===e.getContentHeight()},get className(){return"-grpos"+this.index},getStyles:function(){var e=this,t=new h;return t.add("left",l(e.Left)),t.add("top",l(e.Top)),t.add("width",l(e.Width)),t.add("height",l(e.Height)),t}};var b=function(e,t){function r(){var e,t=o.report.viewer;o.ShowColLine&&(e=new h,e.add("border-right",i(o.ColLinePen)),t.addCustomStyle(e,w(n)+" td:last-child")),o.ShowRowLine&&n.isLastRow&&(e=new h,e.add("border-bottom",i(o.RowLinePen)),t.addCustomStyle(e,w(n)+" td"))}var o,n=this,a=e.cells?e:0;n.index=t,n.section=e,n.isLastRow=1,n.Height=e.CanShrink?0:e.pxHeight,a&&(e=a.section,n.isLastRow=e.tableRows[e.tableRows.length-1]===a),e.Font.font&&(n.Font={},c(n.Font,e.Font.font)),e.owner.Header&&(o=e.owner.owner,n.detailgrid=o),!e.toFillBack()||a&&!o||(n.BackColor=e.BackColor),o&&o.report.fixed&&r()};b.prototype={compare:function(e){var t,r,o=this,n=e.cells?e:0,i=e.CanShrink?0:e.pxHeight,a=1;return n&&(e=e.section,a=e.tableRows[e.tableRows.length-1]===n),t=e.toFillBack()&&(!n||e.owner.Header),r=e.Font.font,o.Height===i&&(r&&o.Font&&u(r,o.Font)||!r&&!o.Font)&&(t&&o.BackColor&&o.BackColor===e.BackColor||!t&&!o.BackColor)&&(e.owner.Header&&e.owner.owner===o.detailgrid||!e.owner.Header&&!o.detailgrid)&&o.isLastRow===a},get className(){return"_grsec"+this.index},getStyles:function(){var e=this,t=e.section,r=t.pctHeight,o=e.Height,n=e.detailgrid,a=new h;return(r||o)&&a.add("height",r?r+"%":l(o)),(r||!t.cells&&t.report.viewer.options.reportFitWidth)&&a.add("width","100%"),e.Font&&(a.items=a.items.concat(x.prototype.getStyles.call(e.Font).items)),e.BackColor&&a.addBackColor(e.BackColor),e.isLastRow&&n&&n.ShowRowLine&&!n.report.fixed&&a.add("border-bottom",i(n.RowLinePen)),a}};var x=function(e,t){var r=this;r.index=t,r.Point=e.Point,r.Bold=e.Bold,r.Italic=e.Italic,r.Name=e.Name};x.prototype={compare:function(e){var t=this;return t.Point===e.Point&&t.Bold===e.Bold&&t.Italic===e.Italic&&t.Name===e.Name},get className(){return"-grfs"+this.index},getStyles:function(){var e=new h;return e.add("font",a(this)),e}};var v=function(e,t,r){var o=this;o.htmlStyles=e,t&&(o.cssName=t),r&&(o.index=r)};v.prototype={get className(){function e(){var e=t.cssName,r=e;return e instanceof Array&&(r="",e.forEach(function(e){r&&(r+=","),r+=e})),r}var t=this,r=t.index;return r>=0?"-grcs"+r:e()},getStyles:function(){return this.htmlStyles}};var S=function(e,t){var r=this;r.items=[],r.viewer=e,r.classStyle=t};S.prototype={toString:function(){for(var e,t,r=this,o=r.items,n=o.length,i=0,a="";n>i;)e=o[i++],t=(e.cssName?"":".")+r.viewer._getCSSName(e),a+=t+"{"+e.getStyles().getText()+"}",DEBUG&&(a+="\r\n");return a},select:function(e,t){var r=this;return r.viewer._getCSSName(r.selectItem(e,t))},selectItem:function(e,t){var r,o,n=this,i=n.items.length;if(t&&t.compare(e))return t;for(r=0;i>r;r++)if(o=n.items[r],o.compare(e))return o;return o=new n.classStyle(e,i),n.items.push(o),o}};var T=function(){var e=this;e.controlPosition=gr.enum_.controlLayout.auto,e.reportFitWidth=!1,e.reportFitHeight=!1,e.detailgridResize=gr.enum_.detailgridResize.asDesign,e.singleChartFill=!0,e.updateOnSize=!1,e.fixed=!1,e.fixedColsLeft=0,e.fixedColsRight=0,e.fixedFooter=1,e.selectionHighlight=!0,e.selectionCell=!1,e.selectionTextColor="rgba(255,255,255,1)",e.selectionBackColor="rgba(20,20,127,1)",e.hoverEnabled=!0,e.hoverBackColor="#eee",e.hoverTextColor="#000"};T.prototype={get fixedHeaderFooter(){return this.fixed},set fixedHeaderFooter(e){his.fixed=e}};var y=function(e){var t,r=0,o=e.length;if("string"!=typeof e)return 0;for(;o>r&&(t=e[r],/\s/g.test(t));)r++;return o>r&&"{"!==t&&"<"!==t},F=function(e){return"string"==typeof e&&"_WR_"==e.substr(0,4)?0:y(e)},w=function(e){return(e.cssName?"":".")+e.className},D=function(e,t,r,o,n){var i=this,a=0,l=34;i.running=!1,i.generated=!1,i.events={},i.viewerNo=e,i.viewerHolderID=t,i.reportSource=r,i.dataSource=o,i.alpha={background:a,border:l,stroke:l,pen:l,text:l,chartGraph:a,chartStroke:0},i.options=new T,i.attachOptions(n),i.report=new gr.dom.Report(i)};D.prototype={attachOptions:function(e){var t=this;t.stop(),e&&c(t.options,e)},loadReport:function(e,r,o){var n,i=this,a=i.report,l=e;if(r=r||r===t,o=o||o===t){try{l.responseText!==t&&(l=l.responseText),i._rawReport=l,a.load(l),i.events.onReportLoaded&&i.events.onReportLoaded(i)}catch(s){o=0,n="解析报表模板"}o&&(i.reportPrepared=!0,r&&!i.waitData&&i.generate())}else n="获取报表模板";!o&&i.displayError(n,e,1)},loadData:function(e,r,o){var n,i=this,a=e;if(r=r||r===t,o=o||o===t){try{if(a.responseText!==t&&(a=a.responseText),i._rawData=a,"string"==typeof a)if(""===a)a={};else if("{"===a.charAt(0))a=JSON.parse(a);else{if(a=a.replace(/\r\n/g,""),a=d(a),!a)throw 0;a=f(a)}i.tables=a}catch(l){o=0,n="解析报表数据"}o&&(i.dataPrepared=!0,r&&i.generate())}else n="获取报表数据";!o&&i.displayError(n,e,0)},start:function(){var t=this;t.waitData&&(t.waitData=0),t.generated||t.running||(t.running=!0,t.events.onGenerateBegin&&t.events.onGenerateBegin(t),t.reportPrepared=t.reportPrepared||!t._rs,t.dataPrepared=t.dataPrepared||!t._ds,t.reportPrepared&&t.dataPrepared?(t.events.onReportLoaded&&t.events.onReportLoaded(t),!t.waitData&&t.generate()):(t.reportPrepared||(t._rs.url?e.rubylong.ajax(t._rs,function(e,r){t.loadReport(e,1,r)},t):t.loadReport(t._rs)),t.dataPrepared||(t._ds.url?e.rubylong.ajax(t._ds,function(e,r){t.loadData(e,1,r)},t):t.loadData(t._ds))))},stop:function(e,r){var o=this,n=o.report,i=o.holder;return o.running?!1:(o.generated&&(e||(n.Clear(),o.reportPrepared=!1,o._rawReport=t),r||(o.tables=t,o.dataPrepared=!1,o._rawData=t),i&&(i.innerHTML="",DEBUG&&(document.getElementById("htmlText")&&(document.getElementById("htmlText").innerText="htmlText"),document.getElementById("stylesText")&&(document.getElementById("stylesText").innerText="stylesText"))),n.unprepare(),o.generated=!1),!0)},update:function(){var e=this;e.stop(1,1),e.generate()},generate:function(){function t(){i.fontStyles=new S(i,x),i.controlStyles=new S(i,m),i.cellStyles=new S(i,g),i.sectionStyles=new S(i,b),i.positionStyles=new S(i,C),i.customStyles=new S(i,v)}var r,o,i=this,a=i.report,l=i.options,c=document.getElementById(i.viewerHolderID);if(c||(c=document.body),i.holder=c,i.stop(1,1),i.reportPrepared&&i.dataPrepared){try{if(c.innerHTML="",DEBUG&&(i.stylesText=""),i._detailgrids=[],i._domevents=[],i.html="",t(),c.classList.add(i.selectFont2(a.Font.font)),16777215!==a.BackColor&&(c.style.backgroundColor=s(a.BackColor)),i.html=a.generateHtml(),i.generateStyles(),c.innerHTML=i.html,a.toFillHolder&&(c.style.height="100%"),a.singleChart&&(c.style.backgroundColor=s(a.singleChart.BackColor)),a.startCanvas(),i._detailgrids.forEach(function(e,t){var r=document.getElementById(i._getDetailGridID(t));p(r,"mousedown",function(t){e.onmousedown(t)})}),a._has_cb)for(r=document.querySelectorAll("."+n.CSS_CB),o=r.length;o-- >0;)p(r[o],"click",function(e){a.onCheckBoxClick(e)});i._domevents.forEach(function(e,t){for(r=document.querySelectorAll("."+i._getEventCSSName(t+1)),t=r.length;t-- >0;)switch(e.type){case 1:p(r[t],"click",function(t){e.fun(t,e.obj)});break;default:p(r[t],"dblclick",function(t){e.fun(t,e.obj)})}}),DEBUG&&(document.getElementById("htmlText")&&(document.getElementById("htmlText").innerText=i.html),document.getElementById("stylesText")&&(document.getElementById("stylesText").innerText=i.stylesText))}catch(u){c.innerHTML="生成报表时发生异常错误!
Error Code: "+u.number+"
Error Name: "+u.name+"
Error Message: "+u.message}i.running=!1,i.generated=!0,i.events.onGenerateEnd&&i.events.onGenerateEnd(i),l.updateOnSize&&!e.onresize&&(i._gen_w=0,i._gen_h=0,e.onresize=function(){var t=e.innerWidth,r=e.innerHeight;i.generated&&(i._gen_w==t&&i._gen_h==r||(i._gen_w=t,i._gen_h=r,i.update()))})}},generateStyles:function(){function e(e){var t=">tbody>tr";e&&(t+=">td"),t+=":hover",s+=n.CSS_DG_F+t+(i?":not(."+n.CSS_SH+")":"")+" {background-color:"+o.hoverBackColor+";color:"+o.hoverTextColor+";}\n"}var t=this,r=t.report,o=t.options,i=o.selectionHighlight,a=n.ID_REPORT_STYLE+t.viewerNo,l=document.getElementById(a),s=r.toFillHolder?"html,body{height:100%;}\n*{margin:0;padding:0;}":"";l||(l=document.createElement("STYLE"),l.id=a,document.head.appendChild(l)),r._has_cb&&(s+="."+n.CSS_CB+"{}\n"),s+=n.CSS_DG_F+"{table-layout:fixed;}\n",o.hoverEnabled&&(e(0),e(1)),i&&(s+="."+n.CSS_SH+"{ background-color:"+o.selectionBackColor+";color:"+o.selectionTextColor+";}\n"),l.innerHTML=[s,t.fontStyles,t.positionStyles,t.sectionStyles,t.cellStyles,t.controlStyles,t.customStyles,t.getDomEventStyles()].join(""),DEBUG&&(t.stylesText=l.innerHTML)},getDomEventStyles:function(){for(var e=this,t=e._domevents.length,r="";t>0;)r+="."+e._getEventCSSName(t--)+"{}\n";return r},addCustomStyle:function(e,t){var r=this.customStyles.items,o=new v(e,t);return r.push(o),!t&&(o.index=r.length),o},selectCell:function(e){return this.cellStyles.select(e,e.defaultStyle)},selectCellItem:function(e){return this.cellStyles.selectItem(e)},selectControl:function(e){return this.controlStyles.select(e,e.defaultStyle)},selectControlItem:function(e){return this.controlStyles.selectItem(e)},selectSection:function(e){return this.sectionStyles.select(e,e.defaultStyle)},selectSectionItem:function(e){return this.sectionStyles.selectItem(e)},selectPosition:function(e){return this.positionStyles.select(e,e.defaultPositionStyle)},selectPositionItem:function(e){return this.positionStyles.selectItem(e)},selectFont:function(e,t){return this.fontStyles.select(e,t)},selectFont2:function(e){var t=this;return t._getCSSName(t.selectFontItem(e))},selectFontItem:function(e){return this.fontStyles.selectItem(e)},displayError:function(e,r,o){var i,a=this,l=r.responseText!==t,s=l?o?a._rs:a._ds:t;i='

'+e+"失败,锐浪HTML5报表("+n.VERSION+")

",s&&(i+='"),l?(i+="
HTTP Status: "+r.status+"
HTTP Status Text: "+r.statusText+"
",r.responseText&&(i+="
HTTP Response Text:
"+r.responseText+"
")):i+="string"==typeof r?"
Loaded Text:
"+r+"
":"
Loaded Json Object:
"+JSON.stringify(r)+"
",i+="
",a.holder.innerHTML=i},_getEventCSSName:function(e){var t=this.viewerNo,r="_grec"+e;return t&&(r+="-"+t),r},_getCSSName:function(e){var t=this.viewerNo,r=e.className;return t&&(r="_"+t+r),r},_getDetailGridID:function(e){var t=this.viewerNo,r=n.ID_DETAILGRID+e;return t&&(r+="-"+t),r},get reportURL(){var e=this._rs;return e?e.url:""},set reportURL(e){var t=this;t._rs={},t._rs.url=e},get dataURL(){var e=this._ds;return e?e.url:""},set dataURL(e){var t=this;t._ds={},t._ds.url=e},get reportSource(){return this._rs},set reportSource(e){var t=this;e&&(t._rs={},F(e)?t._rs.url=e:e.url?c(t._rs,e):t._rs=e)},get dataSource(){return this._ds},set dataSource(e){var t=this;e&&(t._ds={},y(e)?t._ds.url=e:e.url?c(t._ds,e):t._ds=e)},get reportText(){var e=this._rawReport;return"string"!=typeof e&&(e=JSON.stringify(e)),e},get dataText(){var e=this._rawData;return"string"!=typeof e&&(e=JSON.stringify(e)),e}},e.rubylong={grhtml5:{controlLayout:gr.enum_.controlLayout,detailgridResize:gr.enum_.detailgridResize,barcodeURL:"/html5/General/Barcode.ashx",Viewer:D,helper:r,utility:gr.Utility,holderIDs:[],insertReportViewer:function(t,r,o,n){var i=e.rubylong.grhtml5.holderIDs,a=i.indexOf(t);return 0>a&&(a=i.length,i.push(t)),new D(a,t,r,o,n)},createReport:function(){return new gr.dom.Report}},ajax:function(e,t,r,o){function n(e,t){return t?t:/.grf|.txt|.xml|.json/.test(e)?"GET":"POST"}var i,a=new XMLHttpRequest;if(a.onreadystatechange=function(){4==a.readyState&&a.status>0&&t.call(r,a,200==a.status)},a.onerror=function(){t.call(r,a,0)},"string"==typeof e&&(e={url:e}),e.method||(e.method=n(e.url,o)),a.open(e.method,e.url,!0),i=e.headers,i&&"object"==typeof i)for(var l in i)a.setRequestHeader(l,i[l]);a.send(e.data)}},"classList"in document.documentElement||Object.defineProperty(HTMLElement.prototype,"classList",{get:function(){function e(e){return function(r){var o=t.className.split(/\s+/g),n=o.indexOf(r);e(o,n,r),t.className=o.join(" ")}}var t=this;return{add:e(function(e,t,r){~t||e.push(r)}),remove:e(function(e,t){~t&&e.splice(t,1)}),toggle:e(function(e,t,r){~t?e.splice(t,1):e.push(r)}),contains:function(e){return!!~t.className.split(/\s+/g).indexOf(e)},item:function(e){return t.className.split(/\s+/g)[e]||null}}}})}(window); \ No newline at end of file diff --git a/web/src/components/report/F9ParamControl.vue b/web/src/components/report/F9ParamControl.vue new file mode 100644 index 0000000..6e2a8fa --- /dev/null +++ b/web/src/components/report/F9ParamControl.vue @@ -0,0 +1,294 @@ + + + + + diff --git a/web/src/components/report/ReportDesign.vue b/web/src/components/report/ReportDesign.vue new file mode 100644 index 0000000..d19154a --- /dev/null +++ b/web/src/components/report/ReportDesign.vue @@ -0,0 +1,793 @@ + + + + + diff --git a/web/src/components/report/ReportEditor.vue b/web/src/components/report/ReportEditor.vue new file mode 100644 index 0000000..e78aebf --- /dev/null +++ b/web/src/components/report/ReportEditor.vue @@ -0,0 +1,768 @@ + + + + + diff --git a/web/src/components/select/CraftZhuangKouSelect.vue b/web/src/components/select/CraftZhuangKouSelect.vue new file mode 100644 index 0000000..e61e887 --- /dev/null +++ b/web/src/components/select/CraftZhuangKouSelect.vue @@ -0,0 +1,52 @@ + + + diff --git a/web/src/components/select/F9SelectPage.vue b/web/src/components/select/F9SelectPage.vue new file mode 100644 index 0000000..ae68686 --- /dev/null +++ b/web/src/components/select/F9SelectPage.vue @@ -0,0 +1,190 @@ + + + + + diff --git a/web/src/components/select/index.js b/web/src/components/select/index.js new file mode 100644 index 0000000..4712336 --- /dev/null +++ b/web/src/components/select/index.js @@ -0,0 +1,39 @@ +/** + * 选择页注册中心(Web 版 F9.Common.Report.SelectPages) + * + * WinForms 端通过 base.ResolvePage() + ShowModalPage 弹出选择页; + * Web 端把「选择页」注册到本中心,报表查询控件只需声明 page: 'CraftZhuangKou' 即可复用。 + * + * 用法: + * import { registerSelectPage, getSelectPage } from '@/components/select'; + * registerSelectPage('MyPage', { title: '我的选择页', component: MySelectVue }); + */ +import F9SelectPage from './F9SelectPage.vue'; +import CraftZhuangKouSelect from './CraftZhuangKouSelect.vue'; + +const pages = new Map(); + +export function registerSelectPage(key, def) { + if (!key || !def?.component) return null; + const item = { key, title: def.title || key, component: def.component, multiple: !!def.multiple }; + pages.set(key, item); + return item; +} + +export function getSelectPage(key) { + return pages.get(String(key || '').trim()) || null; +} + +export function hasSelectPage(key) { + return pages.has(String(key || '').trim()); +} + +export function allSelectPages() { + return Array.from(pages.values()); +} + +// 注意:.vue 只有默认导出,必须先 import 再具名导出(不能 export { X } from './X.vue') +export { F9SelectPage, CraftZhuangKouSelect }; + +// ---- 内置选择页 ---- +registerSelectPage('CraftZhuangKou', { title: '工艺庄口', component: CraftZhuangKouSelect }); diff --git a/web/src/router/index.js b/web/src/router/index.js index dd004fa..f2ebe1e 100644 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -60,6 +60,8 @@ export const componentMap = { 'base/wuzimulu/index': () => import('../views/base/wuzimulu/index.vue'), 'base/gongyingshang/index': () => import('../views/base/gongyingshang/index.vue'), 'base/employee/index': () => import('../views/base/employee/index.vue'), + 'qc/gyzxs/index': () => import('../views/qc/gyzxs/index.vue'), + 'qc/yjss/index': () => import('../views/qc/yjss/index.vue'), 'prod/lsjl/index': () => import('../views/prod/lsjl/index.vue'), 'prod/xdjy/index': () => import('../views/prod/xdjy/index.vue'), 'prod/hbjy/index': () => import('../views/prod/hbjy/index.vue'), @@ -69,7 +71,9 @@ export const componentMap = { 'sys/user/index': () => import('../views/sys/user/index.vue'), 'sys/role/index': () => import('../views/sys/role/index.vue'), 'sys/menu/index': () => import('../views/sys/menu/index.vue'), - 'sys/department/index': () => import('../views/sys/department/index.vue') + 'sys/department/index': () => import('../views/sys/department/index.vue'), + 'report/center/index': () => import('../views/report/center/index.vue'), + 'report/manage/index': () => import('../views/report/manage/index.vue') }; // 空壳布局组件(用于目录节点,透传子路由) diff --git a/web/src/utils/departments.js b/web/src/utils/departments.js new file mode 100644 index 0000000..38ffd43 --- /dev/null +++ b/web/src/utils/departments.js @@ -0,0 +1,233 @@ +/** + * Web 版 F9GlobalUIHelper(对应 F9.Common.Application.Services.GlobalUIHelpers.F9GlobalUIHelper) + * -------------------------------------------------------------------------------------------- + * WinForms 端部门下拉不是「一把全量」,而是按【口径】取数后再建树: + * + * Dep_Select_GenarateAllDep(sel) → Department_GetAllCacheAsync (全部) + * Dep_Select_GenarateOfSpecialDutyTypes(sel, types) → GetDepartmentByDutyTypeWithoutRightQuery (自身 DutyType 命中) + * Dep_Select_GenarateSiyeDep(sel, includeSiye) → GetDepartmentOfSiyeQuery (丝业中心 + 全部子孙) + * Dep_Select_GenarateDepForEmployee/Kaoqin/Xinzi → GetDepartmentForXxxQuery (本部门 + 子孙;管理员/开发=全部) + * 再 RemoveAll(ManageType == 公司) + * Dep_GenerateSelectItems(deps) → ParentId 递归建树,排序 SortOrder → Code → Name + * Dep_Select_SelectDep(sel, dep_id) → 递归查找并选中(默认选中登录用户部门) + * + * Web 端同构实现:本模块 + F9ParamControl 的 tree-select 渲染。 + * 数据源:GET /api/sys/department(DepartmentReadDto_View,含 parentId / manageType / dutyType / sortOrder) + */ +import request from './request'; + +/** Department_DutyType_Enum(与后端枚举数值一致) */ +export const DUTY = { + 其他: 5, + 仓储: 10, + 选剥茧: 15, + 煮茧: 20, + 前缫: 25, + 后缫: 30, + 设备办: 35, + 副产品: 40, + 质检: 45, + 丝绵: 50, + 人力资源: 55, + 丝业中心: 100, + 前缫管理: 105, + 后缫检验: 110, + 后缫整理: 115, +}; + +/** Department_ManageType_Enum */ +export const MANAGE = { + 公司: 5, + 部门: 10, + 车间: 15, + 科室: 20, + 岗位: 25, + 虚拟: 30, +}; + +/** + * 枚举 → 下拉选项 [{ value: 5, label: '公司' }](按数值升序,与后端枚举定义顺序一致) + * 用于部门管理页的「管理类型 / 职责类型」绑定。 + */ +export function enumOptions(map) { + return Object.entries(map) + .map(([label, value]) => ({ label, value })) + .sort((a, b) => a.value - b.value); +} + +/** 枚举值 → 中文名(取不到就原样返回,便于发现后端新增了枚举项) */ +export function enumLabel(map, value) { + const hit = Object.entries(map).find(([, v]) => v === Number(value)); + return hit ? hit[0] : value; +} + +/** 部门口径(对应 F9 的各个 GetDepartmentXxxQuery) */ +export const DEP_SCOPE = { + ALL: 'all', // 全部部门 + SIYE: 'siye', // 丝业(丝业中心 + 子孙) + EMPLOYEE: 'employee', // 职工口径 + KAOQIN: 'kaoqin', // 考勤口径 + XINZI: 'xinzi', // 薪资口径 +}; + +// ==================== 数据加载(对应 Department_GetAllCacheAsync)==================== + +let cache = null; + +export function clearDepCache() { + cache = null; +} + +function enumNum(v) { + if (typeof v === 'number') return v; + if (typeof v === 'string') { + if (/^\d+$/.test(v.trim())) return Number(v.trim()); + if (v in DUTY) return DUTY[v]; + if (v in MANAGE) return MANAGE[v]; + } + return 0; +} + +function normalize(d) { + return { + id: Number(d.id ?? d.Id ?? 0), + code: d.code ?? d.Code ?? '', + name: d.name ?? d.Name ?? '', + parentId: Number(d.parentId ?? d.ParentId ?? 0), + manageType: enumNum(d.manageType ?? d.ManageType), + dutyType: enumNum(d.dutyType ?? d.DutyType), + isActive: d.isActive ?? d.IsActive ?? true, + sortOrder: Number(d.sortOrder ?? d.SortOrder ?? 0), + raw: d, + }; +} + +/** 全部部门(含缓存,对应 F9 的缓存 DTO) */ +export async function getAllDeps(force = false) { + if (!force && cache) return cache; + const res = await request.get('/sys/department'); + const rows = Array.isArray(res?.data) ? res.data : (res?.data?.items || []); + cache = rows.map(normalize).filter((d) => d.id && d.isActive !== false); + return cache; +} + +// ==================== 各口径过滤 ==================== + +/** 对应 GetDepartmentByDutyTypeWithoutRightQuery:只保留自身 DutyType 命中的部门 */ +export function depsByDuty(deps, types) { + const set = (Array.isArray(types) ? types : [types]).map(enumNum); + return (deps || []).filter((d) => set.includes(d.dutyType)); +} + +/** 对应 GetDepartmentOfSiyeQuery:丝业中心 + 全部子孙(includeSiye 时含丝业中心自身) */ +export function depsOfSiye(deps, includeSiye = true) { + const all = deps || []; + const root = all.find((d) => d.dutyType === DUTY.丝业中心) + || all.find((d) => /丝业/.test(d.name || '')); + if (!root) return []; + const out = []; + const walk = (pid) => { + all.filter((d) => d.parentId === pid).forEach((d) => { out.push(d); walk(d.id); }); + }; + walk(root.id); + if (includeSiye) out.unshift(root); + return out; +} + +/** 对应 GetDepartmentForEmployee/Kaoqin/XinziQuery:本部门 + 全部子孙;管理员或取不到本部门时返回全部 */ +export function depsOfLoginUser(deps, depId, { isAdmin = false } = {}) { + const all = deps || []; + if (isAdmin || !depId) return all.slice(); + const self = all.find((d) => d.id === Number(depId)); + if (!self) return all.slice(); + const out = [self]; + const walk = (pid) => { + all.filter((d) => d.parentId === pid).forEach((d) => { out.push(d); walk(d.id); }); + }; + walk(self.id); + return out; +} + +/** 移除「公司」管理类型(F9 的 entities.RemoveAll(x => x.ManageType == 公司)) */ +export function removeCompany(deps) { + return (deps || []).filter((d) => d.manageType !== MANAGE.公司); +} + +// ==================== 建树(对应 Dep_GenerateSelectItems)==================== + +function cmpDep(a, b) { + return (a.sortOrder - b.sortOrder) + || String(a.code).localeCompare(String(b.code)) + || String(a.name).localeCompare(String(b.name)); +} + +/** 平表 → 树形选项 [{key,value,children}],父不在集合内的节点自动成为根 */ +export function buildDepTree(deps) { + const list = (deps || []).slice().sort(cmpDep); + const ids = new Set(list.map((d) => d.id)); + const build = (dep) => { + const children = list.filter((x) => x.parentId === dep.id).sort(cmpDep).map(build); + const node = { key: String(dep.id), value: dep.name, raw: dep }; + if (children.length) node.children = children; + return node; + }; + return list.filter((d) => !ids.has(d.parentId)).map(build); +} + +/** 平表 → 扁平选项 [{key,value}](平铺下拉用) */ +export function buildDepFlat(deps) { + return (deps || []).slice().sort(cmpDep).map((d) => ({ key: String(d.id), value: d.name, raw: d })); +} + +// ==================== 登录用户(对应 Dep_Select_SelectDep 的默认选中) ==================== + +export function currentUser() { + try { + return JSON.parse(localStorage.getItem('user') || 'null') || null; + } catch (e) { + return null; + } +} + +export function currentUserDepId() { + const u = currentUser(); + const id = u?.departmentId ?? u?.DepartmentId ?? u?.department?.id ?? 0; + return Number(id) || 0; +} + +function isAdminUser() { + const u = currentUser(); + const roles = u?.roles || []; + return roles.some((r) => /admin|管理员|developer|开发/i.test(String(r))); +} + +// ==================== 对外:按口径取选项 ==================== + +/** + * @param {string|Object} scope 'all' | 'siye' | 'employee' | 'kaoqin' | 'xinzi' | { duty: [25] } + * @param {Object} opts { flat: true 返回扁平选项;depId 指定本部门;force 强制刷新缓存 } + */ +export async function depOptions(scope = 'all', opts = {}) { + const deps = await getAllDeps(opts.force); + let list; + if (scope && typeof scope === 'object' && scope.duty) { + list = depsByDuty(deps, scope.duty); + } else if (scope === DEP_SCOPE.SIYE) { + list = depsOfSiye(deps, true); + } else if ([DEP_SCOPE.EMPLOYEE, DEP_SCOPE.KAOQIN, DEP_SCOPE.XINZI].includes(scope)) { + list = removeCompany(depsOfLoginUser(deps, opts.depId ?? currentUserDepId(), { isAdmin: isAdminUser() })); + } else { + list = deps.slice(); + } + return opts.flat ? buildDepFlat(list) : buildDepTree(list); +} + +/** 树中查找某 key 是否存在(用于默认选中登录用户部门) */ +export function findInTree(nodes, key) { + for (const n of nodes || []) { + if (String(n.key) === String(key)) return n; + const sub = n.children ? findInTree(n.children, key) : null; + if (sub) return sub; + } + return null; +} diff --git a/web/src/utils/gridreport/controls/antdControls.js b/web/src/utils/gridreport/controls/antdControls.js new file mode 100644 index 0000000..515d651 --- /dev/null +++ b/web/src/utils/gridreport/controls/antdControls.js @@ -0,0 +1,99 @@ +/** + * AntdUI 基础控件(对应 Page_ReportQueryTool.GetParamControlOfAntdUI) + * + * 结构与 WinForms 端保持一致:每个 ControlType 一个分支函数,末尾统一分发。 + * AntdUI.Input / AntdUI.Select / AntdUI.SelectMultiple + * AntdUI.DatePicker / AntdUI.DatePickerYearMonth / AntdUI.DatePickerYear + */ +import { KIND } from './registry'; + +/** 分组名(编辑器下拉分组) */ +const GROUP = 'AntdUI 基础控件'; + +// ==================== 各控件定义 ==================== + +/** AntdUI.Input:文本输入,取值 input.Text */ +function ctrlInput() { + return { + type: 'AntdUI.Input', + kind: KIND.INPUT, + group: GROUP, + label: '文本输入', + useDs: false, + }; +} + +/** AntdUI.Select:下拉单选,取值 select.SelectedValue(选项来自 Ds_Static / Ds_Service) */ +function ctrlSelect() { + return { + type: 'AntdUI.Select', + kind: KIND.SELECT, + group: GROUP, + label: '下拉选择', + useDs: true, + }; +} + +/** AntdUI.SelectMultiple:下拉多选,取值 string.Join(",", SelectedValue) */ +function ctrlSelectMultiple() { + return { + type: 'AntdUI.SelectMultiple', + kind: KIND.MULTISELECT, + group: GROUP, + label: '下拉多选', + useDs: true, + hint: '取值为逗号分隔字符串,对齐 F9 的 string.Join(",", ids)', + }; +} + +/** AntdUI.DatePicker:日期,取值 datePicker.Value */ +function ctrlDatePicker() { + return { + type: 'AntdUI.DatePicker', + kind: KIND.DATE, + group: GROUP, + label: '日期', + useDs: false, + }; +} + +/** AntdUI.DatePickerYearMonth:年月(Format=yyyy-MM, Picker=Month) */ +function ctrlDatePickerYearMonth() { + return { + type: 'AntdUI.DatePickerYearMonth', + kind: KIND.YEARMONTH, + group: GROUP, + label: '年月', + useDs: false, + }; +} + +/** AntdUI.DatePickerYear:年份(Format=yyyy, Picker=Year) */ +function ctrlDatePickerYear() { + return { + type: 'AntdUI.DatePickerYear', + kind: KIND.YEAR, + group: GROUP, + label: '年份', + useDs: false, + }; +} + +// ==================== 分发(对应 GetParamControlOfAntdUI 的 if/else 链)==================== + +const ANTD_BUILDERS = { + 'AntdUI.Input': ctrlInput, + 'AntdUI.Select': ctrlSelect, + 'AntdUI.SelectMultiple': ctrlSelectMultiple, + 'AntdUI.DatePicker': ctrlDatePicker, + 'AntdUI.DatePickerYearMonth': ctrlDatePickerYearMonth, + 'AntdUI.DatePickerYear': ctrlDatePickerYear, +}; + +export function buildAntdUIControl(type) { + const builder = ANTD_BUILDERS[String(type || '').trim()]; + return builder ? builder() : null; +} + +export const ANTD_CONTROL_DEFS = Object.values(ANTD_BUILDERS).map((b) => b()); +export { GROUP as ANTD_GROUP }; diff --git a/web/src/utils/gridreport/controls/f9Controls.js b/web/src/utils/gridreport/controls/f9Controls.js new file mode 100644 index 0000000..be35748 --- /dev/null +++ b/web/src/utils/gridreport/controls/f9Controls.js @@ -0,0 +1,441 @@ +/** + * F9 业务控件(对应 Page_ReportQueryTool.GetParamControlOfF9) + * + * 与 WinForms 端同构: + * 1. 注入数据源(ApiClient / RegistryService / GlobalUIHelper) + * 2. 一个 ControlType 一个分支函数(创建控件 + 注册取值函数) + * 3. 末尾统一分发(F9_BUILDERS) + * + * Web 端映射: + * AntdUI.Select 类(有接口列表) → kind=select/multiselect,loader 拉选项 + * ShowModalPage(Page_XXX_Select) 类 → kind=picker + page(选择页)或 search(内建远程搜索弹窗) + * Web 端无对应接口的 → 降级为文本输入,并在 hint 中说明 + */ +import request from '../../request'; +import { KIND, apiOptions, registryOptions, registryInt, pickRows } from './registry'; +import { depOptions, DEP_SCOPE, DUTY } from '../../departments'; + +/** 分组名(编辑器下拉分组) */ +export const F9_GROUP = { + GYZK: 'F9 工艺庄口', + DEP: 'F9 部门 / 职工', + WZ: 'F9 物资 / 原料', + PROD: 'F9 生产 / 质量', + WMS: 'F9 仓储 / 出入库', +}; + +// ==================== 数据源(对应构造函数注入的 ApiClient)==================== + +/** + * 部门下拉(对应 F9GlobalUIHelper 的各 Dep_Select_GenarateXxx) + * 不再一把全量:按口径取数后按 ParentId 建树(树形下拉),取值 = 部门 Id + */ +const depLoader = (scope) => () => depOptions(scope); + +/** 职工(Page_Employee_Select_Simple) */ +async function searchEmployee(keyword) { + const res = await request.get('/sys/employee', { params: { like: keyword || '', pageSize: 20 } }); + return pickRows(res?.data); +} + +/** 原料庄口(Page_YuanliaozhuangkouPage_Select) */ +async function searchYlzk(keyword) { + const res = await request.get('/sys/yuanliaozhuangkou', { + params: { like: keyword || '', pageIndex: 1, pageSize: 20 }, + }); + return pickRows(res?.data); +} + +/** 物资目录(Page_WuziMulu_Select) */ +async function searchWuzi(keyword) { + const res = await request.get('/sys/wuzimulu/options', { params: { keyword: keyword || '', limit: 200 } }); + return pickRows(res?.data); +} + +const EMPLOYEE_COLUMNS = [ + { prop: 'code', label: '工号', width: '110' }, + { prop: 'name', label: '姓名', width: '100' }, + { prop: 'depName', label: '部门', width: '140' }, + { prop: 'gangwei', label: '岗位', width: '120' }, +]; + +const WUZI_COLUMNS = [ + { prop: 'code', label: '物资编号', width: '140' }, + { prop: 'name', label: '物资名称', width: '200' }, + { prop: 'groupName', label: '分组', width: '140' }, +]; + +const YLZK_COLUMNS = [ + { prop: 'code', label: '庄口编号', width: '140' }, + { prop: 'name', label: '庄口名称', width: '180' }, + { prop: 'chandi', label: '产地', width: '110' }, + { prop: 'jibie', label: '季别', width: '90' }, +]; + +/** 工艺庄口取值字段(CraftZhuangKouDto) */ +const GYZK_VALUE_FIELD = 'id'; +const GYZK_LABEL_FIELD = 'productionDisplayName'; + +// ==================== 各控件定义(一个 type 一个函数)==================== + +/** F9-GYZK-SELECT:工艺庄口 → 打开【工艺庄口选择页】,取值 Id */ +function ctrlGyzkSelect() { + return { + type: 'F9-GYZK-SELECT', + kind: KIND.PICKER, + group: F9_GROUP.GYZK, + label: '工艺庄口选择', + page: 'CraftZhuangKou', // 对应 Page_CraftZhuangKou_SelectV2 + valueField: GYZK_VALUE_FIELD, + labelField: GYZK_LABEL_FIELD, + hint: '点击「选择」打开工艺庄口选择页,取值为庄口 Id', + }; +} + +/** F9-GYZK-SELECT-MUTIPLE:工艺庄口多选,取值 "1,2,3"(对齐 string.Join(",", ids)) */ +function ctrlGyzkSelectMultiple() { + return { + type: 'F9-GYZK-SELECT-MUTIPLE', + kind: KIND.PICKER_MULTIPLE, + group: F9_GROUP.GYZK, + label: '工艺庄口选择(多选)', + page: 'CraftZhuangKou', + valueField: GYZK_VALUE_FIELD, + labelField: GYZK_LABEL_FIELD, + hint: '取值为逗号分隔的庄口 Id', + }; +} + +/** F9-YEXIAN-SOURCE-SELECT:野蚕来源,取值 Text(默认"全部") */ +function ctrlYexianSource() { + return { + type: 'F9-YEXIAN-SOURCE-SELECT', + kind: KIND.SELECT, + group: F9_GROUP.PROD, + label: '野蚕来源', + options: [{ key: '全部', value: '全部' }, { key: '现场', value: '现场' }, { key: '检验室', value: '检验室' }], + }; +} + +/** 部门控件通用定义 */ +function ctrlDep(type, label, scope, { defaultFromUser = false, hint = '' } = {}) { + return { + type, + kind: KIND.SELECT, + group: F9_GROUP.DEP, + label, + loader: depLoader(scope), + tree: true, // 树形下拉(对应 AntdUI.Select 带 Sub 的层级项) + defaultFromUser, // 默认选中登录用户所在部门(对应 Dep_Select_SelectDep) + hint: hint || '取值为部门 Id,按 ParentId 层级展示', + }; +} + +/** F9-DEP-QS-SELECT:前缫车间(GetDepartmentByDutyTypeWithoutRightQuery([前缫])) */ +function ctrlDepQianshan() { + return ctrlDep('F9-DEP-QS-SELECT', '前缫车间选择', { duty: [DUTY.前缫] }, + { hint: '按 DutyType=前缫 过滤(前缫车间 + 前缫一~四车间)' }); +} + +/** F9-DEP-ALL-SELECT:全部部门(树) */ +function ctrlDepAll() { + return ctrlDep('F9-DEP-ALL-SELECT', '部门选择(全部)', DEP_SCOPE.ALL); +} + +/** F9-DEPARTMENT_SIYE:丝业部门(丝业中心 + 全部子孙) */ +function ctrlDepSiye() { + return ctrlDep('F9-DEPARTMENT_SIYE', '丝业部门选择', DEP_SCOPE.SIYE); +} + +/** F9-DEP-EMPLOYEE-SELECT:部门(职工口径,默认登录用户部门) */ +function ctrlDepEmployee() { + return ctrlDep('F9-DEP-EMPLOYEE-SELECT', '部门选择(职工口径)', DEP_SCOPE.EMPLOYEE, { defaultFromUser: true }); +} + +/** F9-DEP-XZ-SELECT:部门(薪资口径,默认登录用户部门) */ +function ctrlDepXinzi() { + return ctrlDep('F9-DEP-XZ-SELECT', '部门选择(薪资口径)', DEP_SCOPE.XINZI, { defaultFromUser: true }); +} + +/** F9-DEPARTMENT_KAOQIN:部门(考勤口径,默认登录用户部门) */ +function ctrlDepKaoqin() { + return ctrlDep('F9-DEPARTMENT_KAOQIN', '部门选择(考勤口径)', DEP_SCOPE.KAOQIN, { defaultFromUser: true }); +} + +/** F9-EMPLOYEE-SELECT:职工选择页,取值职工 Id */ +function ctrlEmployeeSelect() { + return { + type: 'F9-EMPLOYEE-SELECT', + kind: KIND.PICKER, + group: F9_GROUP.DEP, + label: '职工选择', + search: searchEmployee, + columns: EMPLOYEE_COLUMNS, + valueField: 'id', + labelField: 'name', + hint: '取值为职工 Id', + }; +} + +/** F9-EMPLOYEE-WORKSTATUS:职工工作状态(枚举名 + 全部) */ +function ctrlEmployeeWorkStatus() { + return { + type: 'F9-EMPLOYEE-WORKSTATUS', + kind: KIND.SELECT, + group: F9_GROUP.DEP, + label: '职工工作状态', + options: [{ key: '全部', value: '全部' }, { key: '在职', value: '在职' }, { key: '离职', value: '离职' }], + }; +} + +/** F9-EMPLOYEE-CHANGE_TYPE_SELECT:职工变更类型(Employee_ChangeStatus_Enum) */ +function ctrlEmployeeChangeType() { + return { + type: 'F9-EMPLOYEE-CHANGE_TYPE_SELECT', + kind: KIND.SELECT, + group: F9_GROUP.DEP, + label: '职工变更类型', + options: ['新入职', '调岗', '离职', '再入职', '转正'].map((x) => ({ key: x, value: x })), + }; +} + +/** F9-Employee-isXuegong:学工 / 老工(默认老工,对应 SelectedIndex = 1) */ +function ctrlIsXuegong() { + return { + type: 'F9-Employee-isXuegong', + kind: KIND.SELECT, + group: F9_GROUP.DEP, + label: '学工 / 老工', + options: [{ key: '学工', value: '学工' }, { key: '老工', value: '老工' }], + defaultText: '老工', + }; +} + +/** F9-WZ-*-SELECT:物资目录选择页(按业务类型区分,Web 端统一取物资目录) */ +function ctrlWuziMulu(type, label) { + return { + type, + kind: KIND.PICKER, + group: F9_GROUP.WZ, + label, + search: searchWuzi, + columns: WUZI_COLUMNS, + valueField: 'id', + labelField: 'name', + hint: '取值为物资目录 Id(Web 端无业务类型维度,统一检索物资目录)', + }; +} + +/** F9-YLZK-SELECT:原料庄口选择页 */ +function ctrlYlzkSelect() { + return { + type: 'F9-YLZK-SELECT', + kind: KIND.PICKER, + group: F9_GROUP.WZ, + label: '原料庄口选择', + search: searchYlzk, + columns: YLZK_COLUMNS, + valueField: 'id', + labelField: 'name', + }; +} + +/** F9-WMS-YL-CD-SELECT:产地(注册表 基础数据\原料庄口) */ +function ctrlYlChandi() { + return { + type: 'F9-WMS-YL-CD-SELECT', + kind: KIND.SELECT, + group: F9_GROUP.WZ, + label: '原料产地', + loader: () => registryOptions('基础数据\\原料庄口', '产地'), + }; +} + +/** F9-WMS-YL-JIBIE-SELECT:季别(注册表) */ +function ctrlYlJibie() { + return { + type: 'F9-WMS-YL-JIBIE-SELECT', + kind: KIND.SELECT, + group: F9_GROUP.WZ, + label: '原料季别', + loader: () => registryOptions('基础数据\\原料庄口', '季别'), + }; +} + +/** F9-WMS-YL-CANPINZHONG:蚕品种(注册表) */ +function ctrlYlCanpinzhong() { + return { + type: 'F9-WMS-YL-CANPINZHONG', + kind: KIND.SELECT, + group: F9_GROUP.WZ, + label: '蚕品种', + loader: () => registryOptions('基础数据\\原料庄口', '蚕品种'), + }; +} + +/** F9-WMS-CRKTYPE:入库 / 出库 */ +function ctrlCrkType() { + return { + type: 'F9-WMS-CRKTYPE', + kind: KIND.SELECT, + group: F9_GROUP.WMS, + label: '入库 / 出库类型', + options: [{ key: '入库', value: '入库' }, { key: '出库', value: '出库' }], + }; +} + +/** F9-WMS-SS-PICI-KCSTATUS:生丝批次库存状态(多选,Web 端无枚举 → Ds_Static) */ +function ctrlSsPiciKcStatus() { + return { + type: 'F9-WMS-SS-PICI-KCSTATUS', + kind: KIND.MULTISELECT, + group: F9_GROUP.WMS, + label: '生丝批次库存状态(多选)', + useDs: true, + hint: 'Web 端无 SSPiciKucunStatusEnum,请用 Ds_Static 配置选项', + }; +} + +/** F9-WMS-SS-PICI-SSTYPE:生丝批次代码类型(Web 端无枚举 → Ds_Static) */ +function ctrlSsPiciSsType() { + return { + type: 'F9-WMS-SS-PICI-SSTYPE', + kind: KIND.SELECT, + group: F9_GROUP.WMS, + label: '生丝批次类型', + useDs: true, + hint: 'Web 端无 SSPiciCodeTypeEnum,请用 Ds_Static 配置选项', + }; +} + +/** F9-WMS-YL-RK-TYPE-SELECT:原料入库类型(Web 端无枚举 → Ds_Static) */ +function ctrlYlRkType() { + return { + type: 'F9-WMS-YL-RK-TYPE-SELECT', + kind: KIND.SELECT, + group: F9_GROUP.WMS, + label: '原料入库类型', + useDs: true, + hint: 'Web 端无 YLRk_Type_Enum,请用 Ds_Static 配置选项', + }; +} + +/** F9-WMS-YL-CK-TYPE-SELECT:原料出库类型(Web 端无枚举 → Ds_Static) */ +function ctrlYlCkType() { + return { + type: 'F9-WMS-YL-CK-TYPE-SELECT', + kind: KIND.SELECT, + group: F9_GROUP.WMS, + label: '原料出库类型', + useDs: true, + hint: 'Web 端无 YLCk_Type_Enum,请用 Ds_Static 配置选项', + }; +} + +/** F9-GUIGE-SELECT:规格(Web 端取注册表,未配置则用 Ds_Static) */ +function ctrlGuige() { + return { + type: 'F9-GUIGE-SELECT', + kind: KIND.SELECT, + group: F9_GROUP.PROD, + label: '生丝规格', + useDs: true, + loader: () => registryOptions('基础数据\\生丝定级指标', '规格'), + hint: '优先取注册表[基础数据\\生丝定级指标]规格,未配置时用 Ds_Static', + }; +} + +/** F9-PROD-QC-YLXY-TYPE:试缫原料小样类型(Web 端无枚举 → Ds_Static) */ +function ctrlYlxyType() { + return { + type: 'F9-PROD-QC-YLXY-TYPE', + kind: KIND.SELECT, + group: F9_GROUP.PROD, + label: '试缫原料小样类型', + useDs: true, + hint: 'Web 端无 Prod_Qc_Yjss_Ylxy_Type,请用 Ds_Static 配置选项', + }; +} + +/** F9-PROD-QS-LSCD-YICHANG:粒数测定异常过滤(注册表,默认首项) */ +function ctrlLscdYichang() { + return { + type: 'F9-PROD-QS-LSCD-YICHANG', + kind: KIND.SELECT, + group: F9_GROUP.PROD, + label: '粒数测定异常过滤', + loader: () => registryOptions('生产\\前缫\\粒数测定', '过滤条件'), + }; +} + +/** F9-PROD-QS-ZHJJ-COUNT:煮茧机(注册表数量 → 生成 N 号煮茧机) */ +function ctrlZhujianji() { + return { + type: 'F9-PROD-QS-ZHJJ-COUNT', + kind: KIND.SELECT, + group: F9_GROUP.PROD, + label: '煮茧机选择', + loader: async () => { + const count = await registryInt('生产\\前缫', '煮茧机数量', 0); + return Array.from({ length: Math.max(count, 0) }, (_, i) => { + const text = `${i + 1}号煮茧机`; + return { key: text, value: text }; + }); + }, + }; +} + +/** F9-QC-YJSS-YLXY-SELECT / F9-QC-YJSS-ZHY-SELECT:Web 端无接口 → 降级文本输入 */ +function ctrlYjss(type, label) { + return { + type, + kind: KIND.INPUT, + group: F9_GROUP.PROD, + label, + hint: 'Web 端暂无样茧试缫接口,降级为文本输入(填 Id)', + }; +} + +// ==================== 分发(对应 GetParamControlOfF9 的 if/else 链)==================== + +const F9_BUILDERS = { + 'F9-GYZK-SELECT': ctrlGyzkSelect, + 'F9-GYZK-SELECT-MUTIPLE': ctrlGyzkSelectMultiple, + 'F9-YEXIAN-SOURCE-SELECT': ctrlYexianSource, + 'F9-DEP-QS-SELECT': ctrlDepQianshan, + 'F9-DEP-ALL-SELECT': ctrlDepAll, + 'F9-DEPARTMENT_SIYE': ctrlDepSiye, + 'F9-DEP-EMPLOYEE-SELECT': ctrlDepEmployee, + 'F9-DEP-XZ-SELECT': ctrlDepXinzi, + 'F9-DEPARTMENT_KAOQIN': ctrlDepKaoqin, + 'F9-EMPLOYEE-SELECT': ctrlEmployeeSelect, + 'F9-EMPLOYEE-WORKSTATUS': ctrlEmployeeWorkStatus, + 'F9-EMPLOYEE-CHANGE_TYPE_SELECT': ctrlEmployeeChangeType, + 'F9-Employee-isXuegong': ctrlIsXuegong, + 'F9-WZ-SS-SELECT': () => ctrlWuziMulu('F9-WZ-SS-SELECT', '物资目录(生丝)'), + 'F9-WZ-FP-SELECT': () => ctrlWuziMulu('F9-WZ-FP-SELECT', '物资目录(副产品)'), + 'F9-WZ-ZJP-SELECT': () => ctrlWuziMulu('F9-WZ-ZJP-SELECT', '物资目录(中间品)'), + 'F9-WZ-WJ-SELECT': () => ctrlWuziMulu('F9-WZ-WJ-SELECT', '物资目录(五金)'), + 'F9-YLZK-SELECT': ctrlYlzkSelect, + 'F9-WMS-YL-CD-SELECT': ctrlYlChandi, + 'F9-WMS-YL-JIBIE-SELECT': ctrlYlJibie, + 'F9-WMS-YL-CANPINZHONG': ctrlYlCanpinzhong, + 'F9-WMS-CRKTYPE': ctrlCrkType, + 'F9-WMS-SS-PICI-KCSTATUS': ctrlSsPiciKcStatus, + 'F9-WMS-SS-PICI-SSTYPE': ctrlSsPiciSsType, + 'F9-WMS-YL-RK-TYPE-SELECT': ctrlYlRkType, + 'F9-WMS-YL-CK-TYPE-SELECT': ctrlYlCkType, + 'F9-GUIGE-SELECT': ctrlGuige, + 'F9-PROD-QC-YLXY-TYPE': ctrlYlxyType, + 'F9-PROD-QS-LSCD-YICHANG': ctrlLscdYichang, + 'F9-PROD-QS-ZHJJ-COUNT': ctrlZhujianji, + 'F9-QC-YJSS-YLXY-SELECT': () => ctrlYjss('F9-QC-YJSS-YLXY-SELECT', '样茧试缫原料小样'), + 'F9-QC-YJSS-ZHY-SELECT': () => ctrlYjss('F9-QC-YJSS-ZHY-SELECT', '样茧试缫综合样'), +}; + +export function buildF9Control(type) { + const builder = F9_BUILDERS[String(type || '').trim()]; + return builder ? builder() : null; +} + +export const F9_CONTROL_DEFS = Object.values(F9_BUILDERS).map((b) => b()); diff --git a/web/src/utils/gridreport/controls/index.js b/web/src/utils/gridreport/controls/index.js new file mode 100644 index 0000000..f7ed256 --- /dev/null +++ b/web/src/utils/gridreport/controls/index.js @@ -0,0 +1,83 @@ +/** + * 自定义控件模块入口:导入即注册内置控件。 + * + * 使用方式: + * import { resolveControlDef, groupedControlOptions, loadParamOptions } from '@/utils/gridreport/controls'; + * + * 扩展新控件(无需改动页面): + * import { registerControl, KIND } from '@/utils/gridreport/controls'; + * registerControl({ + * type: 'F9-MY-SELECT', + * kind: KIND.SELECT, + * group: 'F9 自定义', + * label: '我的选择', + * loader: async () => [{ key: '1', value: '选项一' }], + * }); + */ +import request from '../../request'; +import { registerControls } from './registry'; +import { ANTD_CONTROL_DEFS } from './antdControls'; +import { F9_CONTROL_DEFS } from './f9Controls'; + +export * from './registry'; +export { ANTD_CONTROL_DEFS, buildAntdUIControl } from './antdControls'; +export { F9_CONTROL_DEFS, F9_GROUP, buildF9Control } from './f9Controls'; + +// 装配:注册 AntdUI 基础控件 + F9 业务控件(对应 GetParamControl 的两条分支) +registerControls([...ANTD_CONTROL_DEFS, ...F9_CONTROL_DEFS]); + +/** 解析 Ds_Static 为选项数组。支持 "k=v,k2=v2"、"v1,v2"、JSON 数组({key,value} 或字符串) */ +export function parseStaticDs(dsStatic) { + if (dsStatic == null || dsStatic === '') return []; + if (typeof dsStatic !== 'string') dsStatic = JSON.stringify(dsStatic); + const s = dsStatic.trim(); + if (s.startsWith('[')) { + try { + const arr = JSON.parse(s); + return (Array.isArray(arr) ? arr : []).map((it) => + typeof it === 'object' && it !== null + ? { key: String(it.key ?? it.Key ?? it.value ?? ''), value: String(it.value ?? it.Value ?? it.key ?? '') } + : { key: String(it), value: String(it) } + ); + } catch (e) { return []; } + } + return s.split(/[,,]/).map((p) => { + const i = p.indexOf('='); + if (i > 0) return { key: p.slice(0, i).trim(), value: p.slice(i + 1).trim() }; + return { key: p.trim(), value: p.trim() }; + }).filter((x) => x.value !== ''); +} + +/** 拉取 Ds_Service 服务端选项(约定:GET {url} → ApiResult,data 为 [{id,name}] 或字符串数组) */ +export async function fetchServiceDs(dsService) { + if (!dsService) return []; + try { + const res = await request.get(dsService); + const rows = Array.isArray(res?.data) ? res.data : (res?.data?.items || []); + return (Array.isArray(rows) ? rows : []).map((it) => + typeof it === 'object' && it !== null + ? { key: String(it.key ?? it.id ?? it.Id ?? ''), value: String(it.value ?? it.name ?? it.Name ?? '') } + : { key: String(it), value: String(it) } + ); + } catch (e) { + return []; + } +} + +/** + * 计算参数选项中列表:Ds_Static > Ds_Service > 控件内置 loader > 控件静态 options + * @param {Object} prm normalizeParams 输出的归一化参数 + */ +export async function loadParamOptions(prm) { + const cd = prm?.controlDef || {}; + if (prm?.dsStatic) return parseStaticDs(prm.dsStatic); + if (prm?.dsService) return fetchServiceDs(prm.dsService); + if (cd.useDs === false && typeof cd.loader !== 'function') return []; + if (typeof cd.loader === 'function') { + try { + const opts = await cd.loader(prm); + if (opts && opts.length) return opts; + } catch (e) { /* 交由静态选项兜底 */ } + } + return cd.options || []; +} diff --git a/web/src/utils/gridreport/controls/registry.js b/web/src/utils/gridreport/controls/registry.js new file mode 100644 index 0000000..7f6297d --- /dev/null +++ b/web/src/utils/gridreport/controls/registry.js @@ -0,0 +1,203 @@ +/** + * F9 自定义查询控件注册中心(Web 版) + * ---------------------------------------------------------------------------- + * WinForms 端(F9.Common.Report.Pages.Page_ReportQueryTool)的渲染逻辑是: + * + * GetParamControl(ReportParameter prm) + * ├─ ControlType 以 "F9" 开头 → GetParamControlOfF9(...) // 业务选择页:部门/职工/庄口/物资/枚举… + * └─ ControlType 以 "AntdUI" 开头 → GetParamControlOfAntdUI(...) // 基础控件:Input/Select/DatePicker… + * + * 即「按 ControlType 字符串分发 → 创建控件 → 注册取值函数 _paramGetValudFuncs」。 + * + * Web 端同构做法:把「渲染成什么控件 + 选项从哪里来 + 取到的值是什么」抽象成【控件定义】, + * 注册进本中心;渲染统一交给 F9ParamControl.vue。 + * 新增一个自定义控件只需 registerControl({...}) 一次,报表中心查询面板与报表管理编辑器的 + * 「控件类型」下拉会同时生效,无需改动页面代码。 + * + * 控件定义(control definition)字段: + * type 控件类型(与 ReportParameter.ControlType 完全一致) + * kind 渲染种类(KIND.*) + * label 中文名(编辑器下拉显示) + * group 分组(编辑器下拉分组) + * hint 说明(数据源缺失/降级说明等) + * useDs true 表示允许 Ds_Static / Ds_Service 覆盖选项 + * options 静态选项 [{key,value}] + * loader async (prm) => [{key,value}] 动态选项(下拉类) + * search async (keyword) => rows 远程搜索(弹窗选择类) + * columns 弹窗表格列 [{prop,label,width}] + * valueField 弹窗取值字段(默认 id) + * labelField 弹窗显示字段(默认 name) + * multiple 弹窗多选(值以逗号拼接) + */ +import request from '../../request'; + +const registry = new Map(); + +/** 渲染种类,与 F9ParamControl.vue 的分支一一对应 */ +export const KIND = { + INPUT: 'input', // 文本输入(AntdUI.Input) + SELECT: 'select', // 单选下拉(AntdUI.Select) + MULTISELECT: 'multiselect', // 多选下拉(值为 "a,b,c",对应 F9 的 string.Join(",", ids)) + DATE: 'date', // 日期(AntdUI.DatePicker) + YEARMONTH: 'yearmonth', // 年月(AntdUI.DatePickerYearMonth) + YEAR: 'year', // 年份(AntdUI.DatePickerYear) + PICKER: 'picker', // 弹窗选择,取值为 Id(对应 F9 的 ShowModalPage 选择页) + PICKER_MULTIPLE: 'picker-multiple', // 弹窗多选,取值为 "1,2,3" +}; + +// ==================== 注册 / 查询 ==================== + +export function registerControl(def) { + if (!def || !def.type) return null; + const item = { kind: KIND.INPUT, group: '其他', label: def.type, ...def }; + item.type = String(item.type).trim(); + registry.set(item.type, item); + return item; +} + +export function registerControls(list) { + (list || []).forEach(registerControl); + return list; +} + +export function getControlDef(type) { + return registry.get(String(type || '').trim()) || null; +} + +export function hasControl(type) { + return registry.has(String(type || '').trim()); +} + +export function allControls() { + return Array.from(registry.values()); +} + +/** 报表管理编辑器的「控件类型」下拉(按 group 分组) */ +export function groupedControlOptions() { + const map = new Map(); + allControls().forEach((c) => { + if (!map.has(c.group)) map.set(c.group, []); + map.get(c.group).push({ value: c.type, label: c.label || c.type, hint: c.hint || '' }); + }); + return Array.from(map, ([group, items]) => ({ group, items })); +} + +/** 未注册类型的兜底定义(保持旧行为:F9-* 按 Ds 渲染下拉,其余渲染输入框) */ +export function fallbackDef(type) { + const t = String(type || '').trim(); + if (t.startsWith('AntdUI.DatePicker')) { + return { type: t, kind: KIND.DATE, group: 'AntdUI 基础控件', label: t, useDs: false }; + } + if (t.startsWith('AntdUI.Select')) { + const multi = /multiple/i.test(t); + return { + type: t, + kind: multi ? KIND.MULTISELECT : KIND.SELECT, + group: 'AntdUI 基础控件', + label: t, + useDs: true, + }; + } + if (t.startsWith('F9')) { + return { + type: t, + kind: KIND.SELECT, + group: 'F9 未注册控件', + label: t, + useDs: true, + hint: '未注册的 F9 控件:按 Ds_Static / Ds_Service 渲染下拉,否则降级为文本输入', + }; + } + return { type: t || 'AntdUI.Input', kind: KIND.INPUT, group: '其他', label: t || '文本输入', useDs: true }; +} + +/** 查注册表,未命中则兜底 */ +export function resolveControlDef(type) { + return getControlDef(type) || fallbackDef(type); +} + +// ==================== 数据取值工具(内置控件与页面复用) ==================== + +/** 把接口返回的行数组统一成选项 [{key,value,raw}] */ +export function toOptions(list, valueField = 'id', labelField = 'name') { + return (list || []) + .map((it) => { + if (it == null) return null; + if (typeof it !== 'object') return { key: String(it), value: String(it) }; + const key = it[valueField] ?? it.value ?? it.key ?? it.Value ?? it.Key ?? it.id ?? it.Id; + const val = it[labelField] ?? it.label ?? it.name ?? it.text ?? it.Name ?? it.Label ?? it.Value; + return { + key: key == null ? '' : String(key), + value: val == null ? '' : String(val), + raw: it, + }; + }) + .filter(Boolean); +} + +/** 弹窗选择行的显示文本(兼容多套字段命名) */ +export function rowLabel(row, labelField = 'name') { + if (row == null) return ''; + if (typeof row !== 'object') return String(row); + const v = row[labelField] + ?? row.productionDisplayName ?? row.zhuangKouName ?? row.zhuangkouName + ?? row.name ?? row.Name ?? row.zkname ?? row.code ?? row.Code; + return v == null ? '' : String(v); +} + +/** 从 ApiResult / PageResult / 裸数组 中取出数据行 */ +export function pickRows(payload) { + if (Array.isArray(payload)) return payload; + if (!payload) return []; + if (Array.isArray(payload.items)) return payload.items; + if (Array.isArray(payload.list)) return payload.list; + if (Array.isArray(payload.data)) return payload.data; + return []; +} + +/** 拉取下拉选项:GET url → 行数组 → 选项 */ +export async function apiOptions(url, { params, valueField = 'id', labelField = 'name' } = {}) { + const res = await request.get(url, { params }); + return toOptions(pickRows(res?.data), valueField, labelField); +} + +/** 注册表取值(对应 WinForms 端 IRegistryService.GetRegistry(category, key)) */ +export async function registryOptions(category, keyName) { + try { + const res = await request.get('/sys/registry/by-key', { params: { category, keyName } }); + const d = res?.data; + const raw = d?.value ?? d?.Value ?? d; + return parseRegistryValue(raw); + } catch (e) { + return []; + } +} + +/** 注册表数值取整数(如「煮茧机数量」) */ +export async function registryInt(category, keyName, def = 0) { + const opts = await registryOptions(category, keyName); + const n = Number(opts?.[0]?.value); + return Number.isFinite(n) ? n : def; +} + +/** 注册表文本值 → 选项数组(支持 JSON 数组 / 逗号分隔 / 分号分隔) */ +export function parseRegistryValue(raw) { + if (raw == null) return []; + if (Array.isArray(raw)) { + return raw.map((x) => + typeof x === 'object' && x !== null + ? { key: String(x.key ?? x.id ?? ''), value: String(x.value ?? x.name ?? x.text ?? '') } + : { key: String(x), value: String(x) } + ); + } + const s = String(raw).trim(); + if (!s) return []; + if (s.startsWith('[') || s.startsWith('{')) { + try { return parseRegistryValue(JSON.parse(s)); } catch (e) { return []; } + } + return s + .split(/[,,;;\n]/) + .map((x) => x.trim()) + .filter(Boolean) + .map((x) => ({ key: x, value: x })); +} diff --git a/web/src/utils/gridreport/grwebapp.js b/web/src/utils/gridreport/grwebapp.js new file mode 100644 index 0000000..b45834d --- /dev/null +++ b/web/src/utils/gridreport/grwebapp.js @@ -0,0 +1,504 @@ +//变量 _grwebapp_url 指定WEB报表客户端安装程序的下载URL,当通过WebSocket通讯不成功时,提示用户用此URL下载程序进行安装。 +//或用于自动更新WEB报表客户端时下载新版本,开发者应将 _grwebapp_url 改为自己服务器的URL,方便用户从可访问的WEB服务器下载 +//变量 _grwebapp_version 指定自动更新时,客户端程序需要的版本号,如果小于此版本号,则自动进行更新 +var _grwebapp_websocket = null, + _grwr = "_WR", + _grwebapp_url = "http://www.rubylong.cn/download/v7/gridreport7-webapp.exe", + _grwebapp_version = "7.0.0.0", + webapp_onmessage; //如果需要响应执行任务后的消息响应,应该写一个名称为 webapp_onmessage 的 function + +function webapp_url_method_valid(url, method) { + if (!method) { + if (typeof url == "object") { + method = url.method; + url = url.url; + } + if (!method) { + method = /.grf|.txt|.xml|.json/.test(url) ? "GET" : "POST"; + } + } + return method; +} + +//此函数用于判断一个变量是否为URL字符串,如果类型为字符串且首个非空白字符不为“<”与“{”即判定为URL +//如果参数是一个object对象,且其具有“url”属性,则其是HTTPParam对象,是一个url参数 +var webapp_is_url = function (p) { + var index = 0, + len = p.length, + ch; + + //对象如果有url属性,判定为url + if (typeof p == "object") { + return !!p.url; + } + + if (typeof p != "string") { + return 0; + } + + //首先找到第一个非空白字符 + while (index < len) { + ch = p[index]; + if (!/\s/g.test(ch)) + break; + index++; + } + + //如果不是xml或json文本串,则判定为url + return (ch != "{") && (ch != "<") && (p.substr(index, 3) != _grwr); +}; + +function webapp_ajax(method, url, callback, cbthis, params) { + var xmlhttp = new XMLHttpRequest(), + headers; + + xmlhttp.onreadystatechange = function () { + if (xmlhttp.readyState == 4 && xmlhttp.status > 0) { + callback.call(cbthis, xmlhttp, xmlhttp.status == 200); + } + } + + xmlhttp.onerror = function () { + callback.call(cbthis, xmlhttp, 0); + } + + if (typeof url == "object") { + headers = url.headers; + if (typeof headers == "string") { + headers = JSON.parse(headers) + } + + if (!method) { + method = url.method; + } + + if (!params) { + params = url.data; + } + + url = url.url; + } + + xmlhttp.open(webapp_url_method_valid(url, method), url, true); //异步请求数据 + + if (headers && typeof headers == "object") { + for (var key in headers) { + xmlhttp.setRequestHeader(key, headers[key]); + } + } + + xmlhttp.send(params); //POST 或 PUT 可以传递参数 +} + +///////////////////////////////////////////////////////////////////////////// +//创建启动WEB报表客户端的URL协议参数 +function _gr_up_href(args) { + return "gr7webapp://" + (args ? encodeURIComponent(JSON.stringify(args)) : ""); +} + +//通过URL协议启动WEB报表客户端程序 +//参数 appUpdate 指定是否自动更新WEB报表客户端程序,根据_grwebapp_version与已经安装的程序版本进行比较,按需进行自动升级 +function webapp_urlprotocol_startup(appUpdate) { + var arg = appUpdate ? { + type: "update", + url: _grwebapp_url, + ver: _grwebapp_version + } : 0; + window.location.href = _gr_up_href(arg); +} + +//如果WEB报表客户端程序未运行,先通过URL协议启动WEB报表客户端程序 +//通知WEB报表客户端程序根据模板与数据的URL,以及生成类型等相关参数进行报表生成, +function webapp_urlprotocol_run(args, report_url, data_url) { + if (report_url) { + args.report = report_url; + } + if (data_url) { + args.data = data_url; + } + + window.location.href = _gr_up_href(args); +} + +///////////////////////////////////////////////////////////////////////////// +//检查WebSocket是否已经创建并通讯连接成功 +function webapp_ws_check(slience) { + if (!slience) { + if (!_grwebapp_websocket) { + alert("WebSocket没有创建,无法发送数据!"); + } + else if (_grwebapp_websocket.readyState != 1) { + alert("WebSocket正在连接中,暂不能发送数据!"); + } + } + return _grwebapp_websocket && (_grwebapp_websocket.readyState === 1); +} + +//创建并通讯连接WebSocket +function webapp_ws_create(onopen) { + //如果已经创建,且连接正常,则不要再次创建 + if (_grwebapp_websocket && _grwebapp_websocket.readyState === 1) { + return; + } + + //判断当前浏览器是否支持WebSocket + if ('WebSocket' in window) { + _grwebapp_websocket = new WebSocket("ws://127.0.0.1:22337"); + + //连接成功建立的回调方法 + if (onopen) { + _grwebapp_websocket.onopen = function () { + //alert("_grwebapp_websocket.onopen"); + onopen(); + } + } + //debugger; + //接收到消息的回调方法 + if (window.webapp_onmessage) { + _grwebapp_websocket.onmessage = function (event) { + window.webapp_onmessage(event); + } + } + + _grwebapp_websocket.onerror = function () { + var children = document.body.children, + childrenLen = children.length, + referNode = childrenLen ? children[0] : null, + newNode = document.createElement("h3"); + + _grwebapp_websocket = null; + + //弹出alert提示信息,可修改为更适合的表述 + //alert("创建WebSocket失败,可能是‘WEB报表客户端程序’在本机没有安装,或在报表网页加载时没有调用‘webapp_urlprotocol_startup’函数进行启动。"); + alert("创建WebSocket失败,可能是WEB报表客户端程序没有启动,或其在本机没有安装,请根据网页顶部的提示进行相应的操作。"); + + //在网页最前面加上提示下载的文字,可修改为更适合的表述与界面形式 + //newNode.innerHTML = '特别提示:点击下载WEB报表客户端程序,下载后双击下载的文件进行安装,安装完成后重新打开当前网页。'; + newNode.innerHTML = '特别提示:尝试启动WEB报表客户端程序。或点击下载WEB报表客户端程序,下载后双击下载的文件进行安装,安装完成后重新打开当前网页。'; + document.body.insertBefore(newNode, referNode); + }; + + //连接关闭的回调方法 + _grwebapp_websocket.onclose = function () { + _grwebapp_websocket = null; + } + + //感觉这部分可以不要 + //监听窗口关闭事件,当窗口关闭时,主动去关闭_grwebapp_websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。 + //window.onbeforeunload = function () { + // if (_grwebapp_websocket) { + // _grwebapp_websocket.close(); + // } + //} + } + else { + alert('当前浏览器不支持HTML5的WebSocket,请选用更新版本的浏览器!') + } +} + +function webapp_build_one(pack, report, report_method, data, data_method, callback_fun, dataParams) { + if (!report) { + alert("在参数中没有定义报表模板的url,报表不能生成!"); + return; + } + + if (data) { + if (webapp_is_url(data)) { + webapp_ajax(data_method, data, function (xmlhttp, success) { + if (success) { + pack.data = xmlhttp.responseText; + callback_fun(); + } + else { + //alert("载入报表数据失败,响应消息:" + xmlhttp.responseText); + window.open(data, "blank"); + } + }, undefined, dataParams); + } + else { + pack.data = (typeof data == "object") ? JSON.stringify(data) : data; + callback_fun(); + } + } + else { + pack.data = ""; + } + + if (webapp_is_url(report)) { + webapp_ajax(report_method, report, function (xmlhttp, success) { + if (success) { + pack.report = xmlhttp.responseText; + callback_fun(); + } + else { + //alert("载入报表模板失败,响应消息:" + xmlhttp.responseText); + window.open(report, "blank"); + } + }); + } + else { + pack.report = (typeof report == "object") ? JSON.stringify(report) : report; + callback_fun(); + } +} + +//将参数通过WebSocket发送给WEB报表客户端,WEB报表客户端执行对应的任务 +//参数说明: +//args: +// 报表生成相关的参数,为一个结构体对象或结构体对象数组。 +// 当参数为数组时,表示是执行批量任务。此时后面的 report 与 data 参数不会被用到。 +// 结构体对象的各个数据项说明请参考帮助中“WEB报表(B/S报表)->WEB报表客户端->启动参数说明”部分 +//report: +// 报表模板文本,报表定义格式必须为JSON格式。此参数可空,为空表示执行非报表生成任务,或WEB报表客户端通过URL获取报表模板。 +//data: +// 报表数据文本,此参数可空,报表无需提供数据,或WEB报表客户端通过URL获取报表数据。 +// +// 报表数据对应的 XML 或 JSON 文本,请参考帮助“WEB报表(B/S报表)->WEB报表数据”中的说明。 +function webapp_ws_run(variant_args, report, data) { + function run_one(args, reportText, dataText) { + var msg; + + if (webapp_ws_check()) { + if (typeof args === "string") { + args = { + type: args + }; + } + msg = JSON.stringify(args); + + if (reportText) { + if (reportText.substr(0, 3) === _grwr) { + msg += reportText.length; + } + msg += reportText; + + if (dataText) { + msg += dataText; + } + } + + _grwebapp_websocket.send(msg); + } + } //end of run_one + + if (!webapp_ws_check(1)) { + //如果WebSocket没有连接,则首先创建并连接WebSocket,并在onopen事件中执行相关的任务 + webapp_ws_create(function () { + webapp_ws_run(variant_args, report, data); + }); + return; + } + + if (Array.isArray(variant_args)) { + variant_args.forEach(function (args) { + run_one(args); + }) + } + else { + run_one(variant_args, report, data); + } +} + +//通过 ajax 方式获取报表模板与报表数据,然后通过 WebSocket 发送相关数据给WEB报表客户端并生成报表 +//参数既可以是单个结构体对象,也可以是结构体对象数组。如果是数组,表示成批生成多个报表。 +//结构体对象的各个数据项说明请参考帮助中“WEB报表(B/S报表)->WEB报表客户端->启动参数说明”部分。 +function webapp_ws_ajax_run(variant_args) { + function run_one(args) { + var report = args.report, + data = args.data, + report_method = args.report_method, + data_method = args.data_method, + dataUrlParams = args.dataUrlParams, + pack = {}; + + function run() { + //只有当report与data都赋值之后才运行,即向WEB报表客户端程序发送数据 + if (pack.report && pack.data !== undefined) { + webapp_ws_run(args, pack.report, pack.data); + } + } + + //因为args需要传递给WEBAPP,report与data的url相关的参数不需要传递,所以将其删除掉 + delete args.report; + delete args.data; + delete args.report_method; + delete args.data_method; + delete args.dataUrlParams; + + webapp_build_one(pack, report, report_method, data, data_method, run, dataUrlParams); + } //end of run_one + + if (!webapp_ws_check(1)) { + //如果WebSocket没有连接,则首先创建并连接WebSocket,并在onopen事件中执行相关的任务 + webapp_ws_create(function () { + webapp_ws_ajax_run(variant_args); + }); + return; + } + + if (Array.isArray(variant_args)) { + variant_args.forEach(function (args) { + run_one(args); + }); + } + else { + run_one(variant_args); + } +} + +//通过 ajax 方式获取多个报表的模板与数据,然后用 WebSocket 将数据一次性发送给WEB报表客户端程序。 +//实现多个报表同时生成在一个任务中(应用GridReport的独立子报表功能实现),达到多个报表同时预览、打印与数据导出 +function webapp_ws_ajax_together(args, reports) { + var reportCount = reports.length, + reportPacks = [], + gettedCount = 0; //指示已经获取到数据的报表个数 + + function ajaxRequestOne(arg, index) { + var report = arg.report, + data = arg.data, + report_method = arg.report_method, + data_method = arg.data_method, + dataUrlParams = arg.dataUrlParams, + pack = reportPacks[index]; + + function try_ws_send() { + var msg, + dataMsg = "", + lengths = []; + + if (pack.report && pack.data !== undefined) { + if (++gettedCount >= reportCount) { //只有当全部报表的模板与数据都已经获取到之后,才会向WEB报表客户端发送数据 + msg = JSON.stringify(args); + + reportPacks.forEach(function (item) { + var report = item.report, + data = item.data, + reportLen = report.length, + reportLenText = ""; + + if (report.substr(0, 3) === _grwr) { + reportLenText += reportLen; + dataMsg += reportLenText; + reportLen += reportLenText.length; + } + dataMsg += report; + + if (data) { + dataMsg += data; + } + + lengths.push(reportLen + data.length); + }); + + msg += JSON.stringify(lengths); + msg += dataMsg; + + _grwebapp_websocket.send(msg); + } + } + } + + webapp_build_one(pack, report, report_method, data, data_method, try_ws_send, dataUrlParams); + } //end of ajaxRequestOne + + if (!webapp_ws_check(1)) { + //如果WebSocket没有连接,则首先创建并连接WebSocket,并在onopen事件中执行相关的任务 + webapp_ws_create(function () { + webapp_ws_ajax_together(args, reports); + }); + return; + } + + reports.forEach(function (report, index) { + reportPacks.push({}); + ajaxRequestOne(report, index); + }); +} + +///////////////////////////////////////////////////////////////////////////// +//通过WebSocket向WEB报表客户端发送枚举出当前电脑的所有打印机信息的指令 +//在网页的 webapp_onmessage 函数中接收WEB报表客户端返回的结果,具体请参考例子。 +function webapp_ws_fun_Printers() { + webapp_ws_run({ + type: "fun", + fun: "Printers" + }); +} + +//通过WebSocket向WEB报表客户端发送枚举出当前电脑的指定打印机的全部可用纸张的指令 +//在网页的 webapp_onmessage 函数中接收WEB报表客户端返回的结果,具体请参考例子 +function webapp_ws_fun_PrinterPapers(printer) { + webapp_ws_run({ + type: "fun", + fun: "PrinterPapers", + printer: printer + }); +} + +//通过WebSocket向WEB报表客户端发送停止当前报表预览并关闭预览窗口的指令 +function webapp_ws_fun_StopPreview() { + webapp_ws_run({ + type: "fun", + fun: "StopPreview" + }); +} + +///////////////////////////////////////////////////////////////////////////// +//通过WebSocket向WEB报表客户端发送消息进行自动更新检查处理 +function webapp_ws_autoupdate() { + webapp_ws_run({ + type: "update", + url: _grwebapp_url, + ver: _grwebapp_version + }); +} + +///////////////////////////////////////////////////////////////////////////// +//为URL追加一个名为id的随机数参数,用于防止浏览器缓存。 +//报表模板重新设计后,因为浏览器缓存而让报表生成不能反映出新修改的设计结果,URL后追加一个随机数参数可以避免这样的问题 +//参数url必须是静态的URL,其后本身无任何参数 +//如果模板几乎不怎么修改,可以去掉对本函数的调用 +function urlAddRandomNo(url) { + // F9 改动:URL 上已带查询串(如 ?access_token=xxx)时用 & 连接,避免把随机数并进 token 值里 + var sep = url.indexOf("?") >= 0 ? "&" : "?"; + return url + sep + "id=" + Math.floor(Math.random() * 10000); +} + +//根据当前网页URL获取到当前WEB服务器的根URL,并记录在 window.rootURL 中 +function gr_extractRootURL() { + var path = window.location.pathname, + index = path.lastIndexOf("/"); + + window.rootURL = window.location.protocol + "//" + window.location.host; + if (index >= 0) { + path = path.substr(0, index); + + //demmo的根目录在当前页面的1级目录之上 + index = path.lastIndexOf("/"); + if (index >= 0) { + path = path.substr(0, index); + } + + window.rootURL += path; + } + window.rootURL += "/"; +} + +// Grid++Report 7 WEB 报表客户端(gr7webapp64.exe)通信库: +// URL 协议 gr7webapp://,WebSocket 端口 22337(GR6 为 grwebapp:// / 22333)。 +// 本文件在 VUE 中使用,故启用 ES Module 导出。 +export { + webapp_url_method_valid, + webapp_urlprotocol_startup, + webapp_ajax, + webapp_ws_ajax_run, + webapp_urlprotocol_run, + urlAddRandomNo, + webapp_onmessage, + webapp_ws_fun_StopPreview, + webapp_ws_fun_PrinterPapers, + webapp_ws_fun_Printers, + webapp_ws_ajax_together, + webapp_ws_autoupdate, + gr_extractRootURL +} \ No newline at end of file diff --git a/web/src/utils/gridreport/loadGrhtml5.js b/web/src/utils/gridreport/loadGrhtml5.js new file mode 100644 index 0000000..ef0e6bc --- /dev/null +++ b/web/src/utils/gridreport/loadGrhtml5.js @@ -0,0 +1,36 @@ +/** + * 动态加载 Grid++Report 7 HTML5 渲染库(grhtml5-7-min.js)。 + * 库文件位于 /grlib/grhtml5-7-min.js(public 目录),加载后提供全局 window.rubylong.grhtml5。 + * + * 说明:Grid++Report 6 的浏览器插件(NPAPI/ActiveX)已被 Chrome / Edge 移除, + * 只有 GR7 的 HTML5 库支持在网页内直接渲染报表,故统一使用 GR7。 + */ +let loadingPromise = null; + +export function loadGrhtml5() { + if (typeof window !== 'undefined' && window.rubylong?.grhtml5) { + return Promise.resolve(window.rubylong.grhtml5); + } + if (loadingPromise) return loadingPromise; + + loadingPromise = new Promise((resolve, reject) => { + const existing = document.querySelector('script[data-grhtml5]'); + if (existing) { + existing.addEventListener('load', () => resolve(window.rubylong?.grhtml5)); + existing.addEventListener('error', reject); + return; + } + const script = document.createElement('script'); + script.src = '/grlib/grhtml5-7-min.js'; + script.async = false; + script.dataset.grhtml5 = '1'; + script.onload = () => { + if (window.rubylong?.grhtml5) resolve(window.rubylong.grhtml5); + else reject(new Error('grhtml5 加载后未挂载到 window.rubylong')); + }; + script.onerror = () => reject(new Error('无法加载 Grid++Report 7 HTML5 库(/grlib/grhtml5-7-min.js)')); + document.head.appendChild(script); + }); + + return loadingPromise; +} diff --git a/web/src/utils/gridreport/reportConfig.js b/web/src/utils/gridreport/reportConfig.js new file mode 100644 index 0000000..44d68e7 --- /dev/null +++ b/web/src/utils/gridreport/reportConfig.js @@ -0,0 +1,93 @@ +/** + * Grid++Report 动态查询控件渲染(对应 F9 Page_ReportQueryTool 的 GetParamControl)。 + * + * 控件解析改为查【自定义控件注册中心】(utils/gridreport/controls): + * - AntdUI.Input / Select / SelectMultiple / DatePicker / DatePickerYearMonth / DatePickerYear + * - F9-* 业务控件(部门、职工、庄口、物资、注册表枚举…)由 controls/builtins.js 提供实现 + * - 未注册的 F9-* 按 Ds_Static / Ds_Service 渲染下拉,否则降级为文本输入 + * 新增自定义控件:registerControl({...}) 即可,无需改动本文件与页面。 + * + * 参数定义(F9 ReportParameter 约定): + * { Name, Label, Prompt, IsRequired, ControlType, Ds_Static, Ds_Service, DefaultValue } + */ +import { + KIND, + resolveControlDef, + parseStaticDs, + fetchServiceDs, + loadParamOptions, + groupedControlOptions, +} from './controls'; + +export { KIND, parseStaticDs, fetchServiceDs, loadParamOptions, groupedControlOptions }; + +/** 解析 F9 ControlType 到可渲染的控件种类(走注册中心) */ +export function resolveControlKind(controlType) { + return resolveControlDef(controlType).kind; +} + +/** 日期类控件默认值 */ +export function dateDefault(kind) { + const now = new Date(); + const pad = (n) => String(n).padStart(2, '0'); + if (kind === KIND.YEAR) return String(now.getFullYear()); + if (kind === KIND.YEARMONTH) return `${now.getFullYear()}-${pad(now.getMonth() + 1)}`; + return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`; +} + +/** 计算参数初始值(DefaultValue 特殊标记:今天/本月/本年/Datetime.now/不限 或字面量) */ +export function paramInitialValue(prm) { + const dv = (prm.DefaultValue ?? prm.defaultValue ?? '').toString().trim(); + const kind = prm.kind || resolveControlKind(prm.ControlType ?? prm.controlType); + // 显式「不限」:日期类不预填今天(用于开始日期 / 结束日期这类范围参数) + if (/^(不限|不填|none|null|empty)$/i.test(dv)) return ''; + if (dv === '' || dv === '默认值') { + if (kind === KIND.DATE || kind === KIND.YEARMONTH || kind === KIND.YEAR) return dateDefault(kind); + if (kind === KIND.SELECT || kind === KIND.MULTISELECT) return null; // 由控件加载选项后取首项 + return ''; + } + if (dv === '今天' || dv === 'today' || /^datetime\.now$/i.test(dv)) return dateDefault(KIND.DATE); + if (dv === '本月' || dv === '当月') return dateDefault(KIND.YEARMONTH); + if (dv === '本年') return dateDefault(KIND.YEAR); + if (kind === KIND.DATE || kind === KIND.YEARMONTH || kind === KIND.YEAR) { + return /^datetime\.now$/i.test(dv) ? dateDefault(kind) : dv; + } + return dv; +} + +/** 参数定义统一为前端友好的结构(附带控件定义 controlDef) */ +export function normalizeParams(parms) { + return (parms || []).map((p) => { + const controlType = p.ControlType ?? p.controlType ?? 'AntdUI.Input'; + const controlDef = resolveControlDef(controlType); + return { + name: p.Name ?? p.name ?? '', + label: p.Label ?? p.label ?? '', + prompt: p.Prompt ?? p.prompt ?? '', + isRequired: !!(p.IsRequired ?? p.isRequired), + controlType, + dsStatic: p.Ds_Static ?? p.ds_Static ?? p.dsStatic ?? null, + dsService: p.Ds_Service ?? p.ds_Service ?? p.dsService ?? null, + defaultValue: p.DefaultValue ?? p.defaultValue ?? '', + kind: controlDef.kind, + controlDef, + defaultText: controlDef.defaultText || '', + }; + }); +} + +/** 解析报表 Config JSON(安全) */ +export function parseReportConfig(configStr) { + const empty = { dsName: '', viewerType: 0, parms: [] }; + if (!configStr) return empty; + try { + const cfg = JSON.parse(configStr); + return { + dsName: cfg.DsName ?? cfg.dsName ?? '', + viewerType: cfg.ViewerType ?? cfg.viewerType ?? 0, + parms: normalizeParams(cfg.Parms ?? cfg.parms ?? []), + }; + } catch (e) { + return empty; + } +} diff --git a/web/src/utils/request.js b/web/src/utils/request.js index 902ee1c..107cae5 100644 --- a/web/src/utils/request.js +++ b/web/src/utils/request.js @@ -57,6 +57,10 @@ async function refreshAccessToken() { request.interceptors.response.use( (res) => { const data = res.data; + // 非 ApiResult 包装的响应(如报表 .grf 模板原文、文件流)直接放行 + if (data === null || typeof data !== 'object' || !('code' in data)) { + return res; + } if (data.code === 200) { return data; } diff --git a/web/src/views/prod/lsjl/index.vue b/web/src/views/prod/lsjl/index.vue index 084727e..cfc38d7 100644 --- a/web/src/views/prod/lsjl/index.vue +++ b/web/src/views/prod/lsjl/index.vue @@ -2,14 +2,19 @@ import { ref, reactive, onMounted, computed } from 'vue'; import request from '../../../utils/request'; import { ElMessage, ElMessageBox } from 'element-plus'; +import CraftZhuangKouSelect from '../../../components/select/CraftZhuangKouSelect.vue'; +import { depOptions, DUTY } from '../../../utils/departments'; const list = ref([]); const total = ref(0); const loading = ref(false); const deptOptions = ref([]); // 前缫车间 -const zhuangkouOptions = ref([]); // 工艺庄口 const guigeOptions = ref([]); // 规格 +// 工艺庄口:打开选择页选择(原下拉改为弹窗选择页,庄口多时下拉不可用) +const zkPickerVisible = ref(false); +const zkName = ref(''); // 选中庄口的显示名 + const query = reactive({ pageIndex: 1, pageSize: 20, date: '', deptId: null, keyword: '' }); // 颜色字典(写死) @@ -40,15 +45,15 @@ async function load() { } } -// 加载前缫车间(名称含"前缫"的部门) +// 加载前缫车间(按 DutyType=前缫 过滤,替代原来 name.includes('前缫') 的脏过滤) async function loadDepts() { - const res = await request.get('/sys/department'); - deptOptions.value = (res.data || []).filter((d) => d.name.includes('前缫')); + const opts = await depOptions({ duty: [DUTY.前缫] }, { flat: true }); + deptOptions.value = opts.map((o) => ({ id: Number(o.key), name: o.value, ...(o.raw || {}) })); } -async function loadZhuangkou() { - const res = await request.get('/craftzhuangkou', { params: { pageIndex: 1, pageSize: 1000 } }); - zhuangkouOptions.value = res.data.items; +function onZhuangkouPicked(row) { + form.gyzkId = row.id; + zkName.value = row.productionDisplayName || row.zhuangKouName || ''; } async function loadGuige() { @@ -83,6 +88,7 @@ function openCreate() { luosiTime: '', yanse: yanseOptions[0] || '', huishu: 0, chetaiStart: 0, chetaiEnd: 0, chetaishu: 0, isbanche: false, hours: 0, guige: guigeOptions.value[0] || '', chesu: 0 }); + zkName.value = ''; dialogVisible.value = true; } @@ -96,6 +102,7 @@ function openEdit(row) { chetaiStart: row.chetaiStart, chetaiEnd: row.chetaiEnd, chetaishu: row.chetaishu, isbanche: row.isbanche, hours: row.hours, guige: row.guige, chesu: row.chesu }); + zkName.value = row.zhuangKouName || ''; dialogVisible.value = true; } @@ -131,7 +138,7 @@ function handlePageChange(p) { load(); } -onMounted(() => { load(); loadDepts(); loadZhuangkou(); loadGuige(); }); +onMounted(() => { load(); loadDepts(); loadGuige(); }); + + + diff --git a/web/src/views/qc/components/QcGrid.vue b/web/src/views/qc/components/QcGrid.vue new file mode 100644 index 0000000..2b33133 --- /dev/null +++ b/web/src/views/qc/components/QcGrid.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/web/src/views/qc/components/SchemaForm.vue b/web/src/views/qc/components/SchemaForm.vue new file mode 100644 index 0000000..5e5605f --- /dev/null +++ b/web/src/views/qc/components/SchemaForm.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/web/src/views/qc/gyzxs/DocDialog.vue b/web/src/views/qc/gyzxs/DocDialog.vue new file mode 100644 index 0000000..d928674 --- /dev/null +++ b/web/src/views/qc/gyzxs/DocDialog.vue @@ -0,0 +1,249 @@ + + + + + diff --git a/web/src/views/qc/gyzxs/PrintDialog.vue b/web/src/views/qc/gyzxs/PrintDialog.vue new file mode 100644 index 0000000..853db79 --- /dev/null +++ b/web/src/views/qc/gyzxs/PrintDialog.vue @@ -0,0 +1,660 @@ + + + + + + + diff --git a/web/src/views/qc/gyzxs/index.vue b/web/src/views/qc/gyzxs/index.vue new file mode 100644 index 0000000..8f4e2c5 --- /dev/null +++ b/web/src/views/qc/gyzxs/index.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/web/src/views/qc/gyzxs/schema.js b/web/src/views/qc/gyzxs/schema.js new file mode 100644 index 0000000..df6bd99 --- /dev/null +++ b/web/src/views/qc/gyzxs/schema.js @@ -0,0 +1,388 @@ +// 工艺执行书 —— 7 张子单据的字段定义(label / key / 控件类型 / 单位) +// type: text 文本(可存 "185±5"、"20~38" 这类区间)| number 数值 | date 日期 | textarea +// 对应实体:backend/F9WebAiTest.Domain/Entities/QC/Gyzxs/ + +export const DOC_META = { + shichan: { label: '试产(试缫工艺单)', needGuige: true }, + fengmian: { label: '封面', needGuige: false }, + xbj: { label: '混剥选茧', needGuige: false }, + ssdy: { label: '试缫与大样', needGuige: true }, + zhujian: { label: '煮茧', needGuige: false }, + zds: { label: '自动缫', needGuige: true }, + hs: { label: '后缫(工艺执行书)', needGuige: true } +}; + +// 报表类型(打印菜单) +export const PRINT_TYPES = [ + { type: 'shichan', label: '试缫工艺单', needGuige: true }, + { type: 'fengmian', label: '封面', needGuige: false }, + { type: 'xbj', label: '混剥选茧工艺执行书', needGuige: false }, + { type: 'ssdy', label: '试缫与大样', needGuige: true }, + { type: 'zhujian', label: '煮茧工艺执行书', needGuige: false }, + { type: 'zds', label: '自动缫车间生产工艺执行书', needGuige: true }, + { type: 'zdsdy', label: '自动缫大样', needGuige: true }, + { type: 'gyzxs', label: '工艺执行书', needGuige: true } +]; + +const n = (key, label, unit = '', span = 8, precision = 2) => ({ key, label, unit, span, type: 'number', precision }); +const t = (key, label, unit = '', span = 8) => ({ key, label, unit, span, type: 'text' }); +const d = (key, label, span = 8) => ({ key, label, span, type: 'date' }); +const area = (key, label) => ({ key, label, type: 'textarea', span: 24 }); + +export const DOC_SCHEMAS = { + // ==================== 封面 ==================== + fengmian: { + header: [ + t('bianHao', '编号', '', 8), + t('guige', '编制规格', '', 8), + n('yuanLiaoZhongLiang', '原料总量', '吨', 8), + n('shengSiZhongLiang', '生产丝量', '吨', 8), + t('shengChanCheJian', '缫制车间', '', 8), + t('faSongBuMen', '发送部门', '', 8), + d('shengChanStartDate', '开始生产日期', 8), + d('shengChanEndDate', '结束生产日期', 8), + area('memo', '备注') + ], + groups: [] + }, + + // ==================== 试产(试缫工艺单) ==================== + shichan: { + header: [ + t('guige', '规格', '', 8), + t('huanZhuangFangShi', '换庄方式', '', 8), + d('scDate', '试缫日期', 8), + t('scCheJian', '生产车间', '', 8), + t('sheJiRen', '设计人', '', 8), + t('shenHeRen', '审核', '', 8) + ], + groups: [ + { + title: '茧质调查', + fields: [ + n('yiJianSiChang', '一茧丝长', 'm'), n('jieShuLv', '解舒率', '%'), n('jieShuSiChang', '解舒丝长', 'm'), + n('jianCengLv', '茧层率', '%'), n('jianSiXianDu', '茧丝纤度', 'D', 8, 4), + n('jieShuGuangZhe', '解舒光折', 'kg'), n('wanDiao', '万吊', '次') + ] + }, + { + title: '自动缫试缫工艺指标', + fields: [ + t('zdsCheSu', '车速', '转'), t('zdsYunXuLiShu', '允许粒数', '粒'), n('zdsSaoZhe', '缫折', 'kg'), + n('zdsZhongXinXianDu', '中心纤度', 'D', 8, 4), t('zdsZhongXinLiFuLv', '中心粒符率', '%'), + t('zdsSaoSiWenDu', '缫丝温度', '℃'), n('zdsJiSuanXianDu', '计算纤度', 'D', 8, 4), + t('zdsYunXuLiFuLv', '允许粒符率', '%'), t('zdsSuoXuWenDu', '索绪温度', '℃'), + n('zdsPingJunLiShu', '平均粒数', '粒'), n('zdsBaiJianYinXu', '白茧引绪', '粒'), + t('zdsLiXuWenDu', '理绪温度', '℃'), n('zdsZhongXinLiShu', '中心粒数', '粒'), + t('zdsGeiJianKouKuanDu', '给茧口宽度', 'mm'), n('zdsLuoSiLongShu', '落丝笼数', '笼'), + n('zdsLongJianLiShu', '笼茧粒数', '粒') + ] + }, + { + title: '试缫煮茧工艺', + fields: [ + t('zjCheSu', '煮茧车速', '转'), t('zjZhengZhuShi1', '蒸煮室温度1', ''), + t('zjZhengZhuShi2', '蒸煮室温度2', ''), t('zjZhengZhuShi3', '蒸煮室温度3', ''), + n('zjLuoXuWai', '外层掉绪', '', 8, 2), n('zjLuoXuZhong', '中层掉绪', '', 8, 2), + n('zjLuoXuNei', '内层掉绪', '', 8, 2), t('zjTiaoZhengBuWenDu', '调整部温度', ''), + t('zjChuKouWenDu', '出口温度', ''), t('zjFuZhuCheJian', '附煮车间', ''), + t('zjFuZhuGyzkName', '附煮庄口', ''), t('piFuRen', '审批', '') + ] + } + ] + }, + + // ==================== 混剥选茧 ==================== + xbj: { + header: [ + t('guige', '缫制规格', '', 8), + t('cheJian', '缫制车间', '', 8), + d('huanZhuangDate', '换庄时间', 8) + ], + groups: [ + { + title: '生产指标', + fields: [ + n('scRiChanLiang', '日产量(包/人)'), n('scBanChanLiang', '班产量(包)'), + t('scShangCheLv', '上车率', '%'), t('scLianTongLv', '联统茧率', '%'), + t('scWuJiLv', '五级茧率', '%'), t('scXiaJiaoLv', '下茧/茧衣率', '%'), + t('scJianYiLv', '茧衣率', '%'), area('scJiShuCuoShi', '技术措施要求') + ] + }, + { + title: '质量指标(交叉率)', + fields: [ + t('qcTongHaoWuJiLv', '统号茧-五级茧率', '%'), t('qcTongHaoXiaJianLv', '统号茧-下茧率', '%'), + t('qcWuJiLianTongLv', '五级茧-联统茧率', '%'), t('qcWuJiXiaJiaoLv', '五级茧-下脚率', '%'), + t('qcXiaJiaoLianTongLv', '下脚茧-联统率', '%'), t('qcXiaJiaoCiJianLv', '下脚茧-次茧率', '%') + ] + }, + { + title: '质量控制指标', + fields: [ + t('qcZbHaoWuZuChanLiang', '好五茧(kg/组/3人)'), t('qcZbLiuJiZuChanLiang', '六级茧(kg/组/3人)'), + t('qcZbMianJianZuChanLiang', '棉茧(kg/组/3人)'), t('qcZbXiaJiaoZuChanLiang', '下脚茧(kg/组/3人)'), + t('qcZbShuangGongZuChanLiang', '双宫茧(kg/组/3人)'), t('qcZbGongJinLiShu', '公斤粒数', '粒'), + t('qcZbBaoGuangLv', '剥光率', '%'), t('qcZbJianYiLv', '茧衣率', '%') + ] + } + ], + tables: [ + { + key: 'hunJian', + title: '(一)混茧工艺', + columns: [ + { key: 'zhuangBie', label: '庄别', type: 'text', width: 'auto' }, + { key: 'zhongLiang', label: '重量(kg)', type: 'number' }, + { key: 'baoShu', label: '总包数', type: 'number' }, + { key: 'biLi', label: '比例(%)', type: 'number' }, + { key: 'hunJianBaoShu', label: '混茧包数', type: 'number' } + ], + recompute: true + } + ] + }, + + // ==================== 试缫与大样 ==================== + ssdy: { + header: [t('guige', '缫制规格', '', 8)], + groups: [ + { + title: '试缫', + fields: [ + t('ssShangCheLv', '上车率', '%'), t('ssLianTongLv', '联统率', '%'), t('ssCiJianLv', '次茧率', '%'), + t('ssCiLiuJiLv', '次六级', '%'), t('ssMianJianLv', '棉茧率', '%'), t('ssXiaJiaoLv', '下脚率', '%'), + t('ssShuangGongLv', '双宫率', '%'), t('ssJianYiLv', '茧衣率', '%'), + n('ssLiJianZhong', '400粒重', '克', 8, 1), n('ssGongJinLiShu', '公斤粒数', '粒', 8, 0) + ] + }, + { + title: '大样', + fields: [ + t('dyShangCheLv', '上车率', '%'), t('dyLianTongLv', '联统率', '%'), t('dyCiJianLv', '次茧率', '%'), + t('dyCiLiuJiLv', '次六级', '%'), t('dyMianJianLv', '棉茧率', '%'), t('dyXiaJiaoLv', '下脚率', '%'), + t('dyShuangGongLv', '双宫率', '%'), t('dyJianYiLv', '茧衣率', '%'), + n('dyLiJianZhong', '400粒重', '克', 8, 1), n('dyGongJinLiShu', '公斤粒数', '粒', 8, 0) + ] + }, + { + title: '签署', + fields: [ + area('jiShuCuoShi', '主要技术措施及要求'), + t('sheJiRen', '设计', '', 8), t('shenHeRen', '审核', '', 8), t('piFuRen', '审批', '', 8) + ] + } + ] + }, + + // ==================== 煮茧 ==================== + zhujian: { + header: [ + t('guige', '缫制规格', '', 8), + d('huanZhuangDate', '换庄时间', 8), + t('huanZhuangFangShi', '接缫方式', '', 8), + t('cheJian', '生产车间', '', 8), + t('zhuJianJi', '煮茧机号', '', 8) + ], + groups: [ + { + title: '庄口原料性能', + fields: [ + n('ylGongJinLiShu', '公斤粒数', '粒', 8, 0), t('ylSuoZhou', '缩绉', ''), + n('ylJianCengLv', '茧层率', '%'), n('ylQingJie', '清洁', ''), n('ylJingDu', '净度', ''), + n('ylLiJianSiChang', '粒茧丝长', ''), n('ylJieShuLv', '解舒率', '%'), + n('ylJieShuSiChang', '解舒丝长', ''), n('ylJianSiXianDu', '茧丝纤度', 'D', 8, 4), + n('ylWanMiDiaoCao', '万米吊糙', '次'), n('ylJieShuGuangZhe', '解舒光折', 'kg') + ] + }, + { + title: '工艺技术指标', + fields: [ + t('gyzbJieShuLv', '实缫解舒'), t('gyzbWanDiao', '实缫万吊'), t('gyzbQingJie', '清洁'), + t('gyzbJingDu', '净度'), t('gyzbSiJiaoRongShiLv', '丝胶溶失率'), t('gyzbZuLuoSiLongShu', '组落丝笼数'), + t('gyzbSaoZhe', '缫折(kg)'), t('gyzbLongJianLiShu', '笼茧粒数'), t('gyzbMeiTongJianLiang', '每桶茧量(g)'), + t('gyzbMeiBaoTongShu', '每包桶数(笼)'), t('gyzbMeiXiaoShiYongJianLong', '每小时用茧笼数'), + t('gyzbTiaoTuLv', '条吐率(%)') + ] + }, + { + title: '工艺指标 —— 真空渗透', + fields: [ + t('zkstZhenKongDu', '真空度'), t('zkstXiShuiWenDu', '吸水温度'), + t('zkstDaZhenKongShiChang', '没进水到最高真空度时长'), t('zkstZuiGaoWeiShiChang', '液面到最高位时长'), + t('zkstGuanShuiZhenKongShiChang', '关水后继续真空时长'), t('zkstChuJianWenDu', '出茧温度'), + t('zkstQuanJiHuiZhuanShiJian', '全机回转时间(分)') + ] + }, + { + title: '签署', + fields: [ + area('jiShuCuoShi', '主要技术措施'), + t('sheJiRen', '设计', '', 8), t('shenHeRen', '审核', '', 8), t('piFuRen', '审批', '', 8) + ] + } + ], + tables: [ + { + key: 'cheJian', + title: '分车间煮茧指标', + columns: [ + { key: 'cheJian', label: '车间', type: 'text' }, + { key: 'diWenShenTou', label: '低温渗透', type: 'text' }, + { key: 'zhengZhuShi1', label: '蒸煮室1', type: 'text' }, + { key: 'zhengZhuShi2', label: '蒸煮室2', type: 'text' }, + { key: 'zhengZhuShi3', label: '蒸煮室3', type: 'text' }, + { key: 'tiaoZhengBu1', label: '调整部1', type: 'text' }, + { key: 'tiaoZhengBu2', label: '调整部2', type: 'text' }, + { key: 'tiaoZhengBu3', label: '调整部3', type: 'text' }, + { key: 'tiaoZhengBu4', label: '调整部4', type: 'text' }, + { key: 'tiaoZhengBu5', label: '调整部5', type: 'text' }, + { key: 'chuJianWenDu', label: '出茧温度', type: 'text' }, + { key: 'huiZhuanShiJian', label: '全机回转时间', type: 'text' } + ] + }, + { + key: 'fuZhu', + title: '副煮车间结果', + columns: [ + { key: 'cheJian', label: '附煮车间', type: 'text' }, + { key: 'jieShuLv', label: '解舒', type: 'text' }, + { key: 'wanDiao', label: '万吊', type: 'text' }, + { key: 'saoZhe', label: '缫折/kg', type: 'text' }, + { key: 'wenDu1', label: '温度1', type: 'text' }, + { key: 'wenDu2', label: '温度2', type: 'text' }, + { key: 'wenDu3', label: '温度3', type: 'text' }, + { key: 'wenDu4', label: '温度4', type: 'text' }, + { key: 'wenDu5', label: '温度5', type: 'text' } + ] + } + ] + }, + + // ==================== 自动缫 ==================== + zds: { + header: [ + t('guige', '生丝规格', '', 8), + d('huanZhuangDate', '接缫日期', 8), + t('huanZhuangFangShi', '换庄方式', '', 8), + t('cheJian', '生产车间', '', 8), + n('yuSaoTianShu', '预缫天数', '天', 8, 0), + n('yuSaoSiLiang', '预缫丝量', '吨', 8) + ], + groups: [ + { + title: '茧质概况', + fields: [ + n('zgYiJianSiChang', '一茧丝长', '米'), n('zgJieShuSiChang', '解舒丝长', ''), + n('zgJieShuLv', '解舒率', '%'), n('zgJianSiXianDu', '茧丝纤度', 'D', 8, 4), + n('zgJieShuGuangZhe', '解舒光折', 'kg'), n('zgWanMiDiaoCao', '万米吊糙', '次'), + n('zgQingJie', '清洁', '分'), n('zgJingDu', '洁净', '分'), n('zgJianCengLv', '茧层率', '%'), + n('zgXianDuJunFangCha', '茧丝纤度均方差', '', 8, 3), n('zgWaiSanBaiHui', '外三百回纤度', ''), n('zgZhongSanBaiHui', '中三百回纤度', ''), + n('zgNeiSanBaiHui', '内三百回纤度', ''), n('zgZuiDaKaiCha', '最大开差', ''), n('zgNeiWaiKaiCha', '内外开差', ''), + n('zgBaiSeJianDanXian', '白色茧单纤', ''), n('zgHuiSeJianDanXian', '灰色茧单纤', ''), n('zgHongSeJianDanXian', '红色茧单纤', ''), + n('zgLuoXuWai', '落绪分布-外', ''), n('zgLuoXuZhong', '落绪分布-中', ''), n('zgLuoXuNei', '落绪分布-内', ''), + n('zgPingJunJianFu', '平均茧幅', 'mm'), n('zgJianFuZhengQiLv', '茧幅整齐率', '%'), + t('zgJianSiBaiHuiXianDu', '茧丝百回纤度(明细)', '', 24) + ] + }, + { + title: '生产指标', + fields: [ + t('sczbPinWei', '品味'), t('sczbSaoZhe', '缫折(kg)'), t('sczbTaiChan', '台产'), + t('sczbPianCha', '偏差'), t('sczbZongCha', '总差'), t('sczbQingJie', '清洁'), + t('sczbJingJie', '洁净'), t('sczbYunEr', '匀Ⅱ'), t('sczbZhengPinLv', '生产正品率') + ] + }, + { + title: '工艺技术指标', + fields: [ + t('gyzbCheSu', '车速', '转/分'), t('gyzbTianXuCiShu', '添绪次数', '次/分'), + t('gyzbYunZhuanLv', '运转率', '%'), t('gyzbZhongXinXianDu', '中心纤度', 'D'), + t('gyzbSheJiDanXianDu', '设计单纤度', 'D'), t('gyzbPingJunLiShu', '平均粒数', '粒'), + t('gyzbZhongXinLiShu', '中心粒数', '粒'), t('gyzbYunXuLiShu', '允许粒数', '粒'), + t('gyzbYinXu', '引绪', '粒'), t('gyzbZhongXinLiFuLv', '中心粒符率', ''), + t('gyzbYunXuLiFuLv', '允许粒符率', ''), t('gyzbGeiJianKouKuanDu', '给茧口宽度', ''), + t('gyzbTiaoTuLv', '条吐率', '%'), t('gyzbSiQiaoChangDu', '丝鞘长度', 'cm'), + t('gyzbCheTouQiYa', '车头汽压', 'MP'), t('gyzbSuoXuWenDu', '索绪温度', '℃'), + t('gyzbLiXuWenDu', '理绪温度', '℃'), t('gyzbPianXinPanJianLiang', '偏心盘茧量', ''), + t('gyzbSaoTangWenDu', '缫汤温度', '℃'), t('gyzbJiaJianKouJianLiang', '加茧口茧量', '粒'), + t('gyzbWuXuLv', '无绪率', '%'), t('gyzbPengCaoJianLv', '蓬糙茧率', '%'), + t('gyzbJiaJianJunYunLv', '加茧均匀率', '%'), t('gyzbZhengQueTianXuLv', '正确添绪率', '%'), + t('gyzbTongJianLiang', '桶茧量', 'g'), t('gyzbLuoSiLongShu', '落丝笼数', '') + ] + }, + { + title: '大样(表二)', + fields: [ + t('dyzl.pianCha', '偏差'), t('dyzl.qingJie', '清洁'), t('dyzl.jingDu', '净度'), + t('dyzl.jieShuLv', '解舒率'), t('dyzl.wanMiDiaoCao', '万米吊糙'), t('dyzl.saoZhe', '缫折'), + area('dyzl.jiShuCuoShi', '技术措施及要求') + ], + entity: 'dyzl' + }, + { + title: '签署', + fields: [ + t('sheJiRen', '设计', '', 8), t('shenHeRen', '审核', '', 8), t('piFuRen', '审批', '', 8) + ] + } + ] + }, + + // ==================== 后缫(工艺执行书) ==================== + hs: { + header: [ + t('guige', '生丝规格', '', 8), + d('zhiXingDate', '执行时间', 8), + t('qianSaoCheJian', '前缫生产车间', '', 8), + n('yuSaoTianShu', '预缫天数', '天', 8, 0), + t('zhengLiGuiGe', '整理规格', '', 8) + ], + groups: [ + { + title: '生产指标', + fields: [ + t('sczbDanChan', '单产', '片/人、班'), t('sczbRiChan', '日产', '片/班'), + t('sczbBianSiLiang', '编丝量', '片/人、班'), t('sczbZhengJianLiang', '整检量', '片/日'), + t('sczbShengChanZhengPinLv', '生产正品率', '%'), t('sczbShangJianZhengPinLv', '商检正品率', '%'), + t('sczbQieDuan', '切断', '次/批'), t('sczbXiaoLv', '效率', '%'), + t('sczbPinZhiCiDian', '编检品质疵点', '%') + ] + }, + { + title: '工艺条件及控制指标', + fields: [ + t('gyzbCheSu', '车速', '转/分'), t('gyzbXiaoYuDaiFanShiJian', '小䈅待返时间', '分'), + t('gyzbCheJianWenDu', '车间温度', '℃'), t('gyzbCheJianXiangDuiShiDu', '车间相对湿度', '%'), + t('gyzbCheXiangWenDu', '车厢温度', '℃'), t('gyzbCheXiangShiDu', '车厢湿度', '%'), + t('gyzbPingHengShiWenDu', '平衡室温度', '℃'), t('gyzbPingHengShiXiangDuiShiDu', '平衡室相对湿度', '%'), + t('gyzbXiaoYuGeiShiWenDu', '小䈅给湿温度', '℃'), t('gyzbXiaoYuSiPianPingHengShiJian', '小䈅丝片平衡时间', '分'), + t('gyzbZhenKongDu', '真空度', 'mp'), t('gyzbChouQiCiShu', '抽气次数', '次'), + t('gyzbDaYuSiPianPingHengShiJian', '大䈅丝片平衡时间', '分'), t('gyzbDaYuSiPianHuiChaoLv', '大䈅丝片回潮率', '%'), + t('gyzbShengSiHuiChaoLv', '生丝回潮率', '%'), t('gyzbDaYuSiPianKuanDu', '大䈅丝片宽度', 'cm'), + t('gyzbXieSiDuiSiLv', '屑丝对丝率', '%'), t('gyzbDaYuSiPianCiDian', '大䈅丝片疵点', '%') + ] + }, + { + title: '签署', + fields: [ + area('jiShuCuoShi', '主要技术措施'), + t('sheJiRen', '设计', '', 8), t('shenHeRen', '审核', '', 8), t('piFuRen', '审批', '', 8) + ] + } + ] + } +}; + +/// 取某单据的默认空表单(嵌套 dyzl 单独处理) +export function emptyForm(type) { + const s = DOC_SCHEMAS[type]; + const form = {}; + const fill = (f) => { + if (f.key.startsWith('dyzl.')) return; + form[f.key] = f.type === 'number' ? 0 : f.type === 'date' ? null : ''; + }; + (s.header || []).forEach(fill); + (s.groups || []).forEach((g) => g.fields.forEach(fill)); + return form; +} diff --git a/web/src/views/qc/yjss/YjssDocDialog.vue b/web/src/views/qc/yjss/YjssDocDialog.vue new file mode 100644 index 0000000..5fc25fa --- /dev/null +++ b/web/src/views/qc/yjss/YjssDocDialog.vue @@ -0,0 +1,364 @@ + + + + + diff --git a/web/src/views/qc/yjss/YjssPrintDialog.vue b/web/src/views/qc/yjss/YjssPrintDialog.vue new file mode 100644 index 0000000..f971a0e --- /dev/null +++ b/web/src/views/qc/yjss/YjssPrintDialog.vue @@ -0,0 +1,290 @@ + + + + + + + diff --git a/web/src/views/qc/yjss/index.vue b/web/src/views/qc/yjss/index.vue new file mode 100644 index 0000000..0f6e942 --- /dev/null +++ b/web/src/views/qc/yjss/index.vue @@ -0,0 +1,268 @@ + + + + + diff --git a/web/src/views/qc/yjss/schema.js b/web/src/views/qc/yjss/schema.js new file mode 100644 index 0000000..3f87dd6 --- /dev/null +++ b/web/src/views/qc/yjss/schema.js @@ -0,0 +1,206 @@ +// 样茧试缫 —— 6 类调查的字段定义 +// 对应实体:backend/F9WebAiTest.Domain/Entities/QC/Yjss/ + +export const DOC_META = { + xjdc: { label: '选茧调查', tip: '11 个茧别的重量与比例,自动换算' }, + jsdc: { label: '解舒调查(试缫)', tip: '一条样茧可有多条试缫记录' }, + jfdc: { label: '茧幅调查', tip: '按茧幅分档统计粒数,计算平均茧幅与整齐率' }, + qpdc: { label: '切剖调查', tip: '原量 / 干量 / 粒数比例,计算茧层率与脱胶率' }, + qjjj: { label: '清洁洁净', tip: '匀度、洁净档位片数、疵点粒数,自动计算清洁/洁净' }, + jsbhxd: { label: '茧丝百回纤度', tip: '按回数逐个录入纤度,自动汇总标注值' } +}; + +const n = (key, label, unit = '', span = 8, precision = 2) => ({ key, label, unit, span, type: 'number', precision }); +const t = (key, label, unit = '', span = 8) => ({ key, label, unit, span, type: 'text' }); +const area = (key, label) => ({ key, label, type: 'textarea', span: 24 }); + +// ---- 选茧调查 ---- +export const JIAN_BIE = [ + ['ltj', '联统茧'], ['hwj', '好五级茧'], ['wj', '五级茧'], ['mianJian', '棉茧'], + ['huangBan', '黄斑'], ['quJian', '蛆茧'], ['shuangGong', '双宫'], ['xueJian', '血茧'], + ['baoPi', '薄皮'], ['jianYi', '茧衣'], ['meiJian', '霉茧'] +]; + +export const XJDC_SCHEMA = { + groups: [ + { + title: '茧重(g)', + fields: JIAN_BIE.map(([k, l]) => n(`zl${k.charAt(0).toUpperCase()}${k.slice(1)}`, l, 'g', 6, 1)) + }, + { + title: '比例(%)', + fields: JIAN_BIE.map(([k, l]) => n(`bl${k.charAt(0).toUpperCase()}${k.slice(1)}`, l, '%', 6)) + }, + { + title: '换算', + fields: [ + n('gongJinLiShu', '公斤茧粒数', '粒', 8, 0), + n('jieShuLiShu', '解舒样茧粒数', '粒', 8, 0), + n('jieShuZhongLiang', '解舒样茧重量', 'g', 8, 1) + ] + } + ] +}; + +// ---- 解舒调查 ---- +export const JSDC_SCHEMA = { + groups: [ + { + title: '缫丝参数', + fields: [ + n('xuHao', '序号', '', 6, 0), n('xuShu', '绪数', '绪', 6, 0), n('dingLi', '定粒', '粒', 6, 0), + n('zhuJianTangWen', '煮茧汤温', '℃', 6, 0), n('saoSiTangWen', '缫丝汤温', '℃', 6, 0), + n('cheSu', '车速', '转/分', 6, 0), n('xiaoSiGanLiang', '小丝干量', 'g', 6, 3), + n('xiaoSiGongLiang', '小丝公量', 'g', 6, 3) + ] + }, + { + title: '纤度与丝长', + fields: [ + n('shengSiXianDu', '生丝纤度', 'D', 6, 4), n('jianSiXianDu', '茧丝纤度', 'D', 6, 4), + n('yiJianSiChang', '一茧丝长', 'm'), n('shengSiZongChang', '生丝总长', '回'), + n('jianSiZongChang', '茧丝总长', '回'), n('jieShuSiChang', '解舒丝长', 'm'), + n('jieShuLv', '解舒率', '%'), n('jieShuGuangZhe', '解舒光折', 'kg'), + n('jiaoShu', '绞数', '绞', 6, 0), n('lingHuiShu', '零回数', '回', 6, 0), + t('xianJianYouXuLv', '鲜茧有绪率', '', 6) + ] + }, + { + title: '落绪分布(粒)', + fields: [ + n('ldfbWai', '外', '', 6, 0), n('ldfbZhong', '中', '', 6, 0), + n('ldfbNei', '内', '', 6, 0), n('ldfbSum', '总粒数', '', 6, 0) + ] + }, + { + title: '蛹体(粒数 / 比例)', + fields: [ + n('ytLsNeiYin', '内印粒数', '', 6, 0), n('ytLsMaoZu', '毛足粒数', '', 6, 0), + n('ytLsChuanTou', '穿头粒数', '', 6, 0), n('ytLsShuangGong', '双宫粒数', '', 6, 0), + n('ytBlNeiYin', '内印比例', '%', 6), n('ytBlMaoZu', '毛足比例', '%', 6), + n('ytBlChuanTou', '穿头比例', '%', 6), n('ytBlShuangGong', '双宫比例', '%', 6) + ] + }, + { + title: '附煮', + fields: [ + t('fzCheJian', '附煮车间', '', 6), t('fzGyzkName', '附煮工艺庄口', '', 6), + t('fzZhengZhuWenDu', '蒸煮室温度', '', 6), t('fzTiaoZhengBuWenDu', '调整部温度', '', 6), + t('fzCheSu', '附煮车速', '', 6) + ] + }, + { + title: '屑物', + fields: [ + n('xwJianBaLiang', '茧巴量', 'g'), n('xwYongYiZhongLiang', '蛹衣重量', 'g'), + n('xwYongYiLv', '蛹衣率', '%'), n('xwLeiJie', '类节', '个'), + n('xwWanMiDiaoCao', '万米吊糙', '次'), n('xwJianBaBiLi', '茧巴比例', '%') + ] + }, + { + title: '样茧', + fields: [ + n('jieShuLiShu', '解舒样茧粒数', '粒', 8, 0), + n('jieShuZhongLiang', '解舒样茧重量', 'g', 8, 1) + ] + } + ] +}; + +// ---- 切剖调查 ---- +export const QPDC_SCHEMA = { + groups: [ + { + title: '原量(g)', + fields: [n('ylQuanJian', '全茧', 'g', 8, 3), n('ylJianCeng', '茧层', 'g', 8, 3), n('ylYongTi', '蛹体', 'g', 8, 3)] + }, + { + title: '干量(g)', + fields: [n('glJianCeng', '茧层干量', 'g', 8, 3), n('glYongTi', '蛹体干量', 'g', 8, 3), n('glZhuJian', '煮茧后干量', 'g', 8, 3)] + }, + { + title: '结果', + fields: [n('jianCengLv', '茧层率', '%', 8), n('jianCengLvGongLiang', '公量茧层率', '%', 8), n('tuoJiaoLv', '脱胶率', '%', 8)] + }, + { + title: '粒数', + fields: [n('lsJiangCan', '僵蚕', '粒', 6, 0), n('lsNeiRan', '内染', '粒', 6, 0), n('lsMeiYong', '霉蛹', '粒', 6, 0), n('lsMaoJiao', '毛脚', '粒', 6, 0)] + }, + { + title: '比例(%)', + fields: [n('blJiangCan', '僵蚕', '%', 6), n('blNeiRan', '内染', '%', 6), n('blMeiYong', '霉蛹', '%', 6), n('blMaoJiao', '毛脚', '%', 6)] + } + ] +}; + +// ---- 清洁洁净 ---- +export const QJJJ_SCHEMA = { + groups: [ + { title: '匀度(片)', fields: [n('ydYiDu', '一度', '片', 6, 0), n('ydErDu', '二度', '片', 6, 0), n('ydSanDu', '三度', '片', 6, 0)] }, + { + title: '洁净档位(片数)', + fields: [100, 95, 90, 85, 80, 75, 70, 65, 60].map((v) => n(`jj${v}`, `${v} 分`, '片', 6, 0)) + }, + { + title: '次要疵点(粒)', + fields: [ + n('qjTdc', '特大糙', '粒', 6, 0), n('qjFs', '废丝', '粒', 6, 0), n('qjDc', '大糙', '粒', 6, 0), + n('qjNfc', '黏附糙', '粒', 6, 0), n('qjDcj', '大长结', '粒', 6, 0), n('qjZlx', '重螺旋', '粒', 6, 0) + ] + }, + { + title: '普通疵点(粒)', + fields: [ + n('qjXc', '小糙', '粒', 6, 0), n('qjCj', '长结', '粒', 6, 0), + n('qjLx', '螺旋', '粒', 6, 0), n('qjHjls', '环结及裂丝', '粒', 6, 0) + ] + }, + { + title: '计算结果', + fields: [n('pianShu', '片数', '片', 8, 0), n('qingJie', '清洁', '分', 8), n('jingJie', '洁净', '分', 8)] + } + ] +}; + +// ---- 茧丝百回纤度 ---- +export const JSBHXD_SCHEMA = { + groups: [ + { + title: '汇总指标', + fields: [ + n('xianDuJunFangCha', '茧丝纤度均方差', '', 8, 3), n('zuiDaKaiCha', '最大开差', '', 8), + n('neiWaiKaiCha', '内外开差', '', 8), n('waiSanBaiHui', '外三百回', '', 8, 4), + n('zhongSanBaiHui', '中三百回', '', 8, 4), n('neiSanBaiHui', '内三百回', '', 8, 4), + n('baiSeJianDanXian', '白色茧单纤', '', 8, 4), n('huiSeJianDanXian', '灰色茧单纤', '', 8, 4), + n('hongSeJianDanXian', '红色茧单纤', '', 8, 4) + ] + } + ] +}; + +export function emptyXjdc() { + const o = {}; + JIAN_BIE.forEach(([k]) => { + const K = k.charAt(0).toUpperCase() + k.slice(1); + o[`zl${K}`] = 0; + o[`bl${K}`] = 0; + }); + Object.assign(o, { gongJinLiShu: 0, jieShuLiShu: 0, jieShuZhongLiang: 0 }); + return o; +} + +/// 清洁洁净计算(F9 口径):片数=洁净档位片数之和;洁净=按片数加权平均; +/// 清洁=100 - (特大糙×3 + 废丝×3 + 大糙×2 + 黏附糙×1 + 大长结×1 + 重螺旋×1) / 片数 × 100 +export function computeQjjj(f) { + const pian = [100, 95, 90, 85, 80, 75, 70, 65, 60].reduce((s, v) => s + (Number(f[`jj${v}`]) || 0), 0); + f.pianShu = pian; + if (pian > 0) { + const jj = [100, 95, 90, 85, 80, 75, 70, 65, 60].reduce((s, v) => s + v * (Number(f[`jj${v}`]) || 0), 0); + f.jingJie = Math.round((jj / pian) * 100) / 100; + const ded = (Number(f.qjTdc) || 0) * 3 + (Number(f.qjFs) || 0) * 3 + (Number(f.qjDc) || 0) * 2 + + (Number(f.qjNfc) || 0) + (Number(f.qjDcj) || 0) + (Number(f.qjZlx) || 0); + f.qingJie = Math.round((100 - (ded / pian) * 100) * 100) / 100; + if (f.qingJie < 0) f.qingJie = 0; + } + return f; +} diff --git a/web/src/views/report/center/index.vue b/web/src/views/report/center/index.vue new file mode 100644 index 0000000..188f8ea --- /dev/null +++ b/web/src/views/report/center/index.vue @@ -0,0 +1,363 @@ + + + + + diff --git a/web/src/views/report/manage/index.vue b/web/src/views/report/manage/index.vue new file mode 100644 index 0000000..f5581f7 --- /dev/null +++ b/web/src/views/report/manage/index.vue @@ -0,0 +1,188 @@ + + + + + diff --git a/web/src/views/sys/department/index.vue b/web/src/views/sys/department/index.vue index 15d6911..6179cd3 100644 --- a/web/src/views/sys/department/index.vue +++ b/web/src/views/sys/department/index.vue @@ -1,34 +1,117 @@