@bimatrix-aud-platform/aud_mcp_server 1.1.37 → 1.1.39
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/defaults.d.ts +182 -35
- package/dist/generators/defaults.js +255 -44
- package/dist/generators/mtsd-builder.d.ts +191 -0
- package/dist/generators/mtsd-builder.js +945 -0
- package/dist/generators/report-template.d.ts +43 -0
- package/dist/generators/report-template.js +723 -0
- package/dist/index.js +36 -0
- package/package.json +1 -1
- package/schemas/mtsd.interface.ts +2 -2
- package/schemas/mtsd.schema.json +7 -8
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
import { generateId } from "../utils/uuid.js";
|
|
2
|
+
import { parseDocking } from "../utils/docking.js";
|
|
3
|
+
import { parseHexColor } from "../utils/color.js";
|
|
4
|
+
import { generateGridColumns } from "./grid-column.js";
|
|
5
|
+
// ============================================================
|
|
6
|
+
// 2. Schema-Compliant Style Helpers
|
|
7
|
+
// ============================================================
|
|
8
|
+
/** Transparent background (MTSD schema requires Color + flat RGBA) */
|
|
9
|
+
function emptyBg() {
|
|
10
|
+
return {
|
|
11
|
+
Color: { R: 0, G: 0, B: 0, A: 0 },
|
|
12
|
+
ColorR: 0, ColorG: 0, ColorB: 0, ColorA: 0,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/** Colored background from hex string */
|
|
16
|
+
function fullBg(hex) {
|
|
17
|
+
const c = parseHexColor(hex);
|
|
18
|
+
return {
|
|
19
|
+
Color: { R: c.R, G: c.G, B: c.B, A: 1 },
|
|
20
|
+
ColorR: c.R, ColorG: c.G, ColorB: c.B, ColorA: 1,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Transparent border */
|
|
24
|
+
function emptyBorder() {
|
|
25
|
+
return {
|
|
26
|
+
Color: { R: 0, G: 0, B: 0, A: 0 },
|
|
27
|
+
ColorR: 0, ColorG: 0, ColorB: 0, ColorA: 0,
|
|
28
|
+
CornerRadius: "0,0,0,0",
|
|
29
|
+
LineType: "none",
|
|
30
|
+
Thickness: "0,0,0,0",
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** Full border with options */
|
|
34
|
+
function fullBorder(opts = {}) {
|
|
35
|
+
const c = opts.color ? parseHexColor(opts.color) : { R: 224, G: 224, B: 224, A: 255 };
|
|
36
|
+
return {
|
|
37
|
+
Color: { R: c.R, G: c.G, B: c.B, A: 1 },
|
|
38
|
+
ColorR: c.R, ColorG: c.G, ColorB: c.B, ColorA: 1,
|
|
39
|
+
CornerRadius: opts.radius || "0,0,0,0",
|
|
40
|
+
LineType: opts.lineType || "solid",
|
|
41
|
+
Thickness: opts.thickness || "1,1,1,1",
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Full font with options */
|
|
45
|
+
function fullFont(opts = {}) {
|
|
46
|
+
const c = opts.color ? parseHexColor(opts.color) : { R: 0, G: 0, B: 0, A: 255 };
|
|
47
|
+
return {
|
|
48
|
+
Color: { R: c.R, G: c.G, B: c.B, A: 1 },
|
|
49
|
+
Size: opts.size ?? 12,
|
|
50
|
+
Family: opts.family ?? "inherit",
|
|
51
|
+
Bold: opts.bold ?? false,
|
|
52
|
+
Italic: opts.italic ?? false,
|
|
53
|
+
UnderLine: false,
|
|
54
|
+
HorizontalAlignment: opts.align ?? "left",
|
|
55
|
+
VerticalAlignment: opts.valign ?? "middle",
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** Skin style (Type=0, no custom colors applied) */
|
|
59
|
+
function skinStyle() {
|
|
60
|
+
return {
|
|
61
|
+
Type: 0,
|
|
62
|
+
BoxStyle: "",
|
|
63
|
+
Background: emptyBg(),
|
|
64
|
+
Border: emptyBorder(),
|
|
65
|
+
Font: fullFont(),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** Custom style (Type=2, colors applied) */
|
|
69
|
+
function customStyle(opts = {}) {
|
|
70
|
+
return {
|
|
71
|
+
Type: 2,
|
|
72
|
+
BoxStyle: "",
|
|
73
|
+
Background: opts.bg ? fullBg(opts.bg) : emptyBg(),
|
|
74
|
+
Border: opts.border ? fullBorder(opts.border) : emptyBorder(),
|
|
75
|
+
Font: fullFont(opts.font),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// ============================================================
|
|
79
|
+
// 3. Position & Docking Helper
|
|
80
|
+
// ============================================================
|
|
81
|
+
function pos(left, top, width, height, dock, margin) {
|
|
82
|
+
const docking = dock ? parseDocking(dock) : parseDocking("none");
|
|
83
|
+
if (margin)
|
|
84
|
+
docking.Margin = margin;
|
|
85
|
+
return {
|
|
86
|
+
Left: left, Top: top, Width: width, Height: height,
|
|
87
|
+
ZIndex: 1, TabIndex: 0, Docking: docking,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// ============================================================
|
|
91
|
+
// 4. Element Factory Functions
|
|
92
|
+
// ============================================================
|
|
93
|
+
function mkLabel(id, name, text, position, fontOpts) {
|
|
94
|
+
return {
|
|
95
|
+
Type: "Label",
|
|
96
|
+
Id: id || generateId("Label"),
|
|
97
|
+
Name: name,
|
|
98
|
+
Position: position,
|
|
99
|
+
Style: fontOpts ? customStyle({ font: fontOpts }) : skinStyle(),
|
|
100
|
+
LanguageCode: "",
|
|
101
|
+
Text: text,
|
|
102
|
+
Cursor: "default",
|
|
103
|
+
Formula: "",
|
|
104
|
+
UseTextOverflow: false,
|
|
105
|
+
UseAutoLineBreak: false,
|
|
106
|
+
LineSpacing: 0,
|
|
107
|
+
HasLineSpacing: false,
|
|
108
|
+
MxBinding: "",
|
|
109
|
+
MxBindingUseStyle: false,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function mkButton(id, name, text, position, styleOpts) {
|
|
113
|
+
return {
|
|
114
|
+
Type: "Button",
|
|
115
|
+
Id: id || generateId("Button"),
|
|
116
|
+
Name: name,
|
|
117
|
+
Position: position,
|
|
118
|
+
Style: styleOpts || skinStyle(),
|
|
119
|
+
LanguageCode: "",
|
|
120
|
+
Value: text,
|
|
121
|
+
Cursor: "pointer",
|
|
122
|
+
HasNewRadius: false,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function mkGroup(id, name, position, children, styleOpts) {
|
|
126
|
+
// Set InGroup on all children
|
|
127
|
+
const groupId = id || generateId("Group");
|
|
128
|
+
for (const child of children) {
|
|
129
|
+
child.InGroup = groupId;
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
Type: "Group",
|
|
133
|
+
Id: groupId,
|
|
134
|
+
Name: name,
|
|
135
|
+
Position: position,
|
|
136
|
+
Style: styleOpts || skinStyle(),
|
|
137
|
+
ChildElements: children,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function mkCalendar(id, name, position) {
|
|
141
|
+
return {
|
|
142
|
+
Type: "Calendar",
|
|
143
|
+
Id: id || generateId("Calendar"),
|
|
144
|
+
Name: name,
|
|
145
|
+
Position: position,
|
|
146
|
+
Style: skinStyle(),
|
|
147
|
+
LanguageCode: "",
|
|
148
|
+
Value: "",
|
|
149
|
+
Text: "",
|
|
150
|
+
InitType: 0,
|
|
151
|
+
IsReadOnly: false,
|
|
152
|
+
CalendarType: 0,
|
|
153
|
+
UseButton: true,
|
|
154
|
+
AutoRefresh: false,
|
|
155
|
+
AfterRefresh: "",
|
|
156
|
+
DataSource: "",
|
|
157
|
+
MxBinding: "",
|
|
158
|
+
MxBindingUseStyle: false,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function mkDataGrid(id, name, position, columns, dsName) {
|
|
162
|
+
return {
|
|
163
|
+
Type: "DataGrid",
|
|
164
|
+
Id: id || generateId("DataGrid"),
|
|
165
|
+
Name: name,
|
|
166
|
+
Position: position,
|
|
167
|
+
Style: skinStyle(),
|
|
168
|
+
DataSource: dsName,
|
|
169
|
+
CellMargin: "2",
|
|
170
|
+
Columns: columns,
|
|
171
|
+
UsePPTExport: false,
|
|
172
|
+
AutoRefresh: false,
|
|
173
|
+
DoRefresh: false,
|
|
174
|
+
DoExport: false,
|
|
175
|
+
ColumnHeaderHeight: 30,
|
|
176
|
+
RowHeight: 26,
|
|
177
|
+
ShowHeader: true,
|
|
178
|
+
SelectRule: 0,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function mkChart(id, name, position, dsName) {
|
|
182
|
+
return {
|
|
183
|
+
Type: "Chart",
|
|
184
|
+
Id: id || generateId("Chart"),
|
|
185
|
+
Name: name,
|
|
186
|
+
Position: position,
|
|
187
|
+
Style: skinStyle(),
|
|
188
|
+
DataSource: dsName,
|
|
189
|
+
AutoRefresh: false,
|
|
190
|
+
DoRefresh: false,
|
|
191
|
+
DoExport: false,
|
|
192
|
+
PlotOptions: {},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function mkTextBox(id, name, position) {
|
|
196
|
+
return {
|
|
197
|
+
Type: "TextBox",
|
|
198
|
+
Id: id || generateId("TextBox"),
|
|
199
|
+
Name: name,
|
|
200
|
+
Position: position,
|
|
201
|
+
Style: skinStyle(),
|
|
202
|
+
LanguageCode: "",
|
|
203
|
+
Value: "",
|
|
204
|
+
Text: "",
|
|
205
|
+
IsReadOnly: false,
|
|
206
|
+
Formula: "",
|
|
207
|
+
MaxLength: 0,
|
|
208
|
+
MxBinding: "",
|
|
209
|
+
MxBindingUseStyle: false,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function mkComboBox(id, name, position) {
|
|
213
|
+
return {
|
|
214
|
+
Type: "ComboBox",
|
|
215
|
+
Id: id || generateId("ComboBox"),
|
|
216
|
+
Name: name,
|
|
217
|
+
Position: position,
|
|
218
|
+
Style: skinStyle(),
|
|
219
|
+
DataSource: "",
|
|
220
|
+
Value: "",
|
|
221
|
+
Text: "",
|
|
222
|
+
InitType: 0,
|
|
223
|
+
RefreshType: 0,
|
|
224
|
+
IsReadOnly: false,
|
|
225
|
+
SortType: 0,
|
|
226
|
+
AutoRefresh: false,
|
|
227
|
+
AfterRefresh: "",
|
|
228
|
+
UseAllItems: false,
|
|
229
|
+
UseAllItemsText: "",
|
|
230
|
+
DisplayType: 0,
|
|
231
|
+
DataSourceInfo: { LabelField: "", ValueField: "" },
|
|
232
|
+
InitValue: "",
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
// ============================================================
|
|
236
|
+
// 5. DataSource Factory
|
|
237
|
+
// ============================================================
|
|
238
|
+
function extractParamsFromSQL(sql) {
|
|
239
|
+
const paramSet = new Set();
|
|
240
|
+
const regex = /[@%]?:([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
241
|
+
let match;
|
|
242
|
+
while ((match = regex.exec(sql)) !== null) {
|
|
243
|
+
paramSet.add(match[1]);
|
|
244
|
+
}
|
|
245
|
+
return Array.from(paramSet);
|
|
246
|
+
}
|
|
247
|
+
function mkDataSource(name, connection, sql, columns) {
|
|
248
|
+
const paramNames = extractParamsFromSQL(sql);
|
|
249
|
+
return {
|
|
250
|
+
Id: generateId("DS"),
|
|
251
|
+
Name: name,
|
|
252
|
+
ConnectionCode: connection,
|
|
253
|
+
DSType: 0,
|
|
254
|
+
UseMeta: false,
|
|
255
|
+
UseCache: false,
|
|
256
|
+
Encrypted: false,
|
|
257
|
+
SQL: sql,
|
|
258
|
+
Params: paramNames.map(n => ({
|
|
259
|
+
Name: n.startsWith(":") ? n : `:${n}`,
|
|
260
|
+
ParamType: "String",
|
|
261
|
+
})),
|
|
262
|
+
Columns: (columns || []).map(c => ({
|
|
263
|
+
Name: c.name,
|
|
264
|
+
Type: c.type || "string",
|
|
265
|
+
})),
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
// ============================================================
|
|
269
|
+
// 6. Script Generator
|
|
270
|
+
// ============================================================
|
|
271
|
+
function generateSearchListScript(input) {
|
|
272
|
+
const gridName = "GRD_MAIN";
|
|
273
|
+
return `import { Matrix } from "@AUD_CLIENT/control/Matrix";
|
|
274
|
+
import { Button } from "@AUD_CLIENT/control/Button";
|
|
275
|
+
import { Calendar } from "@AUD_CLIENT/control/Calendar";
|
|
276
|
+
import { DataGrid } from "@AUD_CLIENT/control/DataGrid";
|
|
277
|
+
|
|
278
|
+
let Matrix: Matrix;
|
|
279
|
+
|
|
280
|
+
Matrix.OnDocumentLoadComplete = function (sender, args) {
|
|
281
|
+
let btnSearch = Matrix.getObject("BTN_SEARCH") as Button;
|
|
282
|
+
btnSearch.OnClick = btnSearchOnClick;
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const btnSearchOnClick = function (sender, args) {
|
|
286
|
+
let calFrom = Matrix.getObject("CAL_FROM") as Calendar;
|
|
287
|
+
let calTo = Matrix.getObject("CAL_TO") as Calendar;
|
|
288
|
+
|
|
289
|
+
Matrix.SetVariable("VS_YMD_FROM", calFrom.Value);
|
|
290
|
+
Matrix.SetVariable("VS_YMD_TO", calTo.Value);
|
|
291
|
+
|
|
292
|
+
Matrix.doRefresh(["${gridName}"]);
|
|
293
|
+
};
|
|
294
|
+
`;
|
|
295
|
+
}
|
|
296
|
+
function generateCrudScript(input) {
|
|
297
|
+
const gridName = "GRD_MAIN";
|
|
298
|
+
return `import { Matrix } from "@AUD_CLIENT/control/Matrix";
|
|
299
|
+
import { Button } from "@AUD_CLIENT/control/Button";
|
|
300
|
+
import { Calendar } from "@AUD_CLIENT/control/Calendar";
|
|
301
|
+
import { DataGrid } from "@AUD_CLIENT/control/DataGrid";
|
|
302
|
+
|
|
303
|
+
let Matrix: Matrix;
|
|
304
|
+
|
|
305
|
+
Matrix.OnDocumentLoadComplete = function (sender, args) {
|
|
306
|
+
let btnSearch = Matrix.getObject("BTN_SEARCH") as Button;
|
|
307
|
+
let btnSave = Matrix.getObject("BTN_SAVE") as Button;
|
|
308
|
+
let btnAddRow = Matrix.getObject("BTN_ADD_ROW") as Button;
|
|
309
|
+
let btnDelRow = Matrix.getObject("BTN_DEL_ROW") as Button;
|
|
310
|
+
|
|
311
|
+
btnSearch.OnClick = btnSearchOnClick;
|
|
312
|
+
btnSave.OnClick = btnSaveOnClick;
|
|
313
|
+
btnAddRow.OnClick = btnAddRowOnClick;
|
|
314
|
+
btnDelRow.OnClick = btnDelRowOnClick;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const btnSearchOnClick = function (sender, args) {
|
|
318
|
+
let calFrom = Matrix.getObject("CAL_FROM") as Calendar;
|
|
319
|
+
let calTo = Matrix.getObject("CAL_TO") as Calendar;
|
|
320
|
+
|
|
321
|
+
Matrix.SetVariable("VS_YMD_FROM", calFrom.Value);
|
|
322
|
+
Matrix.SetVariable("VS_YMD_TO", calTo.Value);
|
|
323
|
+
|
|
324
|
+
Matrix.doRefresh(["${gridName}"]);
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const btnSaveOnClick = function (sender, args) {
|
|
328
|
+
let grd = Matrix.getObject("${gridName}") as DataGrid;
|
|
329
|
+
Matrix.RunScriptEx(["${gridName}"], "SVC_SAVE", {}, function (p) {
|
|
330
|
+
if (p.Success) {
|
|
331
|
+
Matrix.Alert("저장되었습니다.");
|
|
332
|
+
Matrix.doRefresh(["${gridName}"]);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const btnAddRowOnClick = function (sender, args) {
|
|
338
|
+
let grd = Matrix.getObject("${gridName}") as DataGrid;
|
|
339
|
+
grd.addRow();
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const btnDelRowOnClick = function (sender, args) {
|
|
343
|
+
let grd = Matrix.getObject("${gridName}") as DataGrid;
|
|
344
|
+
let row = grd.getSelectedRowIndex();
|
|
345
|
+
if (row >= 0) {
|
|
346
|
+
grd.deleteRow(row);
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
`;
|
|
350
|
+
}
|
|
351
|
+
function generateMasterDetailScript(input) {
|
|
352
|
+
return `import { Matrix } from "@AUD_CLIENT/control/Matrix";
|
|
353
|
+
import { Button } from "@AUD_CLIENT/control/Button";
|
|
354
|
+
import { Calendar } from "@AUD_CLIENT/control/Calendar";
|
|
355
|
+
import { DataGrid } from "@AUD_CLIENT/control/DataGrid";
|
|
356
|
+
|
|
357
|
+
let Matrix: Matrix;
|
|
358
|
+
|
|
359
|
+
Matrix.OnDocumentLoadComplete = function (sender, args) {
|
|
360
|
+
let btnSearch = Matrix.getObject("BTN_SEARCH") as Button;
|
|
361
|
+
btnSearch.OnClick = btnSearchOnClick;
|
|
362
|
+
|
|
363
|
+
let grdMaster = Matrix.getObject("GRD_MASTER") as DataGrid;
|
|
364
|
+
grdMaster.OnSelectionChanged = grdMasterOnSelectionChanged;
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
const btnSearchOnClick = function (sender, args) {
|
|
368
|
+
let calFrom = Matrix.getObject("CAL_FROM") as Calendar;
|
|
369
|
+
let calTo = Matrix.getObject("CAL_TO") as Calendar;
|
|
370
|
+
|
|
371
|
+
Matrix.SetVariable("VS_YMD_FROM", calFrom.Value);
|
|
372
|
+
Matrix.SetVariable("VS_YMD_TO", calTo.Value);
|
|
373
|
+
|
|
374
|
+
Matrix.doRefresh(["GRD_MASTER"]);
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const grdMasterOnSelectionChanged = function (sender, args) {
|
|
378
|
+
let grd = Matrix.getObject("GRD_MASTER") as DataGrid;
|
|
379
|
+
let row = grd.getSelectedRowIndex();
|
|
380
|
+
if (row < 0) return;
|
|
381
|
+
|
|
382
|
+
let dt = grd.getDataTable();
|
|
383
|
+
// TODO: Set detail key variable and refresh detail grid
|
|
384
|
+
// Matrix.SetVariable("VS_MASTER_KEY", dt.getRowValue(row, "KEY_COLUMN"));
|
|
385
|
+
// Matrix.doRefresh(["GRD_DETAIL"]);
|
|
386
|
+
};
|
|
387
|
+
`;
|
|
388
|
+
}
|
|
389
|
+
function generateDashboardScript(input) {
|
|
390
|
+
const kpiLabels = (input.kpiCards || []).map(k => k.valueLabel);
|
|
391
|
+
const kpiLines = kpiLabels.map(lbl => ` let ${lbl.replace("LBL_", "lbl")} = Matrix.getObject("${lbl}") as Label;`).join("\n");
|
|
392
|
+
return `import { Matrix } from "@AUD_CLIENT/control/Matrix";
|
|
393
|
+
import { Button } from "@AUD_CLIENT/control/Button";
|
|
394
|
+
import { Label } from "@AUD_CLIENT/control/Label";
|
|
395
|
+
import { Calendar } from "@AUD_CLIENT/control/Calendar";
|
|
396
|
+
import { DataGrid } from "@AUD_CLIENT/control/DataGrid";
|
|
397
|
+
import { Chart } from "@AUD_CLIENT/control/Chart";
|
|
398
|
+
|
|
399
|
+
let Matrix: Matrix;
|
|
400
|
+
|
|
401
|
+
Matrix.OnDocumentLoadComplete = function (sender, args) {
|
|
402
|
+
let btnSearch = Matrix.getObject("BTN_SEARCH") as Button;
|
|
403
|
+
btnSearch.OnClick = btnSearchOnClick;
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
Matrix.OnDataBindEnd = function (sender, args) {
|
|
407
|
+
updateKPI();
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const btnSearchOnClick = function (sender, args) {
|
|
411
|
+
let calFrom = Matrix.getObject("CAL_FROM") as Calendar;
|
|
412
|
+
let calTo = Matrix.getObject("CAL_TO") as Calendar;
|
|
413
|
+
|
|
414
|
+
Matrix.SetVariable("VS_YMD_FROM", calFrom.Value);
|
|
415
|
+
Matrix.SetVariable("VS_YMD_TO", calTo.Value);
|
|
416
|
+
|
|
417
|
+
Matrix.doRefresh(["CHART_MAIN", "GRD_MAIN"]);
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
/** KPI 카드 값 업데이트 */
|
|
421
|
+
const updateKPI = function () {
|
|
422
|
+
let grd = Matrix.getObject("GRD_MAIN") as DataGrid;
|
|
423
|
+
let dt = grd.getDataTable();
|
|
424
|
+
|
|
425
|
+
if (dt == null || dt.GetRowCount() === 0) return;
|
|
426
|
+
|
|
427
|
+
// TODO: KPI 계산 로직을 업무에 맞게 수정하세요
|
|
428
|
+
${kpiLines}
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
/** 숫자 콤마 포맷 */
|
|
432
|
+
const formatNumber = function (num: number): string {
|
|
433
|
+
return num.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ",");
|
|
434
|
+
};
|
|
435
|
+
`;
|
|
436
|
+
}
|
|
437
|
+
// ============================================================
|
|
438
|
+
// 7. Template Layout Builders
|
|
439
|
+
// ============================================================
|
|
440
|
+
function buildSearchListLayout(input) {
|
|
441
|
+
const warnings = [];
|
|
442
|
+
const elements = [];
|
|
443
|
+
// IDs
|
|
444
|
+
const grpHeaderId = generateId("Group");
|
|
445
|
+
const grpSearchId = generateId("Group");
|
|
446
|
+
// Header
|
|
447
|
+
const lblTitle = mkLabel(generateId("Label"), "LBL_TITLE", input.title || input.reportName, pos(15, 10, 300, 30), { size: 18, bold: true, color: "#333333" });
|
|
448
|
+
const btnSearch = mkButton(generateId("Button"), "BTN_SEARCH", "조회", pos(0, 10, 80, 30, "right", "0,0,15,0"));
|
|
449
|
+
btnSearch.Position.Docking.HoldSize = true;
|
|
450
|
+
const grpHeader = mkGroup(grpHeaderId, "GRP_HEADER", pos(0, 0, 1200, 50, "left+right"), [lblTitle, btnSearch], customStyle({
|
|
451
|
+
bg: "#F8F9FA",
|
|
452
|
+
border: { thickness: "0,0,1,0", color: "#E0E0E0" },
|
|
453
|
+
}));
|
|
454
|
+
elements.push(grpHeader);
|
|
455
|
+
// Search Conditions
|
|
456
|
+
const lblFrom = mkLabel(generateId("Label"), "LBL_FROM", "조회기간", pos(15, 10, 60, 25), { size: 12, color: "#555555" });
|
|
457
|
+
const calFrom = mkCalendar(generateId("Calendar"), "CAL_FROM", pos(80, 8, 130, 28));
|
|
458
|
+
const lblWave = mkLabel(generateId("Label"), "LBL_WAVE", "~", pos(215, 10, 15, 25), { size: 12, align: "center" });
|
|
459
|
+
const calTo = mkCalendar(generateId("Calendar"), "CAL_TO", pos(235, 8, 130, 28));
|
|
460
|
+
const grpSearch = mkGroup(grpSearchId, "GRP_SEARCH", pos(0, 50, 1200, 45, "left+right"), [lblFrom, calFrom, lblWave, calTo], customStyle({ border: { thickness: "0,0,1,0", color: "#E0E0E0" } }));
|
|
461
|
+
elements.push(grpSearch);
|
|
462
|
+
// Grid
|
|
463
|
+
const gridCols = input.gridColumns
|
|
464
|
+
? generateGridColumns({ columns: input.gridColumns }).columns
|
|
465
|
+
: [];
|
|
466
|
+
const dsName = input.dataSources[0]?.name || "DS_GRID";
|
|
467
|
+
const grdMain = mkDataGrid(generateId("DataGrid"), "GRD_MAIN", pos(0, 95, 1200, 500, "left+right+bottom"), gridCols, dsName);
|
|
468
|
+
elements.push(grdMain);
|
|
469
|
+
return { elements, warnings };
|
|
470
|
+
}
|
|
471
|
+
function buildCrudLayout(input) {
|
|
472
|
+
const warnings = [];
|
|
473
|
+
const elements = [];
|
|
474
|
+
const grpHeaderId = generateId("Group");
|
|
475
|
+
const grpSearchId = generateId("Group");
|
|
476
|
+
// Header with multiple buttons
|
|
477
|
+
const lblTitle = mkLabel(generateId("Label"), "LBL_TITLE", input.title || input.reportName, pos(15, 10, 300, 30), { size: 18, bold: true, color: "#333333" });
|
|
478
|
+
const btnSearch = mkButton(generateId("Button"), "BTN_SEARCH", "조회", pos(0, 10, 80, 30, "right", "0,0,275,0"));
|
|
479
|
+
btnSearch.Position.Docking.HoldSize = true;
|
|
480
|
+
const btnSave = mkButton(generateId("Button"), "BTN_SAVE", "저장", pos(0, 10, 80, 30, "right", "0,0,190,0"));
|
|
481
|
+
btnSave.Position.Docking.HoldSize = true;
|
|
482
|
+
const btnAddRow = mkButton(generateId("Button"), "BTN_ADD_ROW", "행추가", pos(0, 10, 80, 30, "right", "0,0,105,0"));
|
|
483
|
+
btnAddRow.Position.Docking.HoldSize = true;
|
|
484
|
+
const btnDelRow = mkButton(generateId("Button"), "BTN_DEL_ROW", "행삭제", pos(0, 10, 80, 30, "right", "0,0,15,0"));
|
|
485
|
+
btnDelRow.Position.Docking.HoldSize = true;
|
|
486
|
+
const grpHeader = mkGroup(grpHeaderId, "GRP_HEADER", pos(0, 0, 1200, 50, "left+right"), [lblTitle, btnSearch, btnSave, btnAddRow, btnDelRow], customStyle({
|
|
487
|
+
bg: "#F8F9FA",
|
|
488
|
+
border: { thickness: "0,0,1,0", color: "#E0E0E0" },
|
|
489
|
+
}));
|
|
490
|
+
elements.push(grpHeader);
|
|
491
|
+
// Search
|
|
492
|
+
const lblFrom = mkLabel(generateId("Label"), "LBL_FROM", "조회기간", pos(15, 10, 60, 25), { size: 12, color: "#555555" });
|
|
493
|
+
const calFrom = mkCalendar(generateId("Calendar"), "CAL_FROM", pos(80, 8, 130, 28));
|
|
494
|
+
const lblWave = mkLabel(generateId("Label"), "LBL_WAVE", "~", pos(215, 10, 15, 25), { size: 12, align: "center" });
|
|
495
|
+
const calTo = mkCalendar(generateId("Calendar"), "CAL_TO", pos(235, 8, 130, 28));
|
|
496
|
+
const grpSearch = mkGroup(grpSearchId, "GRP_SEARCH", pos(0, 50, 1200, 45, "left+right"), [lblFrom, calFrom, lblWave, calTo], customStyle({ border: { thickness: "0,0,1,0", color: "#E0E0E0" } }));
|
|
497
|
+
elements.push(grpSearch);
|
|
498
|
+
// Grid (editable)
|
|
499
|
+
const gridCols = input.gridColumns
|
|
500
|
+
? generateGridColumns({ columns: input.gridColumns.map(c => ({ ...c, editable: true })) }).columns
|
|
501
|
+
: [];
|
|
502
|
+
const dsName = input.dataSources[0]?.name || "DS_GRID";
|
|
503
|
+
const grdMain = mkDataGrid(generateId("DataGrid"), "GRD_MAIN", pos(0, 95, 1200, 500, "left+right+bottom"), gridCols, dsName);
|
|
504
|
+
elements.push(grdMain);
|
|
505
|
+
return { elements, warnings };
|
|
506
|
+
}
|
|
507
|
+
function buildMasterDetailLayout(input) {
|
|
508
|
+
const warnings = [];
|
|
509
|
+
const elements = [];
|
|
510
|
+
const grpHeaderId = generateId("Group");
|
|
511
|
+
const grpSearchId = generateId("Group");
|
|
512
|
+
// Header
|
|
513
|
+
const lblTitle = mkLabel(generateId("Label"), "LBL_TITLE", input.title || input.reportName, pos(15, 10, 300, 30), { size: 18, bold: true, color: "#333333" });
|
|
514
|
+
const btnSearch = mkButton(generateId("Button"), "BTN_SEARCH", "조회", pos(0, 10, 80, 30, "right", "0,0,15,0"));
|
|
515
|
+
btnSearch.Position.Docking.HoldSize = true;
|
|
516
|
+
const grpHeader = mkGroup(grpHeaderId, "GRP_HEADER", pos(0, 0, 1200, 50, "left+right"), [lblTitle, btnSearch], customStyle({
|
|
517
|
+
bg: "#F8F9FA",
|
|
518
|
+
border: { thickness: "0,0,1,0", color: "#E0E0E0" },
|
|
519
|
+
}));
|
|
520
|
+
elements.push(grpHeader);
|
|
521
|
+
// Search
|
|
522
|
+
const lblFrom = mkLabel(generateId("Label"), "LBL_FROM", "조회기간", pos(15, 10, 60, 25), { size: 12, color: "#555555" });
|
|
523
|
+
const calFrom = mkCalendar(generateId("Calendar"), "CAL_FROM", pos(80, 8, 130, 28));
|
|
524
|
+
const lblWave = mkLabel(generateId("Label"), "LBL_WAVE", "~", pos(215, 10, 15, 25), { size: 12, align: "center" });
|
|
525
|
+
const calTo = mkCalendar(generateId("Calendar"), "CAL_TO", pos(235, 8, 130, 28));
|
|
526
|
+
const grpSearch = mkGroup(grpSearchId, "GRP_SEARCH", pos(0, 50, 1200, 45, "left+right"), [lblFrom, calFrom, lblWave, calTo], customStyle({ border: { thickness: "0,0,1,0", color: "#E0E0E0" } }));
|
|
527
|
+
elements.push(grpSearch);
|
|
528
|
+
// Master grid (left side, ~60% width)
|
|
529
|
+
const gridCols = input.gridColumns
|
|
530
|
+
? generateGridColumns({ columns: input.gridColumns }).columns
|
|
531
|
+
: [];
|
|
532
|
+
const masterDsName = input.dataSources[0]?.name || "DS_MASTER";
|
|
533
|
+
const grdMaster = mkDataGrid(generateId("DataGrid"), "GRD_MASTER", pos(0, 95, 700, 500, "left+bottom"), gridCols, masterDsName);
|
|
534
|
+
elements.push(grdMaster);
|
|
535
|
+
// Detail panel (right side)
|
|
536
|
+
const grpDetailId = generateId("Group");
|
|
537
|
+
const lblDetailTitle = mkLabel(generateId("Label"), "LBL_DETAIL_TITLE", "상세 정보", pos(15, 10, 200, 25), { size: 14, bold: true, color: "#333333" });
|
|
538
|
+
const grpDetail = mkGroup(grpDetailId, "GRP_DETAIL", pos(710, 95, 490, 500, "right+bottom", "0,0,0,0"), [lblDetailTitle], customStyle({
|
|
539
|
+
border: { thickness: "1,0,0,0", color: "#E0E0E0" },
|
|
540
|
+
}));
|
|
541
|
+
grpDetail.Position.Docking.HoldSize = true;
|
|
542
|
+
elements.push(grpDetail);
|
|
543
|
+
warnings.push("master-detail: 상세 패널(GRP_DETAIL)에 업무에 맞는 컨트롤을 추가해주세요.");
|
|
544
|
+
return { elements, warnings };
|
|
545
|
+
}
|
|
546
|
+
function buildDashboardLayout(input) {
|
|
547
|
+
const warnings = [];
|
|
548
|
+
const elements = [];
|
|
549
|
+
const grpHeaderId = generateId("Group");
|
|
550
|
+
const grpSearchId = generateId("Group");
|
|
551
|
+
const grpKpiId = generateId("Group");
|
|
552
|
+
// Header
|
|
553
|
+
const lblTitle = mkLabel(generateId("Label"), "LBL_TITLE", input.title || input.reportName, pos(15, 10, 300, 30), { size: 18, bold: true, color: "#333333" });
|
|
554
|
+
const btnSearch = mkButton(generateId("Button"), "BTN_SEARCH", "조회", pos(0, 10, 80, 30, "right", "0,0,15,0"));
|
|
555
|
+
btnSearch.Position.Docking.HoldSize = true;
|
|
556
|
+
const grpHeader = mkGroup(grpHeaderId, "GRP_HEADER", pos(0, 0, 1200, 50, "left+right"), [lblTitle, btnSearch], customStyle({
|
|
557
|
+
bg: "#F8F9FA",
|
|
558
|
+
border: { thickness: "0,0,1,0", color: "#E0E0E0" },
|
|
559
|
+
}));
|
|
560
|
+
elements.push(grpHeader);
|
|
561
|
+
// Search
|
|
562
|
+
const lblFrom = mkLabel(generateId("Label"), "LBL_FROM", "조회기간", pos(15, 10, 60, 25), { size: 12, color: "#555555" });
|
|
563
|
+
const calFrom = mkCalendar(generateId("Calendar"), "CAL_FROM", pos(80, 8, 130, 28));
|
|
564
|
+
const lblWave = mkLabel(generateId("Label"), "LBL_WAVE", "~", pos(215, 10, 15, 25), { size: 12, align: "center" });
|
|
565
|
+
const calTo = mkCalendar(generateId("Calendar"), "CAL_TO", pos(235, 8, 130, 28));
|
|
566
|
+
const grpSearch = mkGroup(grpSearchId, "GRP_SEARCH", pos(0, 50, 1200, 45, "left+right"), [lblFrom, calFrom, lblWave, calTo], customStyle({ border: { thickness: "0,0,1,0", color: "#E0E0E0" } }));
|
|
567
|
+
elements.push(grpSearch);
|
|
568
|
+
// KPI Cards
|
|
569
|
+
const defaultKpiCards = [
|
|
570
|
+
{ title: "총매출", valueLabel: "LBL_KPI_VAL1", color: "#4A90D9" },
|
|
571
|
+
{ title: "주문건수", valueLabel: "LBL_KPI_VAL2", color: "#50C878" },
|
|
572
|
+
{ title: "고객수", valueLabel: "LBL_KPI_VAL3", color: "#FFB347" },
|
|
573
|
+
{ title: "달성률", valueLabel: "LBL_KPI_VAL4", color: "#FF6B6B" },
|
|
574
|
+
];
|
|
575
|
+
const kpiCards = input.kpiCards && input.kpiCards.length > 0
|
|
576
|
+
? input.kpiCards
|
|
577
|
+
: defaultKpiCards;
|
|
578
|
+
const kpiChildren = [];
|
|
579
|
+
const cardWidth = 270;
|
|
580
|
+
const cardGap = 20;
|
|
581
|
+
kpiCards.forEach((card, i) => {
|
|
582
|
+
const cardX = 15 + i * (cardWidth + cardGap);
|
|
583
|
+
const cardColor = card.color || ["#4A90D9", "#50C878", "#FFB347", "#FF6B6B"][i % 4];
|
|
584
|
+
const cardId = generateId("Group");
|
|
585
|
+
const lblCardTitle = mkLabel(generateId("Label"), `LBL_KPI_TTL${i + 1}`, card.title, pos(15, 8, 240, 22), { size: 12, color: "#666666" });
|
|
586
|
+
const lblCardValue = mkLabel(generateId("Label"), card.valueLabel || `LBL_KPI_VAL${i + 1}`, "-", pos(15, 35, 240, 40), { size: 24, bold: true, color: cardColor });
|
|
587
|
+
const kpiCard = mkGroup(cardId, `GRP_KPI_CARD${i + 1}`, pos(cardX, 10, cardWidth, 90), [lblCardTitle, lblCardValue], customStyle({
|
|
588
|
+
bg: "#FFFFFF",
|
|
589
|
+
border: { thickness: "1,1,1,1", color: "#E0E0E0", radius: "8,8,8,8" },
|
|
590
|
+
}));
|
|
591
|
+
kpiChildren.push(kpiCard);
|
|
592
|
+
});
|
|
593
|
+
const grpKpi = mkGroup(grpKpiId, "GRP_KPI", pos(0, 95, 1200, 120, "left+right"), kpiChildren);
|
|
594
|
+
elements.push(grpKpi);
|
|
595
|
+
// Chart (left)
|
|
596
|
+
const chartDsName = input.dataSources.find(d => d.name.toUpperCase().includes("CHART"))?.name
|
|
597
|
+
|| input.dataSources[0]?.name || "DS_CHART";
|
|
598
|
+
const chartMain = mkChart(generateId("Chart"), "CHART_MAIN", pos(5, 220, 595, 380, "left+bottom"), chartDsName);
|
|
599
|
+
elements.push(chartMain);
|
|
600
|
+
// Grid (right)
|
|
601
|
+
const gridCols = input.gridColumns
|
|
602
|
+
? generateGridColumns({ columns: input.gridColumns }).columns
|
|
603
|
+
: [];
|
|
604
|
+
const gridDsName = input.dataSources.find(d => d.name.toUpperCase().includes("GRID"))?.name
|
|
605
|
+
|| input.dataSources[input.dataSources.length > 1 ? 1 : 0]?.name || "DS_GRID";
|
|
606
|
+
const grdMain = mkDataGrid(generateId("DataGrid"), "GRD_MAIN", pos(605, 220, 590, 380, "left+right+bottom", "600,0,5,5"), gridCols, gridDsName);
|
|
607
|
+
elements.push(grdMain);
|
|
608
|
+
return { elements, warnings };
|
|
609
|
+
}
|
|
610
|
+
// ============================================================
|
|
611
|
+
// 8. Main Entry
|
|
612
|
+
// ============================================================
|
|
613
|
+
export function generateReportTemplate(input) {
|
|
614
|
+
const warnings = [];
|
|
615
|
+
const reportCode = generateId("REP");
|
|
616
|
+
const formId = generateId("Form");
|
|
617
|
+
const title = input.title || input.reportName;
|
|
618
|
+
const moduleCode = input.moduleCode || "SD";
|
|
619
|
+
// --- DataSources ---
|
|
620
|
+
const dataSources = input.dataSources.map(ds => mkDataSource(ds.name, input.connection, ds.sql, ds.columns));
|
|
621
|
+
// --- Layout ---
|
|
622
|
+
let layoutResult;
|
|
623
|
+
switch (input.templateType) {
|
|
624
|
+
case "search-list":
|
|
625
|
+
layoutResult = buildSearchListLayout(input);
|
|
626
|
+
break;
|
|
627
|
+
case "crud":
|
|
628
|
+
layoutResult = buildCrudLayout(input);
|
|
629
|
+
break;
|
|
630
|
+
case "master-detail":
|
|
631
|
+
layoutResult = buildMasterDetailLayout(input);
|
|
632
|
+
break;
|
|
633
|
+
case "dashboard":
|
|
634
|
+
layoutResult = buildDashboardLayout(input);
|
|
635
|
+
break;
|
|
636
|
+
default:
|
|
637
|
+
layoutResult = buildSearchListLayout(input);
|
|
638
|
+
warnings.push(`알 수 없는 템플릿 타입 '${input.templateType}'. search-list로 대체합니다.`);
|
|
639
|
+
}
|
|
640
|
+
warnings.push(...layoutResult.warnings);
|
|
641
|
+
// --- Script ---
|
|
642
|
+
let script;
|
|
643
|
+
switch (input.templateType) {
|
|
644
|
+
case "search-list":
|
|
645
|
+
script = generateSearchListScript(input);
|
|
646
|
+
break;
|
|
647
|
+
case "crud":
|
|
648
|
+
script = generateCrudScript(input);
|
|
649
|
+
break;
|
|
650
|
+
case "master-detail":
|
|
651
|
+
script = generateMasterDetailScript(input);
|
|
652
|
+
break;
|
|
653
|
+
case "dashboard":
|
|
654
|
+
script = generateDashboardScript(input);
|
|
655
|
+
break;
|
|
656
|
+
default:
|
|
657
|
+
script = generateSearchListScript(input);
|
|
658
|
+
}
|
|
659
|
+
// --- MTSD ---
|
|
660
|
+
const mtsd = {
|
|
661
|
+
ReportInfo: {
|
|
662
|
+
ReportCode: reportCode,
|
|
663
|
+
FolderCode: "",
|
|
664
|
+
SavePath: `/${reportCode}.mtsd`,
|
|
665
|
+
ReportName: input.reportName,
|
|
666
|
+
Writer: "",
|
|
667
|
+
WriteDate: "",
|
|
668
|
+
Editor: "",
|
|
669
|
+
EditDate: "",
|
|
670
|
+
TabPosition: 0,
|
|
671
|
+
UsePersonalConditions: false,
|
|
672
|
+
DocumentVersion: "3.0.0.0",
|
|
673
|
+
RefreshType: 0,
|
|
674
|
+
},
|
|
675
|
+
DataSources: { Datas: dataSources },
|
|
676
|
+
ScriptText: "",
|
|
677
|
+
ServerScriptText: [],
|
|
678
|
+
Forms: [
|
|
679
|
+
{
|
|
680
|
+
Id: formId,
|
|
681
|
+
Name: "Form1",
|
|
682
|
+
Activated: true,
|
|
683
|
+
Visible: true,
|
|
684
|
+
LanguageCode: "",
|
|
685
|
+
Style: {
|
|
686
|
+
Type: 0,
|
|
687
|
+
BoxStyle: "",
|
|
688
|
+
Background: emptyBg(),
|
|
689
|
+
Border: emptyBorder(),
|
|
690
|
+
Font: fullFont(),
|
|
691
|
+
},
|
|
692
|
+
Elements: layoutResult.elements,
|
|
693
|
+
},
|
|
694
|
+
],
|
|
695
|
+
MetaDataSources: {
|
|
696
|
+
TemplateMeta: { TemplateName: "" },
|
|
697
|
+
MetaDataSources: [],
|
|
698
|
+
},
|
|
699
|
+
EXECUTION_PLANS: [],
|
|
700
|
+
Variables: [],
|
|
701
|
+
Modules: [],
|
|
702
|
+
ResponsiveLayout: [],
|
|
703
|
+
Langs: [],
|
|
704
|
+
WorkFlowModules: [],
|
|
705
|
+
WorkFlowInfo: "",
|
|
706
|
+
};
|
|
707
|
+
// --- SQL files ---
|
|
708
|
+
const sqlFiles = input.dataSources.map(ds => ({
|
|
709
|
+
name: ds.name,
|
|
710
|
+
sql: ds.sql,
|
|
711
|
+
}));
|
|
712
|
+
return {
|
|
713
|
+
mtsd,
|
|
714
|
+
script,
|
|
715
|
+
dataSources: sqlFiles,
|
|
716
|
+
reportInfo: {
|
|
717
|
+
ReportCode: reportCode,
|
|
718
|
+
ReportName: input.reportName,
|
|
719
|
+
ModuleCode: moduleCode,
|
|
720
|
+
},
|
|
721
|
+
warnings,
|
|
722
|
+
};
|
|
723
|
+
}
|