@qwe8652591/abap-recursive-query 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +223 -0
  3. package/dist/cli/consumer-cli.d.ts +8 -0
  4. package/dist/cli/consumer-cli.d.ts.map +1 -0
  5. package/dist/cli/consumer-cli.js +180 -0
  6. package/dist/cli/producer-cli.d.ts +8 -0
  7. package/dist/cli/producer-cli.d.ts.map +1 -0
  8. package/dist/cli/producer-cli.js +249 -0
  9. package/dist/cli/query-object.d.ts +3 -0
  10. package/dist/cli/query-object.d.ts.map +1 -0
  11. package/dist/cli/query-object.js +486 -0
  12. package/dist/common/FilterCache.d.ts +62 -0
  13. package/dist/common/FilterCache.d.ts.map +1 -0
  14. package/dist/common/FilterCache.js +119 -0
  15. package/dist/common/RedisQueueManager.d.ts +170 -0
  16. package/dist/common/RedisQueueManager.d.ts.map +1 -0
  17. package/dist/common/RedisQueueManager.js +663 -0
  18. package/dist/common/abapStructures.d.ts +391 -0
  19. package/dist/common/abapStructures.d.ts.map +1 -0
  20. package/dist/common/abapStructures.js +2 -0
  21. package/dist/common/config.d.ts +66 -0
  22. package/dist/common/config.d.ts.map +1 -0
  23. package/dist/common/config.js +56 -0
  24. package/dist/common/index.d.ts +13 -0
  25. package/dist/common/index.d.ts.map +1 -0
  26. package/dist/common/index.js +38 -0
  27. package/dist/common/recursiveQueryConfig.d.ts +77 -0
  28. package/dist/common/recursiveQueryConfig.d.ts.map +1 -0
  29. package/dist/common/recursiveQueryConfig.js +129 -0
  30. package/dist/common/tableStructures.d.ts +176 -0
  31. package/dist/common/tableStructures.d.ts.map +1 -0
  32. package/dist/common/tableStructures.js +10 -0
  33. package/dist/common/types.d.ts +104 -0
  34. package/dist/common/types.d.ts.map +1 -0
  35. package/dist/common/types.js +31 -0
  36. package/dist/common/utils.d.ts +57 -0
  37. package/dist/common/utils.d.ts.map +1 -0
  38. package/dist/common/utils.js +300 -0
  39. package/dist/consumer/FileDownloadConsumer.d.ts +127 -0
  40. package/dist/consumer/FileDownloadConsumer.d.ts.map +1 -0
  41. package/dist/consumer/FileDownloadConsumer.js +1003 -0
  42. package/dist/consumer/generators/baseGenerator.d.ts +38 -0
  43. package/dist/consumer/generators/baseGenerator.d.ts.map +1 -0
  44. package/dist/consumer/generators/baseGenerator.js +103 -0
  45. package/dist/consumer/generators/domainGenerator.d.ts +78 -0
  46. package/dist/consumer/generators/domainGenerator.d.ts.map +1 -0
  47. package/dist/consumer/generators/domainGenerator.js +241 -0
  48. package/dist/consumer/generators/guiStatusGenerator.d.ts +16 -0
  49. package/dist/consumer/generators/guiStatusGenerator.d.ts.map +1 -0
  50. package/dist/consumer/generators/guiStatusGenerator.js +48 -0
  51. package/dist/consumer/generators/guiTitleGenerator.d.ts +16 -0
  52. package/dist/consumer/generators/guiTitleGenerator.d.ts.map +1 -0
  53. package/dist/consumer/generators/guiTitleGenerator.js +48 -0
  54. package/dist/consumer/generators/index.d.ts +14 -0
  55. package/dist/consumer/generators/index.d.ts.map +1 -0
  56. package/dist/consumer/generators/index.js +38 -0
  57. package/dist/consumer/generators/messageGenerator.d.ts +16 -0
  58. package/dist/consumer/generators/messageGenerator.d.ts.map +1 -0
  59. package/dist/consumer/generators/messageGenerator.js +73 -0
  60. package/dist/consumer/generators/metadataGenerator.d.ts +29 -0
  61. package/dist/consumer/generators/metadataGenerator.d.ts.map +1 -0
  62. package/dist/consumer/generators/metadataGenerator.js +135 -0
  63. package/dist/consumer/generators/screenGenerator.d.ts +173 -0
  64. package/dist/consumer/generators/screenGenerator.d.ts.map +1 -0
  65. package/dist/consumer/generators/screenGenerator.js +859 -0
  66. package/dist/consumer/generators/structureGenerator.d.ts +36 -0
  67. package/dist/consumer/generators/structureGenerator.d.ts.map +1 -0
  68. package/dist/consumer/generators/structureGenerator.js +131 -0
  69. package/dist/consumer/generators/textElementGenerator.d.ts +16 -0
  70. package/dist/consumer/generators/textElementGenerator.d.ts.map +1 -0
  71. package/dist/consumer/generators/textElementGenerator.js +70 -0
  72. package/dist/consumer/handlers/handleGetClass.d.ts +8 -0
  73. package/dist/consumer/handlers/handleGetClass.d.ts.map +1 -0
  74. package/dist/consumer/handlers/handleGetClass.js +18 -0
  75. package/dist/consumer/handlers/handleGetFunction.d.ts +8 -0
  76. package/dist/consumer/handlers/handleGetFunction.d.ts.map +1 -0
  77. package/dist/consumer/handlers/handleGetFunction.js +19 -0
  78. package/dist/consumer/handlers/handleGetFunctionGroup.d.ts +8 -0
  79. package/dist/consumer/handlers/handleGetFunctionGroup.d.ts.map +1 -0
  80. package/dist/consumer/handlers/handleGetFunctionGroup.js +19 -0
  81. package/dist/consumer/handlers/handleGetInclude.d.ts +8 -0
  82. package/dist/consumer/handlers/handleGetInclude.d.ts.map +1 -0
  83. package/dist/consumer/handlers/handleGetInclude.js +18 -0
  84. package/dist/consumer/handlers/handleGetProgram.d.ts +8 -0
  85. package/dist/consumer/handlers/handleGetProgram.d.ts.map +1 -0
  86. package/dist/consumer/handlers/handleGetProgram.js +27 -0
  87. package/dist/consumer/handlers/handleZTableQuery.d.ts +8 -0
  88. package/dist/consumer/handlers/handleZTableQuery.d.ts.map +1 -0
  89. package/dist/consumer/handlers/handleZTableQuery.js +20 -0
  90. package/dist/consumer/handlers/index.d.ts +10 -0
  91. package/dist/consumer/handlers/index.d.ts.map +1 -0
  92. package/dist/consumer/handlers/index.js +27 -0
  93. package/dist/consumer/index.d.ts +9 -0
  94. package/dist/consumer/index.d.ts.map +1 -0
  95. package/dist/consumer/index.js +24 -0
  96. package/dist/consumer/utils/download.d.ts +13 -0
  97. package/dist/consumer/utils/download.d.ts.map +1 -0
  98. package/dist/consumer/utils/download.js +38 -0
  99. package/dist/consumer/utils/index.d.ts +5 -0
  100. package/dist/consumer/utils/index.d.ts.map +1 -0
  101. package/dist/consumer/utils/index.js +20 -0
  102. package/dist/handlers/handleGetClass.d.ts +8 -0
  103. package/dist/handlers/handleGetClass.d.ts.map +1 -0
  104. package/dist/handlers/handleGetClass.js +19 -0
  105. package/dist/handlers/handleGetFunction.d.ts +8 -0
  106. package/dist/handlers/handleGetFunction.d.ts.map +1 -0
  107. package/dist/handlers/handleGetFunction.js +20 -0
  108. package/dist/handlers/handleGetFunctionGroup.d.ts +8 -0
  109. package/dist/handlers/handleGetFunctionGroup.d.ts.map +1 -0
  110. package/dist/handlers/handleGetFunctionGroup.js +19 -0
  111. package/dist/handlers/handleGetInclude.d.ts +8 -0
  112. package/dist/handlers/handleGetInclude.d.ts.map +1 -0
  113. package/dist/handlers/handleGetInclude.js +19 -0
  114. package/dist/handlers/handleGetInterface.d.ts +8 -0
  115. package/dist/handlers/handleGetInterface.d.ts.map +1 -0
  116. package/dist/handlers/handleGetInterface.js +19 -0
  117. package/dist/handlers/handleGetPackage.d.ts +8 -0
  118. package/dist/handlers/handleGetPackage.d.ts.map +1 -0
  119. package/dist/handlers/handleGetPackage.js +42 -0
  120. package/dist/handlers/handleGetProgram.d.ts +8 -0
  121. package/dist/handlers/handleGetProgram.d.ts.map +1 -0
  122. package/dist/handlers/handleGetProgram.js +19 -0
  123. package/dist/handlers/handleGetStructure.d.ts +8 -0
  124. package/dist/handlers/handleGetStructure.d.ts.map +1 -0
  125. package/dist/handlers/handleGetStructure.js +19 -0
  126. package/dist/handlers/handleGetTable.d.ts +8 -0
  127. package/dist/handlers/handleGetTable.d.ts.map +1 -0
  128. package/dist/handlers/handleGetTable.js +19 -0
  129. package/dist/handlers/handleGetTableContents.d.ts +8 -0
  130. package/dist/handlers/handleGetTableContents.d.ts.map +1 -0
  131. package/dist/handlers/handleGetTableContents.js +22 -0
  132. package/dist/handlers/handleGetTransaction.d.ts +8 -0
  133. package/dist/handlers/handleGetTransaction.d.ts.map +1 -0
  134. package/dist/handlers/handleGetTransaction.js +19 -0
  135. package/dist/handlers/handleGetTypeInfo.d.ts +8 -0
  136. package/dist/handlers/handleGetTypeInfo.d.ts.map +1 -0
  137. package/dist/handlers/handleGetTypeInfo.js +32 -0
  138. package/dist/handlers/handleSearchObject.d.ts +8 -0
  139. package/dist/handlers/handleSearchObject.d.ts.map +1 -0
  140. package/dist/handlers/handleSearchObject.js +20 -0
  141. package/dist/handlers/handleZObjectQuery.d.ts +8 -0
  142. package/dist/handlers/handleZObjectQuery.d.ts.map +1 -0
  143. package/dist/handlers/handleZObjectQuery.js +25 -0
  144. package/dist/handlers/handleZTableQuery.d.ts +8 -0
  145. package/dist/handlers/handleZTableQuery.d.ts.map +1 -0
  146. package/dist/handlers/handleZTableQuery.js +20 -0
  147. package/dist/index.d.ts +17 -0
  148. package/dist/index.d.ts.map +1 -0
  149. package/dist/index.js +45 -0
  150. package/dist/lib/download.d.ts +49 -0
  151. package/dist/lib/download.d.ts.map +1 -0
  152. package/dist/lib/download.js +78 -0
  153. package/dist/lib/index.d.ts +9 -0
  154. package/dist/lib/index.d.ts.map +1 -0
  155. package/dist/lib/index.js +23 -0
  156. package/dist/lib/utils.d.ts +31 -0
  157. package/dist/lib/utils.d.ts.map +1 -0
  158. package/dist/lib/utils.js +272 -0
  159. package/dist/producer/RecursiveQueryProducer.d.ts +92 -0
  160. package/dist/producer/RecursiveQueryProducer.d.ts.map +1 -0
  161. package/dist/producer/RecursiveQueryProducer.js +496 -0
  162. package/dist/producer/handlers/handleZObjectQuery.d.ts +8 -0
  163. package/dist/producer/handlers/handleZObjectQuery.d.ts.map +1 -0
  164. package/dist/producer/handlers/handleZObjectQuery.js +24 -0
  165. package/dist/producer/handlers/index.d.ts +6 -0
  166. package/dist/producer/handlers/index.d.ts.map +1 -0
  167. package/dist/producer/handlers/index.js +21 -0
  168. package/dist/producer/index.d.ts +7 -0
  169. package/dist/producer/index.d.ts.map +1 -0
  170. package/dist/producer/index.js +22 -0
  171. package/dist/recursive/abapStructures.d.ts +377 -0
  172. package/dist/recursive/abapStructures.d.ts.map +1 -0
  173. package/dist/recursive/abapStructures.js +2 -0
  174. package/dist/recursive/generate/baseGenerator.d.ts +34 -0
  175. package/dist/recursive/generate/baseGenerator.d.ts.map +1 -0
  176. package/dist/recursive/generate/baseGenerator.js +112 -0
  177. package/dist/recursive/generate/domainGenerator.d.ts +26 -0
  178. package/dist/recursive/generate/domainGenerator.d.ts.map +1 -0
  179. package/dist/recursive/generate/domainGenerator.js +128 -0
  180. package/dist/recursive/generate/functionGroupGenerator.d.ts +30 -0
  181. package/dist/recursive/generate/functionGroupGenerator.d.ts.map +1 -0
  182. package/dist/recursive/generate/functionGroupGenerator.js +90 -0
  183. package/dist/recursive/generate/index.d.ts +12 -0
  184. package/dist/recursive/generate/index.d.ts.map +1 -0
  185. package/dist/recursive/generate/index.js +34 -0
  186. package/dist/recursive/generate/messageGenerator.d.ts +16 -0
  187. package/dist/recursive/generate/messageGenerator.d.ts.map +1 -0
  188. package/dist/recursive/generate/messageGenerator.js +73 -0
  189. package/dist/recursive/generate/screenGenerator.d.ts +173 -0
  190. package/dist/recursive/generate/screenGenerator.d.ts.map +1 -0
  191. package/dist/recursive/generate/screenGenerator.js +858 -0
  192. package/dist/recursive/generate/structureGenerator.d.ts +22 -0
  193. package/dist/recursive/generate/structureGenerator.d.ts.map +1 -0
  194. package/dist/recursive/generate/structureGenerator.js +88 -0
  195. package/dist/recursive/generate/textElementGenerator.d.ts +16 -0
  196. package/dist/recursive/generate/textElementGenerator.d.ts.map +1 -0
  197. package/dist/recursive/generate/textElementGenerator.js +68 -0
  198. package/dist/recursive/handleRecursiveObjectQuery.d.ts +94 -0
  199. package/dist/recursive/handleRecursiveObjectQuery.d.ts.map +1 -0
  200. package/dist/recursive/handleRecursiveObjectQuery.js +219 -0
  201. package/dist/recursive/recursiveObjectQuery.d.ts +159 -0
  202. package/dist/recursive/recursiveObjectQuery.d.ts.map +1 -0
  203. package/dist/recursive/recursiveObjectQuery.js +1358 -0
  204. package/dist/recursive/recursiveQueryConfig.d.ts +129 -0
  205. package/dist/recursive/recursiveQueryConfig.d.ts.map +1 -0
  206. package/dist/recursive/recursiveQueryConfig.js +133 -0
  207. package/dist/recursive/tableStructures.d.ts +196 -0
  208. package/dist/recursive/tableStructures.d.ts.map +1 -0
  209. package/dist/recursive/tableStructures.js +10 -0
  210. package/package.json +47 -0
