@bimatrix-aud-platform/aud_mcp_server 1.1.56 → 1.1.63
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.
- package/dist/generators/compact.d.ts +16 -0
- package/dist/generators/compact.js +299 -0
- package/dist/generators/defaults.js +2 -2
- package/dist/generators/fixer.js +100 -73
- package/dist/generators/olap-field.js +1 -0
- package/dist/index.js +81 -14
- package/dist/resources/encrypted-commands.d.ts +3 -3
- package/dist/resources/encrypted-commands.js +3 -3
- package/dist/utils/aud-api-client.js +4 -2
- package/dist/utils/report-publisher.d.ts +1 -0
- package/dist/utils/report-publisher.js +6 -2
- package/package.json +1 -1
- package/schemas/design.interface.ts +406 -0
- package/schemas/design.schema.json +5907 -0
- package/dist/generators/report-template.d.ts +0 -43
- package/dist/generators/report-template.js +0 -723
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* compact 모드 유틸리티
|
|
3
|
+
*
|
|
4
|
+
* 생성된 MTSD JSON에서 기본값과 동일한 속성을 제거하여
|
|
5
|
+
* 간소화된 .design.json 형태로 변환합니다.
|
|
6
|
+
*/
|
|
7
|
+
/** Element JSON에서 기본값 속성을 제거 */
|
|
8
|
+
export declare function compactElement(element: Record<string, any>): Record<string, any>;
|
|
9
|
+
/** GridColumn JSON에서 기본값 속성을 제거 */
|
|
10
|
+
export declare function compactGridColumn(column: Record<string, any>): Record<string, any>;
|
|
11
|
+
/** DataSource JSON에서 기본값 속성을 제거 */
|
|
12
|
+
export declare function compactDataSource(ds: Record<string, any>): Record<string, any>;
|
|
13
|
+
/** OlapField JSON에서 기본값 속성을 제거 */
|
|
14
|
+
export declare function compactOlapField(field: Record<string, any>): Record<string, any>;
|
|
15
|
+
/** OlapField 배열 전체를 compact 처리 */
|
|
16
|
+
export declare function compactOlapFields(fields: Record<string, any>[]): Record<string, any>[];
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* compact 모드 유틸리티
|
|
3
|
+
*
|
|
4
|
+
* 생성된 MTSD JSON에서 기본값과 동일한 속성을 제거하여
|
|
5
|
+
* 간소화된 .design.json 형태로 변환합니다.
|
|
6
|
+
*/
|
|
7
|
+
import { defaultStyle, defaultDocking, defaultValidator, ELEMENT_DEFAULTS_MAP, } from "./defaults.js";
|
|
8
|
+
// ============================================
|
|
9
|
+
// 공통 유틸
|
|
10
|
+
// ============================================
|
|
11
|
+
/** 깊은 비교 (키 순서 무관) */
|
|
12
|
+
function deepEqual(a, b) {
|
|
13
|
+
if (a === b)
|
|
14
|
+
return true;
|
|
15
|
+
if (a == null || b == null)
|
|
16
|
+
return false;
|
|
17
|
+
if (typeof a !== typeof b)
|
|
18
|
+
return false;
|
|
19
|
+
if (typeof a !== "object")
|
|
20
|
+
return a === b;
|
|
21
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
22
|
+
return false;
|
|
23
|
+
if (Array.isArray(a)) {
|
|
24
|
+
if (a.length !== b.length)
|
|
25
|
+
return false;
|
|
26
|
+
for (let i = 0; i < a.length; i++) {
|
|
27
|
+
if (!deepEqual(a[i], b[i]))
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
const keysA = Object.keys(a);
|
|
33
|
+
const keysB = Object.keys(b);
|
|
34
|
+
if (keysA.length !== keysB.length)
|
|
35
|
+
return false;
|
|
36
|
+
for (const key of keysA) {
|
|
37
|
+
if (!b.hasOwnProperty(key))
|
|
38
|
+
return false;
|
|
39
|
+
if (!deepEqual(a[key], b[key]))
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
/** 객체에서 기본값과 동일한 속성을 제거. 빈 객체가 되면 undefined 반환 */
|
|
45
|
+
function stripMatchingDefaults(obj, defaults, keepKeys) {
|
|
46
|
+
const result = {};
|
|
47
|
+
let hasValue = false;
|
|
48
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
49
|
+
if (keepKeys && keepKeys.has(key)) {
|
|
50
|
+
result[key] = value;
|
|
51
|
+
hasValue = true;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (key in defaults && deepEqual(value, defaults[key])) {
|
|
55
|
+
continue; // 기본값과 동일하면 제거
|
|
56
|
+
}
|
|
57
|
+
result[key] = value;
|
|
58
|
+
hasValue = true;
|
|
59
|
+
}
|
|
60
|
+
return hasValue ? result : undefined;
|
|
61
|
+
}
|
|
62
|
+
// ============================================
|
|
63
|
+
// Element compact
|
|
64
|
+
// ============================================
|
|
65
|
+
/** Element JSON에서 기본값 속성을 제거 */
|
|
66
|
+
export function compactElement(element) {
|
|
67
|
+
const result = {};
|
|
68
|
+
const type = element.Type;
|
|
69
|
+
// 필수 속성 유지
|
|
70
|
+
result.Type = type;
|
|
71
|
+
result.Id = element.Id;
|
|
72
|
+
result.Name = element.Name;
|
|
73
|
+
// Position: Left/Top/Width/Height만 유지, 기본 Docking/ZIndex/TabIndex 제거
|
|
74
|
+
if (element.Position) {
|
|
75
|
+
const pos = element.Position;
|
|
76
|
+
const compactPos = {
|
|
77
|
+
Left: pos.Left,
|
|
78
|
+
Top: pos.Top,
|
|
79
|
+
Width: pos.Width,
|
|
80
|
+
Height: pos.Height,
|
|
81
|
+
};
|
|
82
|
+
// ZIndex: 기본값(0 또는 1)이 아닌 경우만 유지
|
|
83
|
+
if (pos.ZIndex !== undefined && pos.ZIndex !== 0 && pos.ZIndex !== 1) {
|
|
84
|
+
compactPos.ZIndex = pos.ZIndex;
|
|
85
|
+
}
|
|
86
|
+
// TabIndex: 0이 아닌 경우만 유지
|
|
87
|
+
if (pos.TabIndex !== undefined && pos.TabIndex !== 0) {
|
|
88
|
+
compactPos.TabIndex = pos.TabIndex;
|
|
89
|
+
}
|
|
90
|
+
// Docking: 기본값이 아닌 경우만 유지
|
|
91
|
+
if (pos.Docking && !deepEqual(pos.Docking, defaultDocking())) {
|
|
92
|
+
compactPos.Docking = pos.Docking;
|
|
93
|
+
}
|
|
94
|
+
result.Position = compactPos;
|
|
95
|
+
}
|
|
96
|
+
// Style: Type=0(Skin)이면 Style 전체 제거, Type!=0이면 기본값 비교
|
|
97
|
+
if (element.Style) {
|
|
98
|
+
const style = element.Style;
|
|
99
|
+
const styleType = style.Type;
|
|
100
|
+
if (styleType === undefined || styleType === 0) {
|
|
101
|
+
// Skin 모드: BoxStyle이 있으면 BoxStyle만 유지
|
|
102
|
+
if (style.BoxStyle && style.BoxStyle !== "") {
|
|
103
|
+
result.Style = { Type: 0, BoxStyle: style.BoxStyle };
|
|
104
|
+
}
|
|
105
|
+
// BoxStyle도 없으면 Style 전체 생략
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// Type!=0 (커스텀 스타일): 기본값과 비교하여 제거
|
|
109
|
+
const defStyle = defaultStyle();
|
|
110
|
+
const compactStyle = { Type: styleType };
|
|
111
|
+
if (style.BoxStyle !== undefined && style.BoxStyle !== "") {
|
|
112
|
+
compactStyle.BoxStyle = style.BoxStyle;
|
|
113
|
+
}
|
|
114
|
+
if (style.Background && !deepEqual(style.Background, defStyle.Background)) {
|
|
115
|
+
compactStyle.Background = style.Background;
|
|
116
|
+
}
|
|
117
|
+
if (style.Border && !deepEqual(style.Border, defStyle.Border)) {
|
|
118
|
+
compactStyle.Border = style.Border;
|
|
119
|
+
}
|
|
120
|
+
if (style.Font && !deepEqual(style.Font, defStyle.Font)) {
|
|
121
|
+
compactStyle.Font = style.Font;
|
|
122
|
+
}
|
|
123
|
+
result.Style = compactStyle;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Visible: true(기본값)이면 제거
|
|
127
|
+
if (element.Visible === false) {
|
|
128
|
+
result.Visible = false;
|
|
129
|
+
}
|
|
130
|
+
// Enabled: true(기본값)이면 제거
|
|
131
|
+
if (element.Enabled === false) {
|
|
132
|
+
result.Enabled = false;
|
|
133
|
+
}
|
|
134
|
+
// TabStop: true(기본값)이면 제거
|
|
135
|
+
if (element.TabStop === false) {
|
|
136
|
+
result.TabStop = false;
|
|
137
|
+
}
|
|
138
|
+
// DataSource: 빈 문자열이면 제거
|
|
139
|
+
if (element.DataSource && element.DataSource !== "") {
|
|
140
|
+
result.DataSource = element.DataSource;
|
|
141
|
+
}
|
|
142
|
+
// 타입별 기본값 제거
|
|
143
|
+
const defaultsFactory = ELEMENT_DEFAULTS_MAP[type];
|
|
144
|
+
const typeDefaults = defaultsFactory ? defaultsFactory() : {};
|
|
145
|
+
// 처리 완료 키
|
|
146
|
+
const processedKeys = new Set([
|
|
147
|
+
"Type", "Id", "Name", "Position", "Style",
|
|
148
|
+
"Visible", "Enabled", "TabStop", "DataSource",
|
|
149
|
+
]);
|
|
150
|
+
// 나머지 속성: 타입 기본값과 다른 것만 유지
|
|
151
|
+
for (const [key, value] of Object.entries(element)) {
|
|
152
|
+
if (processedKeys.has(key))
|
|
153
|
+
continue;
|
|
154
|
+
if (key in typeDefaults && deepEqual(value, typeDefaults[key]))
|
|
155
|
+
continue;
|
|
156
|
+
result[key] = value;
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
// ============================================
|
|
161
|
+
// GridColumn compact
|
|
162
|
+
// ============================================
|
|
163
|
+
/** GridColumn JSON에서 기본값 속성을 제거 */
|
|
164
|
+
export function compactGridColumn(column) {
|
|
165
|
+
const result = {};
|
|
166
|
+
// Name 필수
|
|
167
|
+
result.Name = column.Name;
|
|
168
|
+
// Caption: Name과 같으면 제거
|
|
169
|
+
if (column.Caption && column.Caption !== column.Name) {
|
|
170
|
+
result.Caption = column.Caption;
|
|
171
|
+
}
|
|
172
|
+
// Width: 100이면 제거
|
|
173
|
+
if (column.Width !== undefined && column.Width !== 100) {
|
|
174
|
+
result.Width = column.Width;
|
|
175
|
+
}
|
|
176
|
+
// Validator: 기본값이면 제거
|
|
177
|
+
if (column.Validator && !deepEqual(column.Validator, defaultValidator())) {
|
|
178
|
+
result.Validator = column.Validator;
|
|
179
|
+
}
|
|
180
|
+
// KeyType: 0이면 제거
|
|
181
|
+
if (column.KeyType !== undefined && column.KeyType !== 0) {
|
|
182
|
+
result.KeyType = column.KeyType;
|
|
183
|
+
}
|
|
184
|
+
// HeaderPosition: "center"이면 제거
|
|
185
|
+
if (column.HeaderPosition && column.HeaderPosition !== "center") {
|
|
186
|
+
result.HeaderPosition = column.HeaderPosition;
|
|
187
|
+
}
|
|
188
|
+
// ColumnType: 0이면 제거
|
|
189
|
+
if (column.ColumnType !== undefined && column.ColumnType !== 0) {
|
|
190
|
+
result.ColumnType = column.ColumnType;
|
|
191
|
+
}
|
|
192
|
+
// 나머지 속성은 그대로 유지
|
|
193
|
+
const processedKeys = new Set([
|
|
194
|
+
"Name", "Caption", "Width", "Validator", "KeyType",
|
|
195
|
+
"HeaderPosition", "ColumnType",
|
|
196
|
+
]);
|
|
197
|
+
for (const [key, value] of Object.entries(column)) {
|
|
198
|
+
if (processedKeys.has(key))
|
|
199
|
+
continue;
|
|
200
|
+
result[key] = value;
|
|
201
|
+
}
|
|
202
|
+
return result;
|
|
203
|
+
}
|
|
204
|
+
// ============================================
|
|
205
|
+
// DataSource compact
|
|
206
|
+
// ============================================
|
|
207
|
+
/** DataSource JSON에서 기본값 속성을 제거 */
|
|
208
|
+
export function compactDataSource(ds) {
|
|
209
|
+
const result = {};
|
|
210
|
+
// 필수 속성
|
|
211
|
+
result.Id = ds.Id;
|
|
212
|
+
result.Name = ds.Name;
|
|
213
|
+
// ConnectionCode: 빈 문자열이 아니면 유지
|
|
214
|
+
if (ds.ConnectionCode) {
|
|
215
|
+
result.ConnectionCode = ds.ConnectionCode;
|
|
216
|
+
}
|
|
217
|
+
// DSType: 0(기본값)이면 제거
|
|
218
|
+
if (ds.DSType !== undefined && ds.DSType !== 0) {
|
|
219
|
+
result.DSType = ds.DSType;
|
|
220
|
+
}
|
|
221
|
+
// SQL: 빈 문자열이면 제거
|
|
222
|
+
if (ds.SQL && ds.SQL !== "") {
|
|
223
|
+
result.SQL = ds.SQL;
|
|
224
|
+
}
|
|
225
|
+
// UseMeta/UseCache/Encrypted: false(기본값)이면 제거
|
|
226
|
+
if (ds.UseMeta === true)
|
|
227
|
+
result.UseMeta = true;
|
|
228
|
+
if (ds.UseCache === true)
|
|
229
|
+
result.UseCache = true;
|
|
230
|
+
if (ds.Encrypted === true)
|
|
231
|
+
result.Encrypted = true;
|
|
232
|
+
// Params: 빈 배열이면 제거
|
|
233
|
+
if (ds.Params && ds.Params.length > 0) {
|
|
234
|
+
result.Params = ds.Params;
|
|
235
|
+
}
|
|
236
|
+
// Columns: 빈 배열이면 제거
|
|
237
|
+
if (ds.Columns && ds.Columns.length > 0) {
|
|
238
|
+
result.Columns = ds.Columns;
|
|
239
|
+
}
|
|
240
|
+
// 나머지 pass-through
|
|
241
|
+
const processedKeys = new Set([
|
|
242
|
+
"Id", "Name", "ConnectionCode", "DSType", "SQL",
|
|
243
|
+
"UseMeta", "UseCache", "Encrypted", "Params", "Columns",
|
|
244
|
+
]);
|
|
245
|
+
for (const [key, value] of Object.entries(ds)) {
|
|
246
|
+
if (processedKeys.has(key))
|
|
247
|
+
continue;
|
|
248
|
+
result[key] = value;
|
|
249
|
+
}
|
|
250
|
+
return result;
|
|
251
|
+
}
|
|
252
|
+
// ============================================
|
|
253
|
+
// OlapField compact
|
|
254
|
+
// ============================================
|
|
255
|
+
/** OlapField JSON에서 기본값 속성을 제거 */
|
|
256
|
+
export function compactOlapField(field) {
|
|
257
|
+
const result = {};
|
|
258
|
+
// 필수: Key, Caption
|
|
259
|
+
result.Key = field.Key;
|
|
260
|
+
if (field.Caption && field.Caption !== field.Key) {
|
|
261
|
+
result.Caption = field.Caption;
|
|
262
|
+
}
|
|
263
|
+
// Category: 0(Dimension)이 기본, 명시적으로 유지
|
|
264
|
+
if (field.Category !== undefined) {
|
|
265
|
+
result.Category = field.Category;
|
|
266
|
+
}
|
|
267
|
+
// Area: 0(Hidden)이 아닌 경우 유지
|
|
268
|
+
if (field.Area !== undefined && field.Area !== 0) {
|
|
269
|
+
result.Area = field.Area;
|
|
270
|
+
}
|
|
271
|
+
// SummaryType: 0(None)이 아닌 경우 유지
|
|
272
|
+
if (field.SummaryType !== undefined && field.SummaryType !== 0) {
|
|
273
|
+
result.SummaryType = field.SummaryType;
|
|
274
|
+
}
|
|
275
|
+
// Format: 빈 문자열이 아닌 경우 유지
|
|
276
|
+
if (field.Format && field.Format !== "") {
|
|
277
|
+
result.Format = field.Format;
|
|
278
|
+
}
|
|
279
|
+
// Width: 100이 아닌 경우 유지
|
|
280
|
+
if (field.Width !== undefined && field.Width !== 100) {
|
|
281
|
+
result.Width = field.Width;
|
|
282
|
+
}
|
|
283
|
+
// SortType: 0(None)이 아닌 경우 유지
|
|
284
|
+
if (field.SortType !== undefined && field.SortType !== 0) {
|
|
285
|
+
result.SortType = field.SortType;
|
|
286
|
+
}
|
|
287
|
+
// DataType: 명시적으로 유지 (Dimension/Measure 구분에 중요)
|
|
288
|
+
if (field.DataType !== undefined) {
|
|
289
|
+
result.DataType = field.DataType;
|
|
290
|
+
}
|
|
291
|
+
// 나머지 boolean/기본값들은 제거
|
|
292
|
+
// 이하 속성들은 거의 항상 기본값이므로 compact에서 생략
|
|
293
|
+
// Visible, Expanded, MoveAble, SortAble, FilterAble, AllowFilter, etc.
|
|
294
|
+
return result;
|
|
295
|
+
}
|
|
296
|
+
/** OlapField 배열 전체를 compact 처리 */
|
|
297
|
+
export function compactOlapFields(fields) {
|
|
298
|
+
return fields.map(compactOlapField);
|
|
299
|
+
}
|
|
@@ -62,9 +62,9 @@ export function defaultStyle() {
|
|
|
62
62
|
export function defaultValidator() {
|
|
63
63
|
return {
|
|
64
64
|
ValidateType: 8, // enGridValidateType.None
|
|
65
|
-
UseGuideMessage:
|
|
65
|
+
UseGuideMessage: true,
|
|
66
66
|
GuideLanguageCode: "",
|
|
67
|
-
UseErrorMessage:
|
|
67
|
+
UseErrorMessage: true,
|
|
68
68
|
ErrorLanguageCode: "",
|
|
69
69
|
};
|
|
70
70
|
}
|
package/dist/generators/fixer.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync } from "fs";
|
|
2
|
+
import { basename } from "path";
|
|
2
3
|
import { randomBytes } from "crypto";
|
|
3
4
|
import { createDefaultOlapField, isNumericColumnType } from "./olap-field.js";
|
|
5
|
+
import { compactOlapField } from "./compact.js";
|
|
4
6
|
/**
|
|
5
7
|
* MTSD 파일을 읽고, 자동 보정 규칙을 적용한 뒤, 파일을 덮어씁니다.
|
|
6
8
|
*/
|
|
@@ -50,15 +52,17 @@ export function fixMtsd(filePath) {
|
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
}
|
|
55
|
+
// .design.json 여부 판별 (compact 형식 → 기본값 추가 생략)
|
|
56
|
+
const isDesignJson = basename(filePath) === ".design.json";
|
|
53
57
|
// 3. 보정 규칙 적용
|
|
54
58
|
// Rule 1: DataSource Name→Id 참조 보정
|
|
55
59
|
fixDataSourceReferences(doc, dsNameToId, dsIdSet, fixes);
|
|
56
60
|
// Rule 2: OlapGrid DataSource 기반 Fields 자동 생성
|
|
57
|
-
fixOlapGridFields(doc, datas, fixes);
|
|
61
|
+
fixOlapGridFields(doc, datas, fixes, isDesignJson);
|
|
58
62
|
// Rule 2-2: OlapGrid Fields 내 CreateType=1 (Measures) 필드 보정
|
|
59
63
|
fixOlapMeasuresField(doc, fixes);
|
|
60
64
|
// Rule 3: Enum/Range 값 범위 초과 보정
|
|
61
|
-
fixEnumAndRangeValues(doc, datas, fixes);
|
|
65
|
+
fixEnumAndRangeValues(doc, datas, fixes, isDesignJson);
|
|
62
66
|
// Rule 4: Tab > TabItems 내 ChildElements → Controls 노드 이름 보정
|
|
63
67
|
fixTabItemChildElements(doc, fixes);
|
|
64
68
|
// Rule 5: ServerScriptText Key 누락 보정
|
|
@@ -111,7 +115,7 @@ function fixDataSourceReferences(doc, dsNameToId, dsIdSet, fixes) {
|
|
|
111
115
|
}
|
|
112
116
|
// ---- Rule 2: OlapGrid DataSource 기반 Fields 자동 생성 ----
|
|
113
117
|
// createDefaultOlapField, isNumericColumnType는 olap-field.ts에서 import
|
|
114
|
-
function fixOlapGridFields(doc, datas, fixes) {
|
|
118
|
+
function fixOlapGridFields(doc, datas, fixes, isDesignJson) {
|
|
115
119
|
// DataSource Id → DataSource 객체 매핑
|
|
116
120
|
const dsById = new Map();
|
|
117
121
|
for (const ds of datas) {
|
|
@@ -193,11 +197,15 @@ function fixOlapGridFields(doc, datas, fixes) {
|
|
|
193
197
|
}
|
|
194
198
|
}
|
|
195
199
|
}
|
|
200
|
+
// .design.json이면 compact 처리 (기본값 속성 제거)
|
|
201
|
+
const finalFields = isDesignJson
|
|
202
|
+
? newFields.map(compactOlapField)
|
|
203
|
+
: newFields;
|
|
196
204
|
// iOLAPView 구조 생성/보정
|
|
197
205
|
if (!el.iOLAPView) {
|
|
198
206
|
el.iOLAPView = {};
|
|
199
207
|
}
|
|
200
|
-
el.iOLAPView.Fields =
|
|
208
|
+
el.iOLAPView.Fields = finalFields;
|
|
201
209
|
fixes.push(`[Rule2] ${path}: DataSource "${ds.Name || ds.Id}" 컬럼 기준 OlapGrid Fields ${newFields.length}개 자동 생성`);
|
|
202
210
|
});
|
|
203
211
|
}
|
|
@@ -366,7 +374,7 @@ function clampColorRGBA(obj, path, fixes) {
|
|
|
366
374
|
}
|
|
367
375
|
}
|
|
368
376
|
}
|
|
369
|
-
function fixEnumAndRangeValues(doc, datas, fixes) {
|
|
377
|
+
function fixEnumAndRangeValues(doc, datas, fixes, isDesignJson) {
|
|
370
378
|
// ── 1. ReportInfo ──
|
|
371
379
|
const ri = doc.ReportInfo;
|
|
372
380
|
if (ri) {
|
|
@@ -384,14 +392,16 @@ function fixEnumAndRangeValues(doc, datas, fixes) {
|
|
|
384
392
|
if (form.Style) {
|
|
385
393
|
const fPath = `Form("${form.Name}").Style`;
|
|
386
394
|
fixIntRange(form.Style, "Type", 0, 2, 0, fPath, fixes); // 기본값: 0(Skin)
|
|
387
|
-
// Background 빈 객체 보정
|
|
388
|
-
if (!
|
|
389
|
-
form.Style.Background
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
form.Style.Background.Color
|
|
394
|
-
|
|
395
|
+
// Background 빈 객체 보정 (.design.json에서는 생략 — compact 원칙)
|
|
396
|
+
if (!isDesignJson) {
|
|
397
|
+
if (!form.Style.Background || typeof form.Style.Background !== "object") {
|
|
398
|
+
form.Style.Background = { Color: { R: 255, G: 255, B: 255, A: 1 } };
|
|
399
|
+
fixes.push(`[Rule3] ${fPath}.Background: 누락 → 흰색 기본값 보정`);
|
|
400
|
+
}
|
|
401
|
+
else if (!form.Style.Background.Color) {
|
|
402
|
+
form.Style.Background.Color = { R: 255, G: 255, B: 255, A: 1 };
|
|
403
|
+
fixes.push(`[Rule3] ${fPath}.Background.Color: 누락 → 흰색 기본값 보정`);
|
|
404
|
+
}
|
|
395
405
|
}
|
|
396
406
|
}
|
|
397
407
|
}
|
|
@@ -400,26 +410,29 @@ function fixEnumAndRangeValues(doc, datas, fixes) {
|
|
|
400
410
|
const elements = form.Elements || [];
|
|
401
411
|
walkElements(elements, (el, path) => {
|
|
402
412
|
// ---- AutoRefresh / DoRefresh / DoExport 필수 속성 보정 ----
|
|
413
|
+
// .design.json에서는 기본값 추가를 생략 (compact 원칙)
|
|
403
414
|
const type = el.Type;
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
415
|
+
if (!isDesignJson) {
|
|
416
|
+
// AutoRefresh 대상: DataGrid, ComboBox, MultiComboBox, OlapGrid, Chart, PieChart, ScatterChart, PolygonChart, TreeGrid, iGrid, WebContainer, Tree
|
|
417
|
+
const hasAutoRefresh = ["DataGrid", "ComboBox", "MultiComboBox", "OlapGrid", "Chart", "PieChart", "ScatterChart", "PolygonChart", "TreeGrid", "iGrid", "WebContainer", "Tree"];
|
|
418
|
+
if (hasAutoRefresh.includes(type) && !("AutoRefresh" in el)) {
|
|
419
|
+
const autoDefault = type === "ComboBox" ? true : false;
|
|
420
|
+
el.AutoRefresh = autoDefault;
|
|
421
|
+
fixes.push(`[Rule3] ${path}.AutoRefresh: 누락 → ${autoDefault}`);
|
|
422
|
+
}
|
|
423
|
+
// DoRefresh 대상: DataGrid, ComboBox, OlapGrid, Chart, PieChart, ScatterChart, PolygonChart, TreeGrid, iGrid, UserComponent, WebContainer, Tree
|
|
424
|
+
const hasDoRefresh = ["DataGrid", "ComboBox", "OlapGrid", "Chart", "PieChart", "ScatterChart", "PolygonChart", "TreeGrid", "iGrid", "UserComponent", "WebContainer", "Tree"];
|
|
425
|
+
if (hasDoRefresh.includes(type) && !("DoRefresh" in el)) {
|
|
426
|
+
const isCombo = type === "ComboBox";
|
|
427
|
+
el.DoRefresh = isCombo ? false : true;
|
|
428
|
+
fixes.push(`[Rule3] ${path}.DoRefresh: 누락 → ${el.DoRefresh}`);
|
|
429
|
+
}
|
|
430
|
+
// DoExport 대상: DataGrid, OlapGrid, Chart, PieChart, ScatterChart, PolygonChart, TreeGrid, iGrid, UserComponent, Tab
|
|
431
|
+
const hasDoExport = ["DataGrid", "OlapGrid", "Chart", "PieChart", "ScatterChart", "PolygonChart", "TreeGrid", "iGrid", "UserComponent", "Tab"];
|
|
432
|
+
if (hasDoExport.includes(type) && !("DoExport" in el)) {
|
|
433
|
+
el.DoExport = true;
|
|
434
|
+
fixes.push(`[Rule3] ${path}.DoExport: 누락 → true`);
|
|
435
|
+
}
|
|
423
436
|
}
|
|
424
437
|
// ---- Style.Type: 커스텀 색상 설정 시 Type=2(Custom) 자동 보정 ----
|
|
425
438
|
// Control.ts UpdateStyle: Type=0(Skin)이면 커스텀 Background/Border/Font CSS 무시됨
|
|
@@ -487,45 +500,56 @@ function fixEnumAndRangeValues(doc, datas, fixes) {
|
|
|
487
500
|
}
|
|
488
501
|
// ---- Style.Background Color (빈 객체 {} 허용 안 됨) ----
|
|
489
502
|
if (el.Style) {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
503
|
+
// .design.json에서는 Background 기본값 추가 생략 (compact 원칙)
|
|
504
|
+
if (!isDesignJson) {
|
|
505
|
+
if (!el.Style.Background || typeof el.Style.Background !== "object") {
|
|
506
|
+
el.Style.Background = { Color: { R: 255, G: 255, B: 255, A: 1 } };
|
|
507
|
+
fixes.push(`[Rule3] ${path}.Style.Background: 누락 → 흰색 기본값 보정`);
|
|
508
|
+
}
|
|
509
|
+
else if (!el.Style.Background.Color) {
|
|
510
|
+
el.Style.Background.Color = { R: 255, G: 255, B: 255, A: 1 };
|
|
511
|
+
fixes.push(`[Rule3] ${path}.Style.Background.Color: 누락 → 흰색 기본값 보정`);
|
|
512
|
+
}
|
|
497
513
|
}
|
|
514
|
+
// 색상 범위 검증은 항상 수행 (존재하는 값의 유효성 체크)
|
|
498
515
|
const bg = el.Style.Background;
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
516
|
+
if (bg) {
|
|
517
|
+
clampColorRGBA(bg, `${path}.Style.Background`, fixes);
|
|
518
|
+
if (bg.Color)
|
|
519
|
+
clampColorRGBA(bg.Color, `${path}.Style.Background.Color`, fixes);
|
|
520
|
+
}
|
|
502
521
|
}
|
|
503
522
|
// ---- DataGrid: ColumnHeaderHeight / RowHeight / ShowHeader 필수 ----
|
|
504
523
|
if (el.Type === "DataGrid") {
|
|
505
|
-
if (!
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
524
|
+
if (!isDesignJson) {
|
|
525
|
+
if (!("ColumnHeaderHeight" in el)) {
|
|
526
|
+
el.ColumnHeaderHeight = 28;
|
|
527
|
+
fixes.push(`[Rule3] ${path}.ColumnHeaderHeight: 누락 → 28`);
|
|
528
|
+
}
|
|
529
|
+
if (!("RowHeight" in el)) {
|
|
530
|
+
el.RowHeight = 24;
|
|
531
|
+
fixes.push(`[Rule3] ${path}.RowHeight: 누락 → 24`);
|
|
532
|
+
}
|
|
533
|
+
if (!("ShowHeader" in el)) {
|
|
534
|
+
el.ShowHeader = 3;
|
|
535
|
+
fixes.push(`[Rule3] ${path}.ShowHeader: 누락 → 3 (Row&Column)`);
|
|
536
|
+
}
|
|
537
|
+
if (!("SelectRule" in el)) {
|
|
538
|
+
el.SelectRule = 2;
|
|
539
|
+
fixes.push(`[Rule3] ${path}.SelectRule: 누락 → 2 (SingleRange)`);
|
|
540
|
+
}
|
|
521
541
|
}
|
|
522
|
-
|
|
542
|
+
// 범위 검증은 항상 수행 (존재하는 값의 유효성 체크)
|
|
543
|
+
fixIntRange(el, "ShowHeader", 0, 3, 3, path, fixes);
|
|
544
|
+
fixIntRange(el, "SelectRule", 0, 3, 2, path, fixes);
|
|
523
545
|
}
|
|
524
546
|
// ---- TreeGrid: ToggleBtnSize 필수 ----
|
|
525
547
|
if (el.Type === "TreeGrid") {
|
|
526
|
-
if (!
|
|
527
|
-
|
|
528
|
-
|
|
548
|
+
if (!isDesignJson) {
|
|
549
|
+
if (!("ToggleBtnSize" in el)) {
|
|
550
|
+
el.ToggleBtnSize = 10;
|
|
551
|
+
fixes.push(`[Rule3] ${path}.ToggleBtnSize: 누락 → 10`);
|
|
552
|
+
}
|
|
529
553
|
}
|
|
530
554
|
}
|
|
531
555
|
// ---- DataGrid / TreeGrid Columns ----
|
|
@@ -544,7 +568,7 @@ function fixEnumAndRangeValues(doc, datas, fixes) {
|
|
|
544
568
|
}
|
|
545
569
|
}
|
|
546
570
|
// ---- FileUploadButton: Value / Cursor 필수 ----
|
|
547
|
-
if (el.Type === "FileUploadButton") {
|
|
571
|
+
if (el.Type === "FileUploadButton" && !isDesignJson) {
|
|
548
572
|
if (!("Value" in el)) {
|
|
549
573
|
el.Value = "upload";
|
|
550
574
|
fixes.push(`[Rule3] ${path}.Value: 누락 → "upload"`);
|
|
@@ -556,7 +580,7 @@ function fixEnumAndRangeValues(doc, datas, fixes) {
|
|
|
556
580
|
}
|
|
557
581
|
// ---- Chart: PlotOptions 필수 ----
|
|
558
582
|
const isChart = ["Chart", "PieChart", "ScatterChart", "PolygonChart"].includes(type);
|
|
559
|
-
if (isChart) {
|
|
583
|
+
if (isChart && !isDesignJson) {
|
|
560
584
|
if (!el.PlotOptions || typeof el.PlotOptions !== "object") {
|
|
561
585
|
el.PlotOptions = { Animation: 1000, EnableMouseTracking: true, DataLabels: {}, ConnectNulls: false, AllowOverlap: false };
|
|
562
586
|
fixes.push(`[Rule3] ${path}.PlotOptions: 누락 → 기본값 생성`);
|
|
@@ -587,16 +611,19 @@ function fixEnumAndRangeValues(doc, datas, fixes) {
|
|
|
587
611
|
}
|
|
588
612
|
// ---- MultiComboBox: InitType / RefreshType 필수 + enum 범위 ----
|
|
589
613
|
if (el.Type === "MultiComboBox") {
|
|
590
|
-
if (!
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
614
|
+
if (!isDesignJson) {
|
|
615
|
+
if (!("InitType" in el)) {
|
|
616
|
+
el.InitType = 0;
|
|
617
|
+
fixes.push(`[Rule3] ${path}.InitType: 누락 → 0 (CurrentValue)`);
|
|
618
|
+
}
|
|
619
|
+
if (!("RefreshType" in el)) {
|
|
620
|
+
el.RefreshType = 1;
|
|
621
|
+
fixes.push(`[Rule3] ${path}.RefreshType: 누락 → 1 (FirstTime)`);
|
|
622
|
+
}
|
|
598
623
|
}
|
|
599
|
-
|
|
624
|
+
// 범위 검증은 항상 수행
|
|
625
|
+
fixIntRange(el, "InitType", 0, 2, 0, path, fixes);
|
|
626
|
+
fixIntRange(el, "RefreshType", 0, 2, 1, path, fixes);
|
|
600
627
|
}
|
|
601
628
|
// ---- OlapGrid Fields ----
|
|
602
629
|
if (el.Type === "OlapGrid") {
|