[agent-pipeline] pipe-20260329112709-ncml round-2
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { ComponentRendererProps } from "../../types";
|
||||
import { ComponentRendererProps } from "@/types/component";
|
||||
import { TextDisplayConfig } from "./types";
|
||||
import { getAdaptiveLabelColor } from "@/lib/utils/darkModeColor";
|
||||
import { filterDOMProps } from "@/lib/utils/domPropsFilter";
|
||||
|
||||
@@ -116,8 +116,8 @@ export const TextInputComponent: React.FC<TextInputComponentProps> = ({
|
||||
|
||||
// 🆕 채번 규칙이 설정되어 있으면 항상 _numberingRuleId를 formData에 설정
|
||||
// (값 생성 성공 여부와 관계없이, 저장 시점에 allocateCode를 호출하기 위함)
|
||||
if (testAutoGeneration.type === "numbering_rule" && testAutoGeneration.options?.numberingRuleId) {
|
||||
const ruleId = testAutoGeneration.options.numberingRuleId;
|
||||
if (testAutoGeneration.type === "numbering_rule" && testAutoGeneration.options?.numbering_rule_id) {
|
||||
const ruleId = testAutoGeneration.options.numbering_rule_id;
|
||||
if (ruleId && ruleId !== "undefined" && ruleId !== "null" && ruleId !== "") {
|
||||
const ruleIdKey = `${component.columnName}_numberingRuleId`;
|
||||
// formData에 아직 설정되지 않은 경우에만 설정
|
||||
@@ -135,7 +135,7 @@ export const TextInputComponent: React.FC<TextInputComponentProps> = ({
|
||||
|
||||
// 채번 규칙은 비동기로 처리
|
||||
if (testAutoGeneration.type === "numbering_rule") {
|
||||
const ruleId = testAutoGeneration.options?.numberingRuleId;
|
||||
const ruleId = testAutoGeneration.options?.numbering_rule_id;
|
||||
if (ruleId && ruleId !== "undefined" && ruleId !== "null") {
|
||||
try {
|
||||
const { previewNumberingCode } = await import("@/lib/api/numberingRule");
|
||||
@@ -154,7 +154,7 @@ export const TextInputComponent: React.FC<TextInputComponentProps> = ({
|
||||
}
|
||||
} else {
|
||||
// 기타 타입은 동기로 처리
|
||||
generatedValue = AutoGenerationUtils.generateValue(testAutoGeneration, component.columnName);
|
||||
generatedValue = await AutoGenerationUtils.generateValue(testAutoGeneration, component.columnName);
|
||||
isGeneratingRef.current = false;
|
||||
}
|
||||
|
||||
@@ -168,13 +168,13 @@ export const TextInputComponent: React.FC<TextInputComponentProps> = ({
|
||||
onFormDataChange(component.columnName, generatedValue);
|
||||
|
||||
// 채번 규칙 ID도 함께 저장 (저장 시점에 실제 할당하기 위함)
|
||||
if (testAutoGeneration.type === "numbering_rule" && testAutoGeneration.options?.numberingRuleId) {
|
||||
if (testAutoGeneration.type === "numbering_rule" && testAutoGeneration.options?.numbering_rule_id) {
|
||||
const ruleIdKey = `${component.columnName}_numberingRuleId`;
|
||||
onFormDataChange(ruleIdKey, testAutoGeneration.options.numberingRuleId);
|
||||
onFormDataChange(ruleIdKey, testAutoGeneration.options.numbering_rule_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!autoGeneratedValue && testAutoGeneration.type !== "none") {
|
||||
} else if (!autoGeneratedValue) {
|
||||
// 디자인 모드에서도 미리보기용 자동생성 값 표시
|
||||
const previewValue = AutoGenerationUtils.generatePreviewValue(testAutoGeneration);
|
||||
setAutoGeneratedValue(previewValue);
|
||||
@@ -844,7 +844,7 @@ export const TextInputComponent: React.FC<TextInputComponentProps> = ({
|
||||
|
||||
// 채번 규칙 ID 복구
|
||||
if (isInteractive && onFormDataChange && component.columnName) {
|
||||
const ruleId = testAutoGeneration.options?.numberingRuleId;
|
||||
const ruleId = testAutoGeneration.options?.numbering_rule_id;
|
||||
if (ruleId) {
|
||||
const ruleIdKey = `${component.columnName}_numberingRuleId`;
|
||||
onFormDataChange(ruleIdKey, ruleId);
|
||||
|
||||
@@ -34,7 +34,7 @@ export const TextInputConfigPanel: React.FC<TextInputConfigPanelProps> = ({ conf
|
||||
|
||||
// useState 초기값에서 저장된 값 복원 (우선순위: 저장된 값 > menuObjid prop)
|
||||
const [selectedMenuObjid, setSelectedMenuObjid] = useState<number | undefined>(() => {
|
||||
return config.autoGeneration?.selectedMenuObjid || menuObjid;
|
||||
return (config.autoGeneration as any)?.selectedMenuObjid || menuObjid;
|
||||
});
|
||||
|
||||
const [loadingMenus, setLoadingMenus] = useState(false);
|
||||
@@ -250,14 +250,14 @@ export const TextInputConfigPanel: React.FC<TextInputConfigPanelProps> = ({ conf
|
||||
채번 규칙 선택 <span className="text-destructive">*</span>
|
||||
</Label>
|
||||
<Select
|
||||
value={config.autoGeneration?.options?.numberingRuleId || ""}
|
||||
value={config.autoGeneration?.options?.numbering_rule_id || ""}
|
||||
onValueChange={(value) => {
|
||||
const currentConfig = config.autoGeneration!;
|
||||
handleChange("autoGeneration", {
|
||||
...currentConfig,
|
||||
options: {
|
||||
...currentConfig.options,
|
||||
numberingRuleId: value,
|
||||
numbering_rule_id: value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
|
||||
@@ -4168,12 +4168,12 @@ export class ButtonActionExecutor {
|
||||
}
|
||||
|
||||
const extendedContext: ExtendedControlContext = {
|
||||
formData: context.formData || {},
|
||||
selectedRows: context.selectedRows || [],
|
||||
selectedRowsData: context.selectedRowsData || [],
|
||||
flowSelectedData: context.flowSelectedData || [],
|
||||
flowSelectedStepId: context.flowSelectedStepId,
|
||||
controlDataSource,
|
||||
form_data: context.formData || {},
|
||||
selected_rows: context.selectedRows || [],
|
||||
selected_rows_data: context.selectedRowsData || [],
|
||||
flow_selected_data: context.flowSelectedData || [],
|
||||
flow_selected_step_id: context.flowSelectedStepId,
|
||||
control_data_source: controlDataSource,
|
||||
};
|
||||
|
||||
// 🔥 새로운 버튼 액션 실행 시스템 사용
|
||||
@@ -4395,10 +4395,10 @@ export class ButtonActionExecutor {
|
||||
}
|
||||
|
||||
const extendedContext: ExtendedControlContext = {
|
||||
formData: context.formData || {},
|
||||
selectedRows: context.selectedRows || [],
|
||||
selectedRowsData: context.selectedRowsData || [],
|
||||
controlDataSource,
|
||||
form_data: context.formData || {},
|
||||
selected_rows: context.selectedRows || [],
|
||||
selected_rows_data: context.selectedRowsData || [],
|
||||
control_data_source: controlDataSource,
|
||||
};
|
||||
|
||||
// 🔥 다중 제어 지원 (flowControls 배열)
|
||||
|
||||
@@ -87,16 +87,16 @@ export const validateFormData = async (
|
||||
const widgetComponents = components.filter((c) => c.type === "widget") as WidgetComponent[];
|
||||
|
||||
for (const component of widgetComponents) {
|
||||
const fieldName = component.columnName || component.id;
|
||||
const fieldName = component.column_name || component.id;
|
||||
const value = formData[fieldName];
|
||||
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
const fieldValidation = validateFieldValue(
|
||||
fieldName,
|
||||
value,
|
||||
component.widgetType,
|
||||
component.webTypeConfig,
|
||||
component.validationRules,
|
||||
component.widget_type,
|
||||
component.web_type_config,
|
||||
component.validation_rules,
|
||||
);
|
||||
|
||||
if (!fieldValidation.isValid && fieldValidation.error) {
|
||||
@@ -137,11 +137,11 @@ export const validateFormSchema = (
|
||||
const invalidTypes: { field: string; expected: WebType; actual: string }[] = [];
|
||||
const suggestions: string[] = [];
|
||||
|
||||
const columnMap = new Map(tableColumns.map((col) => [col.columnName, col]));
|
||||
const columnMap = new Map(tableColumns.map((col) => [col.column_name, col]));
|
||||
const widgetComponents = components.filter((c) => c.type === "widget") as WidgetComponent[];
|
||||
|
||||
for (const component of widgetComponents) {
|
||||
const fieldName = component.columnName;
|
||||
const fieldName = component.column_name;
|
||||
if (!fieldName) continue;
|
||||
|
||||
// 컬럼 존재 여부 확인
|
||||
@@ -158,8 +158,8 @@ export const validateFormSchema = (
|
||||
}
|
||||
|
||||
// 웹타입 일치 여부 확인
|
||||
const componentWebType = normalizeWebType(component.widgetType);
|
||||
const columnWebType = columnInfo.webType ? normalizeWebType(columnInfo.webType) : null;
|
||||
const componentWebType = normalizeWebType(component.widget_type);
|
||||
const columnWebType = columnInfo.web_type ? normalizeWebType(columnInfo.web_type) : null;
|
||||
|
||||
if (columnWebType && componentWebType !== columnWebType) {
|
||||
invalidTypes.push({
|
||||
@@ -170,11 +170,11 @@ export const validateFormSchema = (
|
||||
}
|
||||
|
||||
// 웹타입 유효성 확인
|
||||
if (!isValidWebType(component.widgetType)) {
|
||||
if (!isValidWebType(component.widget_type)) {
|
||||
invalidTypes.push({
|
||||
field: fieldName,
|
||||
expected: "text", // 기본값
|
||||
actual: component.widgetType,
|
||||
actual: component.widget_type,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -199,7 +199,7 @@ export const validateRequiredFields = (
|
||||
const widgetComponents = components.filter((c) => c.type === "widget") as WidgetComponent[];
|
||||
|
||||
for (const component of widgetComponents) {
|
||||
const fieldName = component.columnName || component.id;
|
||||
const fieldName = component.column_name || component.id;
|
||||
// 수동 required + NOT NULL 메타데이터 기반 통합 체크
|
||||
const isRequired = component.required || isColumnRequiredByMeta(tableName, fieldName);
|
||||
if (!isRequired) continue;
|
||||
@@ -627,9 +627,9 @@ const findSimilarColumns = (targetColumn: string, columns: ColumnInfo[], thresho
|
||||
const similar: string[] = [];
|
||||
|
||||
for (const column of columns) {
|
||||
const similarity = calculateStringSimilarity(targetColumn, column.columnName);
|
||||
const similarity = calculateStringSimilarity(targetColumn, column.column_name);
|
||||
if (similarity >= threshold) {
|
||||
similar.push(column.columnName);
|
||||
similar.push(column.column_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Position, Size } from "@/types/screen";
|
||||
import { GridSettings } from "@/types/screen-management";
|
||||
export type { GridSettings };
|
||||
|
||||
export interface GridInfo {
|
||||
columnWidth: number;
|
||||
|
||||
@@ -171,28 +171,33 @@ export class ImprovedButtonActionExecutor {
|
||||
return plan;
|
||||
}
|
||||
|
||||
// 🔧 controlMode가 없으면 flowConfig/relationshipConfig 존재 여부로 자동 판단
|
||||
const effectiveControlMode = dataflowConfig.controlMode
|
||||
|| (dataflowConfig.flowConfig ? "flow" : null)
|
||||
|| (dataflowConfig.relationshipConfig ? "relationship" : null)
|
||||
// 🔧 control_mode가 없으면 flow_config/relationship_config 존재 여부로 자동 판단
|
||||
const effectiveControlMode = dataflowConfig.control_mode
|
||||
|| (dataflowConfig.flow_config ? "flow" : null)
|
||||
|| (dataflowConfig.relationship_config ? "relationship" : null)
|
||||
|| "none";
|
||||
|
||||
console.log("📋 실행 계획 생성:", {
|
||||
controlMode: dataflowConfig.controlMode,
|
||||
controlMode: dataflowConfig.control_mode,
|
||||
effectiveControlMode,
|
||||
hasFlowConfig: !!dataflowConfig.flowConfig,
|
||||
hasRelationshipConfig: !!dataflowConfig.relationshipConfig,
|
||||
hasFlowConfig: !!dataflowConfig.flow_config,
|
||||
hasRelationshipConfig: !!dataflowConfig.relationship_config,
|
||||
enableDataflowControl: buttonConfig.enable_dataflow_control,
|
||||
});
|
||||
|
||||
// 관계 기반 제어
|
||||
if (effectiveControlMode === "relationship" && dataflowConfig.relationshipConfig) {
|
||||
if (effectiveControlMode === "relationship" && dataflowConfig.relationship_config) {
|
||||
const control: ControlConfig = {
|
||||
type: "relationship",
|
||||
relationshipConfig: dataflowConfig.relationshipConfig,
|
||||
relationshipConfig: {
|
||||
relationshipId: dataflowConfig.relationship_config.relationship_id,
|
||||
relationshipName: dataflowConfig.relationship_config.relationship_name,
|
||||
executionTiming: dataflowConfig.relationship_config.execution_timing,
|
||||
contextData: dataflowConfig.relationship_config.context_data,
|
||||
},
|
||||
};
|
||||
|
||||
switch (dataflowConfig.relationshipConfig.executionTiming) {
|
||||
switch (dataflowConfig.relationship_config.execution_timing) {
|
||||
case "before":
|
||||
plan.beforeControls.push(control);
|
||||
break;
|
||||
@@ -207,15 +212,20 @@ export class ImprovedButtonActionExecutor {
|
||||
}
|
||||
|
||||
// 🆕 플로우 기반 제어
|
||||
if (effectiveControlMode === "flow" && dataflowConfig.flowConfig) {
|
||||
if (effectiveControlMode === "flow" && dataflowConfig.flow_config) {
|
||||
const control: ControlConfig = {
|
||||
type: "flow",
|
||||
flowConfig: dataflowConfig.flowConfig,
|
||||
flowConfig: {
|
||||
flowId: dataflowConfig.flow_config.flow_id,
|
||||
flowName: dataflowConfig.flow_config.flow_name,
|
||||
executionTiming: dataflowConfig.flow_config.execution_timing,
|
||||
contextData: dataflowConfig.flow_config.context_data,
|
||||
},
|
||||
};
|
||||
|
||||
console.log("📋 플로우 제어 설정:", dataflowConfig.flowConfig);
|
||||
console.log("📋 플로우 제어 설정:", dataflowConfig.flow_config);
|
||||
|
||||
switch (dataflowConfig.flowConfig.executionTiming) {
|
||||
switch (dataflowConfig.flow_config.execution_timing) {
|
||||
case "before":
|
||||
plan.beforeControls.push(control);
|
||||
break;
|
||||
@@ -1074,7 +1084,7 @@ export class ImprovedButtonActionExecutor {
|
||||
console.error("🔄 실행 오류 처리 시작:", error.message);
|
||||
|
||||
// 롤백이 필요한 경우 처리
|
||||
const rollbackNeeded = buttonConfig.dataflow_config?.executionOptions?.rollbackOnError;
|
||||
const rollbackNeeded = (buttonConfig.dataflow_config?.execution_options as any)?.rollback_on_error;
|
||||
if (rollbackNeeded) {
|
||||
console.log("🔄 롤백 처리 시작...");
|
||||
|
||||
|
||||
@@ -377,16 +377,6 @@ function getDefaultText(key: string): string {
|
||||
"menu.management.user": "사용자",
|
||||
"menu.management.user.description": "일반 사용자 업무 메뉴",
|
||||
"menu.list.title": "메뉴 목록",
|
||||
"filter.search.placeholder": "메뉴명 검색...",
|
||||
"filter.reset": "초기화",
|
||||
"button.add.top.level": "최상위 메뉴 추가",
|
||||
"button.delete.selected": "선택 삭제",
|
||||
"table.header.menu.name": "메뉴명",
|
||||
"table.header.sequence": "순서",
|
||||
"table.header.company": "회사",
|
||||
"table.header.menu.url": "URL",
|
||||
"table.header.status": "상태",
|
||||
"table.header.actions": "작업",
|
||||
};
|
||||
|
||||
return defaultTexts[key] || "";
|
||||
|
||||
@@ -419,6 +419,7 @@ export function applyMultilangMappings(
|
||||
const updateComponent = (comp: ComponentData): ComponentData => {
|
||||
const anyComp = comp as any;
|
||||
const config = anyComp.componentConfig || anyComp.config;
|
||||
const compType = anyComp.componentType || anyComp.type;
|
||||
let updated = { ...comp } as any;
|
||||
|
||||
// 기본 컴포넌트 라벨 매핑 확인
|
||||
|
||||
@@ -42,7 +42,7 @@ export interface FlowExecutionResult {
|
||||
* 노드 플로우 실행 함수
|
||||
*/
|
||||
export async function executeButtonWithFlow(
|
||||
flowConfig: ButtonDataflowConfig["flowConfig"],
|
||||
flowConfig: ButtonDataflowConfig["flow_config"],
|
||||
context: ButtonExecutionContext,
|
||||
originalAction?: () => Promise<void>,
|
||||
): Promise<FlowExecutionResult> {
|
||||
@@ -50,7 +50,7 @@ export async function executeButtonWithFlow(
|
||||
throw new Error("플로우 설정이 없습니다.");
|
||||
}
|
||||
|
||||
const { flowId, flowName, executionTiming = "before" } = flowConfig;
|
||||
const { flow_id: flowId, flow_name: flowName, execution_timing: executionTiming = "before" } = flowConfig!;
|
||||
|
||||
logger.info(`🚀 노드 플로우 실행 시작:`, {
|
||||
flowId,
|
||||
|
||||
@@ -26,17 +26,17 @@ export function generateSmartDefaults(
|
||||
if (fullWidthComponents.includes(componentId) || fullWidthComponents.includes(componentType)) {
|
||||
return {
|
||||
desktop: {
|
||||
gridColumns: 12, // 전체 너비
|
||||
grid_columns: 12, // 전체 너비
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
tablet: {
|
||||
gridColumns: 8, // 전체 너비
|
||||
grid_columns: 8, // 전체 너비
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
mobile: {
|
||||
gridColumns: 4, // 전체 너비
|
||||
grid_columns: 4, // 전체 너비
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
@@ -53,17 +53,17 @@ export function generateSmartDefaults(
|
||||
|
||||
return {
|
||||
desktop: {
|
||||
gridColumns: desktopColumns,
|
||||
grid_columns: desktopColumns,
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
tablet: {
|
||||
gridColumns: tabletColumns,
|
||||
grid_columns: tabletColumns,
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
mobile: {
|
||||
gridColumns: mobileColumns,
|
||||
grid_columns: mobileColumns,
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
@@ -73,17 +73,17 @@ export function generateSmartDefaults(
|
||||
else if (componentWidthPercent <= 10) {
|
||||
return {
|
||||
desktop: {
|
||||
gridColumns: 1, // 12컬럼 중 1개 (~8%)
|
||||
grid_columns: 1, // 12컬럼 중 1개 (~8%)
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
tablet: {
|
||||
gridColumns: 1, // 8컬럼 중 1개 (~12.5%)
|
||||
grid_columns: 1, // 8컬럼 중 1개 (~12.5%)
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
mobile: {
|
||||
gridColumns: 1, // 4컬럼 중 1개 (25%)
|
||||
grid_columns: 1, // 4컬럼 중 1개 (25%)
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
@@ -93,17 +93,17 @@ export function generateSmartDefaults(
|
||||
else if (componentWidthPercent <= 25) {
|
||||
return {
|
||||
desktop: {
|
||||
gridColumns: 3, // 12컬럼 중 3개 (25%)
|
||||
grid_columns: 3, // 12컬럼 중 3개 (25%)
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
tablet: {
|
||||
gridColumns: 2, // 8컬럼 중 2개 (25%)
|
||||
grid_columns: 2, // 8컬럼 중 2개 (25%)
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
mobile: {
|
||||
gridColumns: 1, // 4컬럼 중 1개 (25%)
|
||||
grid_columns: 1, // 4컬럼 중 1개 (25%)
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
@@ -113,17 +113,17 @@ export function generateSmartDefaults(
|
||||
else if (componentWidthPercent <= 50) {
|
||||
return {
|
||||
desktop: {
|
||||
gridColumns: 6, // 12컬럼 중 6개 (50%)
|
||||
grid_columns: 6, // 12컬럼 중 6개 (50%)
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
tablet: {
|
||||
gridColumns: 4, // 8컬럼 중 4개 (50%)
|
||||
grid_columns: 4, // 8컬럼 중 4개 (50%)
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
mobile: {
|
||||
gridColumns: 4, // 4컬럼 전체 (100%)
|
||||
grid_columns: 4, // 4컬럼 전체 (100%)
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
@@ -133,17 +133,17 @@ export function generateSmartDefaults(
|
||||
else {
|
||||
return {
|
||||
desktop: {
|
||||
gridColumns: 12, // 전체 너비
|
||||
grid_columns: 12, // 전체 너비
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
tablet: {
|
||||
gridColumns: 8, // 전체 너비
|
||||
grid_columns: 8, // 전체 너비
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
mobile: {
|
||||
gridColumns: 4, // 전체 너비
|
||||
grid_columns: 4, // 전체 너비
|
||||
order: 1,
|
||||
hide: false,
|
||||
},
|
||||
@@ -162,13 +162,13 @@ export function ensureResponsiveConfig(component: ComponentData, screenWidth?: n
|
||||
return {
|
||||
...component,
|
||||
responsive_config: {
|
||||
designerPosition: {
|
||||
designer_position: {
|
||||
x: component.position.x,
|
||||
y: component.position.y,
|
||||
width: component.size.width,
|
||||
height: component.size.height,
|
||||
},
|
||||
useSmartDefaults: true,
|
||||
use_smart_defaults: true,
|
||||
responsive: generateSmartDefaults(component, screenWidth),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -21,10 +21,11 @@ export function convertWebTypeConfigToAutoGeneration(component: ComponentData):
|
||||
}
|
||||
|
||||
// 이미 auto_generation이 올바르게 설정되어 있으면 변환 불필요
|
||||
const anyComp = component as any;
|
||||
if (
|
||||
component.auto_generation &&
|
||||
component.auto_generation.type === config.autoValueType &&
|
||||
component.auto_generation.options?.numbering_rule_id === config.numberingRuleId
|
||||
anyComp.auto_generation &&
|
||||
anyComp.auto_generation.type === config.autoValueType &&
|
||||
anyComp.auto_generation.options?.numbering_rule_id === config.numberingRuleId
|
||||
) {
|
||||
return component;
|
||||
}
|
||||
@@ -46,7 +47,7 @@ export function convertWebTypeConfigToAutoGeneration(component: ComponentData):
|
||||
return {
|
||||
...component,
|
||||
auto_generation,
|
||||
};
|
||||
} as unknown as ComponentData;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,7 @@ export function generateZPL(layout: BarcodeLabelLayout): string {
|
||||
"^LH0,0",
|
||||
];
|
||||
|
||||
const sorted = [...components].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));
|
||||
const sorted = [...components].sort((a, b) => (a.z_index ?? 0) - (b.z_index ?? 0));
|
||||
|
||||
for (const c of sorted) {
|
||||
const x = pxToDots(c.x);
|
||||
@@ -34,25 +34,25 @@ export function generateZPL(layout: BarcodeLabelLayout): string {
|
||||
const h = pxToDots(c.height);
|
||||
|
||||
if (c.type === "text") {
|
||||
const fontH = Math.max(10, Math.min(120, (c.fontSize || 10) * 4)); // 대략적 변환
|
||||
const fontH = Math.max(10, Math.min(120, (c.font_size || 10) * 4)); // 대략적 변환
|
||||
const fontW = Math.round(fontH * 0.6);
|
||||
lines.push(`^FO${x},${y}`);
|
||||
lines.push(`^A0N,${fontH},${fontW}`);
|
||||
lines.push(`^FD${escapeZPL(c.content || "")}^FS`);
|
||||
} else if (c.type === "barcode") {
|
||||
if (c.barcodeType === "QR") {
|
||||
if (c.barcode_type === "QR") {
|
||||
const size = Math.min(w, h);
|
||||
const qrSize = Math.max(1, Math.min(10, Math.round(size / 20)));
|
||||
lines.push(`^FO${x},${y}`);
|
||||
lines.push(`^BQN,2,${qrSize}`);
|
||||
lines.push(`^FDQA,${escapeZPL(c.barcodeValue || "")}^FS`);
|
||||
lines.push(`^FDQA,${escapeZPL(c.barcode_value || "")}^FS`);
|
||||
} else {
|
||||
// CODE128: ^BC, CODE39: ^B3
|
||||
const mod = c.barcodeType === "CODE39" ? "^B3N" : "^BCN";
|
||||
const showText = c.showBarcodeText !== false ? "Y" : "N";
|
||||
const mod = c.barcode_type === "CODE39" ? "^B3N" : "^BCN";
|
||||
const showText = c.show_barcode_text !== false ? "Y" : "N";
|
||||
lines.push(`^FO${x},${y}`);
|
||||
lines.push(`${mod},${Math.max(20, h - 10)},${showText},N,N`);
|
||||
lines.push(`^FD${escapeZPL(c.barcodeValue || "")}^FS`);
|
||||
lines.push(`^FD${escapeZPL(c.barcode_value || "")}^FS`);
|
||||
}
|
||||
}
|
||||
// 이미지/선/사각형은 ZPL에서 비트맵 또는 ^GB 등으로 확장 가능 (생략)
|
||||
|
||||
Reference in New Issue
Block a user