@@ -0,0 +1,859 @@
1
+ "use strict";
2
+ /**
3
+ * 统一屏幕生成器 (优化版)
4
+ * 整合屏幕字段和流程逻辑,生成完整的屏幕XML文件
5
+ * 遵循abapGit协议规范,完全符合SAP标准格式
6
+ *
7
+ * 功能说明:
8
+ * 1. 支持单屏幕和多屏幕XML生成
9
+ * 2. 完整的头部、容器、字段和流程逻辑整合
10
+ * 3. 数据验证和格式化
11
+ * 4. 向后兼容旧版本接口
12
+ *
13
+ * @author Unified Screen Generator
14
+ * @version 2.0.0
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.ScreenCache = void 0;
18
+ exports.validateScreenData = validateScreenData;
19
+ exports.generateMultipleScreensXmlContent = generateMultipleScreensXmlContent;
20
+ exports.addScreenDataToCache = addScreenDataToCache;
21
+ exports.finalizeScreenGeneration = finalizeScreenGeneration;
22
+ exports.generateMultipleScreensXml = generateMultipleScreensXml;
23
+ const fs_1 = require("fs");
24
+ const path_1 = require("path");
25
+ const baseGenerator_1 = require("./baseGenerator");
26
+ /**
27
+ * 常量定义
28
+ */
29
+ const CONSTANTS = {
30
+ // 默认值
31
+ DEFAULT_LANGUAGE: 'E',
32
+ DEFAULT_SCREEN_TYPE: 'N',
33
+ DEFAULT_LINES: '027',
34
+ DEFAULT_COLUMNS: '132',
35
+ DEFAULT_CONTAINER_TYPE: 'SCREEN',
36
+ DEFAULT_CONTAINER_NAME: 'SCREEN',
37
+ DEFAULT_PROGRAM_SUBC: 'M',
38
+ DEFAULT_RLOAD: 'E',
39
+ DEFAULT_FIXPT: 'X',
40
+ DEFAULT_UCCHECK: 'X',
41
+ // XML相关
42
+ XML_VERSION: '<?xml version="1.0" encoding="utf-8"?>',
43
+ ABAPGIT_VERSION: 'v1.0.0',
44
+ ABAPGIT_SERIALIZER: 'LCL_OBJECT_PROG',
45
+ // 文件扩展名
46
+ EXT_PROG: '.prog.xml',
47
+ // 数字格式
48
+ NUM_PADDING: 3,
49
+ };
50
+ // ============================================================
51
+ // 数据验证函数
52
+ // ============================================================
53
+ /**
54
+ * 验证屏幕数据完整性
55
+ * 对所有输入数据进行严格验证,确保生成的XML符合规范
56
+ */
57
+ function validateScreenData(screenData) {
58
+ const errors = [];
59
+ const warnings = [];
60
+ // 验证必填字段
61
+ if (!screenData.screenNumber) {
62
+ errors.push('缺少屏幕号(screenNumber)');
63
+ }
64
+ else {
65
+ // 验证屏幕号格式(必须是4位数字)
66
+ if (!/^\d{4}$/.test(screenData.screenNumber)) {
67
+ warnings.push(`屏幕号建议使用4位数字格式: ${screenData.screenNumber}`);
68
+ }
69
+ }
70
+ if (!screenData.programName) {
71
+ errors.push('缺少对象名称(objectName)');
72
+ }
73
+ else {
74
+ // 验证对象名称长度
75
+ if (screenData.programName.length > 40) {
76
+ errors.push(`对象名称过长(最多40字符): ${screenData.programName}`);
77
+ }
78
+ }
79
+ // 验证字段数据
80
+ if (screenData.fields && Array.isArray(screenData.fields)) {
81
+ for (let i = 0; i < screenData.fields.length; i++) {
82
+ const field = screenData.fields[i];
83
+ // ISCREENFIELD中NAME是必需字段
84
+ if (!field.NAME) {
85
+ errors.push(`字段 ${i + 1}: 缺少字段名(NAME)`);
86
+ }
87
+ // 验证字段类型
88
+ if (field.TYPE && String(field.TYPE).length > 4) {
89
+ warnings.push(`字段 ${i + 1}: TYPE可能过长: ${field.TYPE}`);
90
+ }
91
+ // 验证长度字段(ISCREENFIELD中使用LENG)
92
+ if (field.LENG !== undefined && field.LENG !== null) {
93
+ if (field.LENG < 0) {
94
+ warnings.push(`字段 ${i + 1}: LENG值无效: ${field.LENG}`);
95
+ }
96
+ }
97
+ }
98
+ }
99
+ // 验证流程逻辑数据
100
+ if (screenData.flowLogic && Array.isArray(screenData.flowLogic)) {
101
+ let hasPBO = false;
102
+ let hasPAI = false;
103
+ for (let i = 0; i < screenData.flowLogic.length; i++) {
104
+ const flow = screenData.flowLogic[i];
105
+ // CODE字段可以为空(空行)
106
+ if (flow.CODE === undefined || flow.CODE === null) {
107
+ flow.CODE = '';
108
+ }
109
+ // 检查是否包含必要的流程控制语句
110
+ const code = String(flow.CODE).toUpperCase();
111
+ if (code.includes('PROCESS BEFORE OUTPUT')) {
112
+ hasPBO = true;
113
+ }
114
+ if (code.includes('PROCESS AFTER INPUT')) {
115
+ hasPAI = true;
116
+ }
117
+ }
118
+ // PBO和PAI是屏幕必需的流程控制
119
+ if (screenData.flowLogic.length > 0) {
120
+ if (!hasPBO) {
121
+ warnings.push('流程逻辑中缺少PROCESS BEFORE OUTPUT语句');
122
+ }
123
+ if (!hasPAI) {
124
+ warnings.push('流程逻辑中缺少PROCESS AFTER INPUT语句');
125
+ }
126
+ }
127
+ }
128
+ // 验证容器数据
129
+ if (screenData.containers && Array.isArray(screenData.containers)) {
130
+ const hasMainScreen = screenData.containers.some(c => c.TYPE === 'SCREEN' && c.NAME === 'SCREEN');
131
+ if (!hasMainScreen) {
132
+ warnings.push('建议包含主屏幕容器(TYPE=SCREEN, NAME=SCREEN)');
133
+ }
134
+ }
135
+ return {
136
+ valid: errors.length === 0,
137
+ errors,
138
+ warnings
139
+ };
140
+ }
141
+ // ============================================================
142
+ // 屏幕头部处理
143
+ // ============================================================
144
+ /**
145
+ * 从ScreenDesc创建ScreenHeader
146
+ * 将屏幕描述信息转换为头部信息
147
+ * 严格按照ISCREENDESC的值,不设置默认值
148
+ */
149
+ function createHeaderFromScreenDesc(screenDesc) {
150
+ return {
151
+ PROGRAM: screenDesc.PROG || '',
152
+ SCREEN: screenDesc.SCREEN || '',
153
+ LANGUAGE: screenDesc.SPRA,
154
+ DESCRIPT: screenDesc.DTXT,
155
+ TYPE: screenDesc.TYPE,
156
+ NEXTSCREEN: screenDesc.FNUM,
157
+ LINES: screenDesc.NOLI,
158
+ COLUMNS: screenDesc.NOCO
159
+ };
160
+ }
161
+ /**
162
+ * 创建默认的ScreenHeader
163
+ * 当没有ScreenDesc时使用
164
+ */
165
+ function createDefaultScreenHeader(objectName, screenNumber) {
166
+ return {
167
+ PROGRAM: objectName,
168
+ SCREEN: screenNumber,
169
+ LANGUAGE: CONSTANTS.DEFAULT_LANGUAGE,
170
+ DESCRIPT: `Screen ${screenNumber}`,
171
+ TYPE: CONSTANTS.DEFAULT_SCREEN_TYPE,
172
+ NEXTSCREEN: screenNumber,
173
+ LINES: CONSTANTS.DEFAULT_LINES,
174
+ COLUMNS: CONSTANTS.DEFAULT_COLUMNS
175
+ };
176
+ }
177
+ /**
178
+ * 获取屏幕头部(统一逻辑,避免重复)
179
+ * 说明:
180
+ * - header 通常不会被初始化(undefined),它的值应该从 screenDesc 派生
181
+ * - 优先级:screenDesc > 默认值
182
+ * - header 参数保留仅用于向后兼容,实际使用中应该依赖 screenDesc
183
+ */
184
+ function getScreenHeader(screenDesc, header, objectName, screenNumber) {
185
+ // header 通常没有初始值,实际值从 screenDesc 派生
186
+ if (screenDesc) {
187
+ return createHeaderFromScreenDesc(screenDesc);
188
+ }
189
+ // header 参数仅用于向后兼容特殊场景
190
+ if (header) {
191
+ return header;
192
+ }
193
+ // 如果既没有 screenDesc 也没有 header,使用默认值
194
+ return createDefaultScreenHeader(objectName, screenNumber);
195
+ }
196
+ /**
197
+ * 生成屏幕头部XML
198
+ * 严格遵循abapGit协议的HEADER结构
199
+ * 兼容空值输出
200
+ */
201
+ function generateScreenHeaderXml(header, indent = ' ') {
202
+ let xml = `${indent}<HEADER>\n`;
203
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'PROGRAM', header.PROGRAM, indent + ' ');
204
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'SCREEN', header.SCREEN, indent + ' ');
205
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'LANGUAGE', header.LANGUAGE, indent + ' ');
206
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'DESCRIPT', header.DESCRIPT, indent + ' ');
207
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TYPE', header.TYPE, indent + ' ');
208
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'NEXTSCREEN', header.NEXTSCREEN, indent + ' ');
209
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'LINES', header.LINES, indent + ' ');
210
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'COLUMNS', header.COLUMNS, indent + ' ');
211
+ xml += `${indent}</HEADER>\n`;
212
+ return xml;
213
+ }
214
+ // ============================================================
215
+ // 容器处理
216
+ // ============================================================
217
+ /**
218
+ * 生成容器定义XML
219
+ * 遵循RPY_DYCATT结构定义
220
+ */
221
+ function generateScreenContainersXml(containers, indent = ' ') {
222
+ let xml = `${indent}<CONTAINERS>\n`;
223
+ for (const container of containers) {
224
+ xml += `${indent} <RPY_DYCATT>\n`;
225
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TYPE', container.TYPE || CONSTANTS.DEFAULT_CONTAINER_TYPE, indent + ' ');
226
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'NAME', container.NAME || CONSTANTS.DEFAULT_CONTAINER_NAME, indent + ' ');
227
+ xml += `${indent} </RPY_DYCATT>\n`;
228
+ }
229
+ xml += `${indent}</CONTAINERS>\n`;
230
+ return xml;
231
+ }
232
+ // ============================================================
233
+ // 屏幕字段处理
234
+ // ============================================================
235
+ /**
236
+ * 生成屏幕字段XML
237
+ * 遵循RPY_DYFATC结构定义,严格按照ISCREENFIELD字段顺序输出
238
+ *
239
+ * 字段名映射关系(仅对特殊字段):
240
+ * - LENG -> LENGTH (长度)
241
+ * - VLENG -> VISLENGTH (可见长度)
242
+ * - HIGH -> HEIGHT (高度)
243
+ *
244
+ * 其他字段保持原名直接输出到XML
245
+ */
246
+ function generateScreenFieldsXml(fields, programName, indent = ' ') {
247
+ if (!fields || fields.length === 0) {
248
+ return '';
249
+ }
250
+ let xml = `${indent}<FIELDS>\n`;
251
+ for (const field of fields) {
252
+ xml += `${indent} <RPY_DYFATC>\n`;
253
+ // PROGRAM 标签(使用传入的程序名)
254
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'PROGRAM', programName, indent + ' ');
255
+ // XML特有的容器字段(不在ISCREENFIELD中,用于XML结构完整性)
256
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'CONT_TYPE', field.CONT_TYPE || CONSTANTS.DEFAULT_CONTAINER_TYPE, indent + ' ');
257
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'CONT_NAME', field.CONT_NAME || CONSTANTS.DEFAULT_CONTAINER_NAME, indent + ' ');
258
+ // ========== 以下严格按照ISCREENFIELD的字段顺序 ==========
259
+ // 字段类型和名称(ISCREENFIELD核心字段)
260
+ // TYPE: 字段类型(直接使用,不做转换)
261
+ // NAME: 字段名称(必需)
262
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TYPE', field.TYPE, indent + ' ');
263
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'NAME', field.NAME, indent + ' ');
264
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TYP', field.TYP, indent + ' ');
265
+ // XML特有字段
266
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TEXT', field.TEXT, indent + ' ');
267
+ // 尺寸属性(特殊映射:ISCREENFIELD字段名 -> XML标签名)
268
+ // LENG -> LENGTH, VLENG -> VISLENGTH, HIGH -> HEIGHT
269
+ // 这些是数字字段,0也是有效值,需要补零
270
+ if (field.LENG !== undefined && field.LENG !== null) {
271
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'LENGTH', String(field.LENG).padStart(CONSTANTS.NUM_PADDING, '0'), indent + ' ');
272
+ }
273
+ if (field.VLENG !== undefined && field.VLENG !== null) {
274
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'VISLENGTH', String(field.VLENG).padStart(CONSTANTS.NUM_PADDING, '0'), indent + ' ');
275
+ }
276
+ if (field.HIGH !== undefined && field.HIGH !== null) {
277
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'HEIGHT', String(field.HIGH).padStart(CONSTANTS.NUM_PADDING, '0'), indent + ' ');
278
+ }
279
+ // XML特有字段
280
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FORMAT', field.FORMAT, indent + ' ');
281
+ // 位置信息
282
+ xml = (0, baseGenerator_1.addXmlNumericField)(xml, 'LINE', field.LINE, indent + ' ');
283
+ xml = (0, baseGenerator_1.addXmlNumericField)(xml, 'COLN', field.COLN, indent + ' ');
284
+ // 输入输出属性(按ISCREENFIELD顺序)
285
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FEIN', field.FEIN, indent + ' ');
286
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FOUT', field.FOUT, indent + ' ');
287
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'SCROLL', field.SCROLL, indent + ' ');
288
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'DICT', field.DICT, indent + ' ');
289
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'DMOD', field.DMOD, indent + ' ');
290
+ // 分组
291
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'GRP1', field.GRP1, indent + ' ');
292
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'GRP2', field.GRP2, indent + ' ');
293
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'GRP3', field.GRP3, indent + ' ');
294
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'GRP4', field.GRP4, indent + ' ');
295
+ xml = (0, baseGenerator_1.addXmlNumericField)(xml, 'LOOP', field.LOOP, indent + ' ');
296
+ // ISCREENFIELD特有输入输出字段(按顺序)
297
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FOBL', field.FOBL, indent + ' ');
298
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FSTR', field.FSTR, indent + ' ');
299
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FNRS', field.FNRS, indent + ' ');
300
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FGKS', field.FGKS, indent + ' ');
301
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FFIX', field.FFIX, indent + ' ');
302
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FOSB', field.FOSB, indent + ' ');
303
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FFKY', field.FFKY, indent + ' ');
304
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FSPA', field.FSPA, indent + ' ');
305
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FGPA', field.FGPA, indent + ' ');
306
+ // 系统字段
307
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'PAID', field.PAID, indent + ' ');
308
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'UCNV', field.UCNV, indent + ' ');
309
+ // 颜色和样式(按ISCREENFIELD顺序)
310
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'HELL', field.HELL, indent + ' ');
311
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'UNSI', field.UNSI, indent + ' ');
312
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'INVR', field.INVR, indent + ' ');
313
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'BLIN', field.BLIN, indent + ' ');
314
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'ULIN', field.ULIN, indent + ' ');
315
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'COLR', field.COLR, indent + ' ');
316
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FJUS', field.FJUS, indent + ' ');
317
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FILL', field.FILL, indent + ' ');
318
+ // 标签
319
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'LABELLEFT', field.LABELLEFT, indent + ' ');
320
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'LABELRIGHT', field.LABELRIGHT, indent + ' ');
321
+ // 交互属性
322
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'DBLCLICK', field.DBLCLICK, indent + ' ');
323
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'LTR', field.LTR, indent + ' ');
324
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'BIDICTRL', field.BIDICTRL, indent + ' ');
325
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'MTCH', field.MTCH, indent + ' ');
326
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'WAER', field.WAER, indent + ' ');
327
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FCOD', field.FCOD, indent + ' ');
328
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'PTYPE', field.PTYPE, indent + ' ');
329
+ // 上下文菜单
330
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'CTMENUPROG', field.CTMENUPROG, indent + ' ');
331
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'CTMENUSTAT', field.CTMENUSTAT, indent + ' ');
332
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'CTMENUONCT', field.CTMENUONCT, indent + ' ');
333
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'CTMENUST', field.CTMENUST, indent + ' ');
334
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'CTMENUDY', field.CTMENUDY, indent + ' ');
335
+ // 控件容器
336
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'CCNAME', field.CCNAME, indent + ' ');
337
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'CCDLEFT', field.CCDLEFT, indent + ' ');
338
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'CCDRIGHT', field.CCDRIGHT, indent + ' ');
339
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'CCDTOP', field.CCDTOP, indent + ' ');
340
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'CCDBOTTOM', field.CCDBOTTOM, indent + ' ');
341
+ xml = (0, baseGenerator_1.addXmlNumericField)(xml, 'CCDLEN_L', field.CCDLEN_L, indent + ' ');
342
+ xml = (0, baseGenerator_1.addXmlNumericField)(xml, 'CCDLEN_R', field.CCDLEN_R, indent + ' ');
343
+ xml = (0, baseGenerator_1.addXmlNumericField)(xml, 'CCDHIG_T', field.CCDHIG_T, indent + ' ');
344
+ xml = (0, baseGenerator_1.addXmlNumericField)(xml, 'CCDHIG_B', field.CCDHIG_B, indent + ' ');
345
+ // 列表类型
346
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'LTYP', field.LTYP, indent + ' ');
347
+ // 表格控件
348
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'GTYP', field.GTYP, indent + ' ');
349
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FIXF', field.FIXF, indent + ' ');
350
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'NO3D', field.NO3D, indent + ' ');
351
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'OOUT', field.OOUT, indent + ' ');
352
+ // 窗口
353
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'WIN1', field.WIN1, indent + ' ');
354
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'WIN2', field.WIN2, indent + ' ');
355
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'WIN3', field.WIN3, indent + ' ');
356
+ // 静态文本
357
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'STXT', field.STXT, indent + ' ');
358
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'STXT_EN', field.STXT_EN, indent + ' ');
359
+ // 组合框
360
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'CMBTAST', field.CMBTAST, indent + ' ');
361
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'CMBFOC', field.CMBFOC, indent + ' ');
362
+ // 下拉列表
363
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'DROPFROM', field.DROPFROM, indent + ' ');
364
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'DROPDOWN', field.DROPDOWN, indent + ' ');
365
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FETCHFLD', field.FETCHFLD, indent + ' ');
366
+ // RFC
367
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'RFCDEST', field.RFCDEST, indent + ' ');
368
+ // 搜索帮助
369
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'SCHAB', field.SCHAB, indent + ' ');
370
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'SCHLK', field.SCHLK, indent + ' ');
371
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'SCHLM', field.SCHLM, indent + ' ');
372
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'SCHLL', field.SCHLL, indent + ' ');
373
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'UEBER', field.UEBER, indent + ' ');
374
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'SCHLNO', field.SCHLNO, indent + ' ');
375
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'SCHABNO', field.SCHABNO, indent + ' ');
376
+ // 文本模式
377
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TEXTMOD', field.TEXTMOD, indent + ' ');
378
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TEXTORIG', field.TEXTORIG, indent + ' ');
379
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'DEZUSATZ', field.DEZUSATZ, indent + ' ');
380
+ // 表格布局(TL*系列,按ISCREENFIELD顺序)
381
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLTRENNHOR', field.TLTRENNHOR, indent + ' ');
382
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLTRENNVER', field.TLTRENNVER, indent + ' ');
383
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLSCROLLH', field.TLSCROLLH, indent + ' ');
384
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLSCROLLV', field.TLSCROLLV, indent + ' ');
385
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLSCROLL', field.TLSCROLL, indent + ' ');
386
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLSELECTZN', field.TLSELECTZN, indent + ' ');
387
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLSELECTZS', field.TLSELECTZS, indent + ' ');
388
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLSELECTZM', field.TLSELECTZM, indent + ' ');
389
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLSELECTSN', field.TLSELECTSN, indent + ' ');
390
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLSELECTSS', field.TLSELECTSS, indent + ' ');
391
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLSELECTSM', field.TLSELECTSM, indent + ' ');
392
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLSHOWONLY', field.TLSHOWONLY, indent + ' ');
393
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLVARV', field.TLVARV, indent + ' ');
394
+ xml = (0, baseGenerator_1.addXmlNumericField)(xml, 'TLVARVMIN', field.TLVARVMIN, indent + ' ');
395
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLVARH', field.TLVARH, indent + ' ');
396
+ xml = (0, baseGenerator_1.addXmlNumericField)(xml, 'TLVARHMIN', field.TLVARHMIN, indent + ' ');
397
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLMARK', field.TLMARK, indent + ' ');
398
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLTITEL', field.TLTITEL, indent + ' ');
399
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLUEBER', field.TLUEBER, indent + ' ');
400
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLERFA', field.TLERFA, indent + ' ');
401
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLAUSW', field.TLAUSW, indent + ' ');
402
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLCONF', field.TLCONF, indent + ' ');
403
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLTEXT', field.TLTEXT, indent + ' ');
404
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLENTRY', field.TLENTRY, indent + ' ');
405
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLRADIOB', field.TLRADIOB, indent + ' ');
406
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLCHECKB', field.TLCHECKB, indent + ' ');
407
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLPUSHB', field.TLPUSHB, indent + ' ');
408
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLNAMETYP', field.TLNAMETYP, indent + ' ');
409
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLNAMEFLD', field.TLNAMEFLD, indent + ' ');
410
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLNAMEUEB', field.TLNAMEUEB, indent + ' ');
411
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLNAMESEL', field.TLNAMESEL, indent + ' ');
412
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLNAMETIT', field.TLNAMETIT, indent + ' ');
413
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TLFELDFIX', field.TLFELDFIX, indent + ' ');
414
+ xml = (0, baseGenerator_1.addXmlNumericField)(xml, 'TLFIXANZ', field.TLFIXANZ, indent + ' ');
415
+ xml = (0, baseGenerator_1.addXmlNumericField)(xml, 'TLFELDNR', field.TLFELDNR, indent + ' ');
416
+ xml = (0, baseGenerator_1.addXmlNumericField)(xml, 'TLFELDNR00', field.TLFELDNR00, indent + ' ');
417
+ xml = (0, baseGenerator_1.addXmlNumericField)(xml, 'TLFELDSPBR', field.TLFELDSPBR, indent + ' ');
418
+ // 分割面板(SP*系列)
419
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'SPCELL1', field.SPCELL1, indent + ' ');
420
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'SPCELL2', field.SPCELL2, indent + ' ');
421
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'SPSPLITTHORIZ', field.SPSPLITTHORIZ, indent + ' ');
422
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'SPSPLITTVERT', field.SPSPLITTVERT, indent + ' ');
423
+ xml = (0, baseGenerator_1.addXmlNumericField)(xml, 'SPSASH', field.SPSASH, indent + ' ');
424
+ // 输出样式
425
+ xml = (0, baseGenerator_1.addXmlNumericField)(xml, 'OUTPUTSTYLE', field.OUTPUTSTYLE, indent + ' ');
426
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'SIGN', field.SIGN, indent + ' ');
427
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'NOINPUTHISTORY', field.NOINPUTHISTORY, indent + ' ');
428
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'AMPMENABLED', field.AMPMENABLED, indent + ' ');
429
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'EXTEND', field.EXTEND, indent + ' ');
430
+ // 层次结构
431
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'HIERARCHY', field.HIERARCHY, indent + ' ');
432
+ // 图标(ICON*系列)
433
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'ICON', field.ICON, indent + ' ');
434
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'ICON_N', field.ICON_N, indent + ' ');
435
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'ICON_T', field.ICON_T, indent + ' ');
436
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'ICON_L', field.ICON_L, indent + ' ');
437
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'ICON_Q', field.ICON_Q, indent + ' ');
438
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'ICON_B', field.ICON_B, indent + ' ');
439
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'ICON_X', field.ICON_X, indent + ' ');
440
+ // 字段属性
441
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FLDP', field.FLDP, indent + ' ');
442
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FLDN', field.FLDN, indent + ' ');
443
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'PROG', field.PROG, indent + ' ');
444
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'DNUM', field.DNUM, indent + ' ');
445
+ // 事件处理
446
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'PBO_F', field.PBO_F, indent + ' ');
447
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'PBO_L', field.PBO_L, indent + ' ');
448
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'PAI_F', field.PAI_F, indent + ' ');
449
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'PAI_L', field.PAI_L, indent + ' ');
450
+ // 扩展属性(EX*系列)
451
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'EXTEXT', field.EXTEXT, indent + ' ');
452
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'EXENTRY', field.EXENTRY, indent + ' ');
453
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'EXRADIO', field.EXRADIO, indent + ' ');
454
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'EXCHECK', field.EXCHECK, indent + ' ');
455
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'EXPUSH', field.EXPUSH, indent + ' ');
456
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'EXFRAME', field.EXFRAME, indent + ' ');
457
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'EXTABCTRL', field.EXTABCTRL, indent + ' ');
458
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'EXCUSTOM', field.EXCUSTOM, indent + ' ');
459
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'EXTSTRCTRL', field.EXTSTRCTRL, indent + ' ');
460
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'EXSUBSCR', field.EXSUBSCR, indent + ' ');
461
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'EXSPLITTER', field.EXSPLITTER, indent + ' ');
462
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'EXSHOW', field.EXSHOW, indent + ' ');
463
+ // 消息
464
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'MSG_LINE1', field.MSG_LINE1, indent + ' ');
465
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'MSG_LINE2', field.MSG_LINE2, indent + ' ');
466
+ // 属性(PROP*系列)
467
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'PROPSHOW', field.PROPSHOW, indent + ' ');
468
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'PROPNAME', field.PROPNAME, indent + ' ');
469
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'PROPBOOL', field.PROPBOOL, indent + ' ');
470
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'PROPSTRING', field.PROPSTRING, indent + ' ');
471
+ // 表格控件高度
472
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'TSVHEIGHT', field.TSVHEIGHT, indent + ' ');
473
+ xml += `${indent} </RPY_DYFATC>\n`;
474
+ }
475
+ xml += `${indent}</FIELDS>\n`;
476
+ return xml;
477
+ }
478
+ // ============================================================
479
+ // 流程逻辑处理
480
+ // ============================================================
481
+ /**
482
+ * 生成流程逻辑XML
483
+ * 遵循RPY_DYFLOW结构定义
484
+ *
485
+ * 流程逻辑包含:
486
+ * - PROCESS BEFORE OUTPUT (PBO): 屏幕输出前处理
487
+ * - PROCESS AFTER INPUT (PAI): 屏幕输入后处理
488
+ * - MODULE调用
489
+ * - CALL SUBSCREEN
490
+ * - 空行和注释
491
+ */
492
+ function generateScreenFlowLogicXml(flowLogic, indent = ' ') {
493
+ if (!flowLogic || flowLogic.length === 0) {
494
+ return '';
495
+ }
496
+ let xml = `${indent}<FLOW_LOGIC>\n`;
497
+ for (const flow of flowLogic) {
498
+ xml += `${indent} <RPY_DYFLOW>\n`;
499
+ // CODE字段可以为空(空行在屏幕流程中很常见)
500
+ const codeContent = (flow.CODE !== undefined && flow.CODE !== null) ? String(flow.CODE) : '';
501
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'LINE', codeContent, indent + ' ');
502
+ xml += `${indent} </RPY_DYFLOW>\n`;
503
+ }
504
+ xml += `${indent}</FLOW_LOGIC>\n`;
505
+ return xml;
506
+ }
507
+ // ============================================================
508
+ // 程序目录处理
509
+ // ============================================================
510
+ /**
511
+ * 生成程序目录XML
512
+ * PROGDIR节点包含程序的基本信息
513
+ */
514
+ function generateProgramDirectoryXml(progdir) {
515
+ let xml = ' <PROGDIR>\n';
516
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'NAME', progdir.NAME, ' ');
517
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'SUBC', progdir.SUBC || CONSTANTS.DEFAULT_PROGRAM_SUBC, ' ');
518
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'RLOAD', progdir.RLOAD || CONSTANTS.DEFAULT_RLOAD, ' ');
519
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'FIXPT', progdir.FIXPT || CONSTANTS.DEFAULT_FIXPT, ' ');
520
+ xml = (0, baseGenerator_1.addXmlField)(xml, 'UCCHECK', progdir.UCCHECK || CONSTANTS.DEFAULT_UCCHECK, ' ');
521
+ xml += ' </PROGDIR>\n';
522
+ return xml;
523
+ }
524
+ /**
525
+ * 生成多屏幕XML内容
526
+ * 支持一个文件包含多个屏幕定义,符合abapGit标准格式
527
+ *
528
+ * XML结构:
529
+ * <?xml version="1.0" encoding="utf-8"?>
530
+ * <abapGit>
531
+ * <asx:abap>
532
+ * <asx:values>
533
+ * <PROGDIR>...</PROGDIR> (可选)
534
+ * <DYNPROS>
535
+ * <item>...</item> (屏幕1)
536
+ * <item>...</item> (屏幕2)
537
+ * ...
538
+ * </DYNPROS>
539
+ * </asx:values>
540
+ * </asx:abap>
541
+ * </abapGit>
542
+ */
543
+ function generateMultipleScreensXmlContent(screens, programName, programType, includePROGDIR = true) {
544
+ let xml = `${CONSTANTS.XML_VERSION}\n`;
545
+ xml += `<abapGit version="${CONSTANTS.ABAPGIT_VERSION}" serializer="${CONSTANTS.ABAPGIT_SERIALIZER}" serializer_version="${CONSTANTS.ABAPGIT_VERSION}">\n`;
546
+ xml += ' <asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">\n';
547
+ xml += ' <asx:values>\n';
548
+ // 程序目录信息(可选,但推荐包含)
549
+ if (includePROGDIR) {
550
+ const progdir = {
551
+ NAME: programName,
552
+ SUBC: programType || CONSTANTS.DEFAULT_PROGRAM_SUBC,
553
+ RLOAD: CONSTANTS.DEFAULT_RLOAD,
554
+ FIXPT: CONSTANTS.DEFAULT_FIXPT,
555
+ UCCHECK: CONSTANTS.DEFAULT_UCCHECK
556
+ };
557
+ xml += generateProgramDirectoryXml(progdir);
558
+ }
559
+ // 多屏幕定义
560
+ xml += ' <DYNPROS>\n';
561
+ for (const screenData of screens) {
562
+ xml += ' <item>\n';
563
+ // 屏幕头部 - 使用统一函数
564
+ const effectiveObjectName = screenData.programName || programName;
565
+ const screenHeader = getScreenHeader(screenData.screenDesc, screenData.header, effectiveObjectName, screenData.screenNumber);
566
+ xml += generateScreenHeaderXml(screenHeader, ' ');
567
+ // 容器定义
568
+ const screenContainers = (screenData.containers && screenData.containers.length > 0)
569
+ ? screenData.containers
570
+ : [{ TYPE: CONSTANTS.DEFAULT_CONTAINER_TYPE, NAME: CONSTANTS.DEFAULT_CONTAINER_NAME }];
571
+ xml += generateScreenContainersXml(screenContainers, ' ');
572
+ // 字段定义(可选)
573
+ if (screenData.fields && screenData.fields.length > 0) {
574
+ xml += generateScreenFieldsXml(screenData.fields, effectiveObjectName, ' ');
575
+ }
576
+ // 流程逻辑(可选)
577
+ if (screenData.flowLogic && screenData.flowLogic.length > 0) {
578
+ xml += generateScreenFlowLogicXml(screenData.flowLogic, ' ');
579
+ }
580
+ xml += ' </item>\n';
581
+ }
582
+ xml += ' </DYNPROS>\n';
583
+ xml += ' </asx:values>\n';
584
+ xml += ' </asx:abap>\n';
585
+ xml += '</abapGit>\n';
586
+ return xml;
587
+ }
588
+ // ============================================================
589
+ // 屏幕缓存管理
590
+ // ============================================================
591
+ /**
592
+ * 屏幕缓存管理器(多实例模式)
593
+ * 用于收集同一程序的多个屏幕,最后统一生成一个文件
594
+ *
595
+ * 优化说明:
596
+ * 1. 使用 Map<string, Map<string, ScreenData>> 结构提升查找性能(O(1))
597
+ * 2. 支持多个下载进程,每个进程创建独立的 ScreenCache 实例
598
+ * 3. 不再使用单例模式,避免多进程数据混乱
599
+ */
600
+ class ScreenCache {
601
+ // 优化:使用嵌套 Map 结构 Map<programName, Map<screenNumber, ScreenData>>
602
+ cache = new Map();
603
+ constructor() { }
604
+ /**
605
+ * 添加屏幕到缓存(只收集数据,不涉及文件生成)
606
+ * O(1) 时间复杂度的重复检测,如果屏幕号已存在则跳过
607
+ */
608
+ addScreen(programName, screenData) {
609
+ // 初始化程序的屏幕 Map
610
+ if (!this.cache.has(programName)) {
611
+ this.cache.set(programName, new Map());
612
+ }
613
+ const screenMap = this.cache.get(programName);
614
+ const screenNumber = screenData.screenNumber;
615
+ // O(1) 时间复杂度检查重复
616
+ if (screenMap.has(screenNumber)) {
617
+ console.warn(`[screenGenerator] 屏幕号 ${screenNumber} 已存在于程序 ${programName} 中,跳过重复添加`);
618
+ return;
619
+ }
620
+ // 添加新屏幕
621
+ screenMap.set(screenNumber, screenData);
622
+ console.error(`[screenGenerator] 缓存屏幕 ${screenNumber},当前共 ${screenMap.size} 个屏幕`);
623
+ }
624
+ /**
625
+ * 获取缓存的屏幕数据
626
+ */
627
+ getScreens(objectName) {
628
+ const screenMap = this.cache.get(objectName);
629
+ if (!screenMap) {
630
+ return undefined;
631
+ }
632
+ // 将 Map 转换为数组并按屏幕号排序
633
+ return Array.from(screenMap.values()).sort((a, b) => a.screenNumber.localeCompare(b.screenNumber));
634
+ }
635
+ /**
636
+ * 生成指定程序的所有屏幕XML
637
+ */
638
+ async generateForProgram(programName, downloadRootPath, resultDownloadedFiles) {
639
+ const screenMap = this.cache.get(programName);
640
+ if (!screenMap || screenMap.size === 0) {
641
+ console.warn(`[screenGenerator] 没有找到程序 ${programName} 的屏幕数据`);
642
+ return;
643
+ }
644
+ // 将 Map 转换为排序后的数组
645
+ const screens = Array.from(screenMap.values()).sort((a, b) => a.screenNumber.localeCompare(b.screenNumber));
646
+ console.error(`[screenGenerator] 开始生成程序 ${programName} 的多屏幕XML文件(共 ${screens.length} 个屏幕)...`);
647
+ await generateMultipleScreensXml(screens, programName, downloadRootPath, resultDownloadedFiles, true // 包含PROGDIR
648
+ );
649
+ // 生成后清除缓存
650
+ this.cache.delete(programName);
651
+ }
652
+ /**
653
+ * 生成所有缓存的屏幕XML
654
+ */
655
+ async generateAll(downloadRootPath, resultDownloadedFiles) {
656
+ const programNames = Array.from(this.cache.keys());
657
+ console.error(`[screenGenerator] 开始批量生成 ${programNames.length} 个程序的屏幕XML文件...`);
658
+ for (const programName of programNames) {
659
+ await this.generateForProgram(programName, downloadRootPath, resultDownloadedFiles);
660
+ }
661
+ }
662
+ /**
663
+ * 清除指定程序的缓存
664
+ */
665
+ clear(programName) {
666
+ if (programName) {
667
+ this.cache.delete(programName);
668
+ }
669
+ else {
670
+ this.cache.clear();
671
+ }
672
+ }
673
+ /**
674
+ * 获取缓存状态
675
+ */
676
+ getStatus() {
677
+ return Array.from(this.cache.entries()).map(([programName, screenMap]) => ({
678
+ programName,
679
+ screenCount: screenMap.size
680
+ }));
681
+ }
682
+ }
683
+ exports.ScreenCache = ScreenCache;
684
+ /**
685
+ * 添加原始屏幕数据到缓存(简化版)
686
+ * 直接处理从 SAP 返回的原始数据,自动按屏幕号分组
687
+ *
688
+ * 【数据收集阶段】只负责收集和分组数据,不涉及文件生成
689
+ *
690
+ * @param cache ScreenCache 实例(每个下载进程使用独立实例)
691
+ * @param programName 程序名(PROGNAME)
692
+ * @param fields 屏幕字段数组(ISCREENFIELD,可选)
693
+ * @param flowLogic 流程逻辑数组(ISCREENFLOW,可选)
694
+ * @param screenDescs 屏幕描述数组(ISCREENDESC,可选)
695
+ */
696
+ function addScreenDataToCache(cache, programName, fields, flowLogic, screenDescs) {
697
+ // 按屏幕号分组数据
698
+ const screenDataMap = new Map();
699
+ // 处理字段
700
+ if (fields && fields.length > 0) {
701
+ for (const field of fields) {
702
+ const screenNumber = field.SCREEN;
703
+ if (!screenNumber)
704
+ continue;
705
+ if (!screenDataMap.has(screenNumber)) {
706
+ screenDataMap.set(screenNumber, {});
707
+ }
708
+ const data = screenDataMap.get(screenNumber);
709
+ if (!data.fields) {
710
+ data.fields = [];
711
+ }
712
+ data.fields.push(field);
713
+ }
714
+ }
715
+ // 处理流程逻辑
716
+ if (flowLogic && flowLogic.length > 0) {
717
+ for (const flow of flowLogic) {
718
+ const screenNumber = flow.SCREEN;
719
+ if (!screenNumber)
720
+ continue;
721
+ if (!screenDataMap.has(screenNumber)) {
722
+ screenDataMap.set(screenNumber, {});
723
+ }
724
+ const data = screenDataMap.get(screenNumber);
725
+ if (!data.flowLogic) {
726
+ data.flowLogic = [];
727
+ }
728
+ data.flowLogic.push(flow);
729
+ }
730
+ }
731
+ // 处理屏幕描述
732
+ if (screenDescs && screenDescs.length > 0) {
733
+ for (const desc of screenDescs) {
734
+ const screenNumber = desc.SCREEN;
735
+ if (!screenNumber)
736
+ continue;
737
+ if (!screenDataMap.has(screenNumber)) {
738
+ screenDataMap.set(screenNumber, {});
739
+ }
740
+ const data = screenDataMap.get(screenNumber);
741
+ data.screenDesc = desc;
742
+ }
743
+ }
744
+ // 如果没有任何屏幕数据,直接返回
745
+ if (screenDataMap.size === 0) {
746
+ return;
747
+ }
748
+ console.error(`[screenGenerator] 为对象 ${programName} 添加 ${screenDataMap.size} 个屏幕到缓存`);
749
+ // 将每个屏幕添加到缓存
750
+ for (const [screenNumber, data] of screenDataMap) {
751
+ const screenData = {
752
+ screenNumber,
753
+ programName,
754
+ fields: data.fields,
755
+ flowLogic: data.flowLogic,
756
+ screenDesc: data.screenDesc
757
+ };
758
+ cache.addScreen(programName, screenData);
759
+ }
760
+ }
761
+ /**
762
+ * 完成屏幕收集,批量生成所有缓存的屏幕XML文件
763
+ *
764
+ * 【文件生成阶段】在处理完所有屏幕数据收集后调用此函数
765
+ *
766
+ * @param cache ScreenCache 实例(每个下载进程使用独立实例)
767
+ * @param downloadRootPath 下载根路径
768
+ * @param resultDownloadedFiles 下载结果记录数组
769
+ */
770
+ async function finalizeScreenGeneration(cache, downloadRootPath, resultDownloadedFiles) {
771
+ const status = cache.getStatus();
772
+ if (status.length === 0) {
773
+ console.error('[screenGenerator] 没有需要生成的屏幕数据');
774
+ return;
775
+ }
776
+ console.error('[screenGenerator] ========================================');
777
+ console.error('[screenGenerator] 开始批量生成屏幕XML文件');
778
+ console.error(`[screenGenerator] 共 ${status.length} 个程序:`);
779
+ for (const { programName, screenCount } of status) {
780
+ console.error(`[screenGenerator] - ${programName}: ${screenCount} 个屏幕`);
781
+ }
782
+ console.error('[screenGenerator] ========================================');
783
+ await cache.generateAll(downloadRootPath, resultDownloadedFiles);
784
+ console.error('[screenGenerator] ========================================');
785
+ console.error('[screenGenerator] 屏幕XML文件生成完成');
786
+ console.error('[screenGenerator] ========================================');
787
+ }
788
+ /**
789
+ * 生成多屏幕XML文件
790
+ * 将多个屏幕定义整合到一个文件中,符合abapGit标准格式
791
+ *
792
+ * @param screens 屏幕数据数组
793
+ * @param programName 程序名称
794
+ * @param programType 程序类型(M=模块池等)
795
+ * @param downloadRootPath 下载根路径
796
+ * @param resultDownloadedFiles 下载结果记录数组
797
+ * @param includePROGDIR 是否包含PROGDIR节点(默认true)
798
+ */
799
+ async function generateMultipleScreensXml(screens, programName, downloadRootPath, resultDownloadedFiles, includePROGDIR = true, programType = CONSTANTS.DEFAULT_PROGRAM_SUBC) {
800
+ console.error(`[screenGenerator] 开始生成程序 ${programName} 的多屏幕XML文件...`);
801
+ if (!screens || screens.length === 0) {
802
+ console.warn('[screenGenerator] 没有屏幕数据需要生成');
803
+ return;
804
+ }
805
+ // 验证所有屏幕数据
806
+ let validScreenCount = 0;
807
+ let totalWarnings = 0;
808
+ for (const screenData of screens) {
809
+ const validation = validateScreenData(screenData);
810
+ if (validation.warnings && validation.warnings.length > 0) {
811
+ totalWarnings += validation.warnings.length;
812
+ for (const warning of validation.warnings) {
813
+ console.warn(`[screenGenerator] 屏幕 ${screenData.screenNumber} 警告: ${warning}`);
814
+ }
815
+ }
816
+ if (!validation.valid) {
817
+ console.error(`[screenGenerator] 屏幕 ${screenData.screenNumber} 验证失败: ${validation.errors.join(', ')}`);
818
+ }
819
+ else {
820
+ validScreenCount++;
821
+ }
822
+ }
823
+ console.error(`[screenGenerator] 屏幕验证完成: ${validScreenCount}/${screens.length} 个有效, ${totalWarnings} 个警告`);
824
+ const safeProgramName = programName.replace(/[/\\]/g, '_');
825
+ const baseDir = (0, path_1.join)(downloadRootPath, 'screens');
826
+ if (!(0, fs_1.existsSync)(baseDir)) {
827
+ (0, fs_1.mkdirSync)(baseDir, { recursive: true });
828
+ }
829
+ // 生成多屏幕XML内容
830
+ const xmlContent = generateMultipleScreensXmlContent(screens, programName, programType, includePROGDIR);
831
+ // 验证生成的XML
832
+ const xmlValidation = (0, baseGenerator_1.validateXmlContent)(xmlContent);
833
+ if (!xmlValidation.valid) {
834
+ console.warn(`[screenGenerator] XML格式验证警告: ${xmlValidation.errors.join(', ')}`);
835
+ }
836
+ // 文件命名(遵循abapGit命名规范)
837
+ const xmlFileName = `${safeProgramName}${CONSTANTS.EXT_PROG}`;
838
+ const xmlFilePath = (0, path_1.join)(baseDir, xmlFileName);
839
+ (0, fs_1.writeFileSync)(xmlFilePath, xmlContent, 'utf-8');
840
+ // 计算统计信息
841
+ const totalFields = screens.reduce((sum, s) => sum + (s.fields?.length || 0), 0);
842
+ const totalFlowLogic = screens.reduce((sum, s) => sum + (s.flowLogic?.length || 0), 0);
843
+ console.error(`[screenGenerator] ✓ 成功导出程序 ${programName} 的多屏幕文件`);
844
+ console.error(`[screenGenerator] 文件路径: ${xmlFilePath}`);
845
+ console.error(`[screenGenerator] 统计: ${screens.length} 个屏幕, ${totalFields} 个字段, ${totalFlowLogic} 行流程逻辑`);
846
+ // 记录下载统计
847
+ resultDownloadedFiles.push({
848
+ objectName: programName,
849
+ objectType: 'SCREENS',
850
+ filePath: xmlFilePath,
851
+ success: true,
852
+ statistics: {
853
+ screenCount: screens.length,
854
+ totalFields: totalFields,
855
+ totalFlowLogic: totalFlowLogic,
856
+ includePROGDIR: includePROGDIR
857
+ }
858
+ });
859
+ }