다국어 키 자동생성 로직
This commit is contained in:
@@ -190,7 +190,7 @@ export const getLangKeys = async (
|
||||
res: Response
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const { companyCode, menuCode, keyType, searchText } = req.query;
|
||||
const { companyCode, menuCode, keyType, searchText, categoryId } = req.query;
|
||||
logger.info("다국어 키 목록 조회 요청", {
|
||||
query: req.query,
|
||||
user: req.user,
|
||||
@@ -202,6 +202,7 @@ export const getLangKeys = async (
|
||||
menuCode: menuCode as string,
|
||||
keyType: keyType as string,
|
||||
searchText: searchText as string,
|
||||
categoryId: categoryId ? parseInt(categoryId as string, 10) : undefined,
|
||||
});
|
||||
|
||||
const response: ApiResponse<any[]> = {
|
||||
|
||||
@@ -696,6 +696,21 @@ export class MultiLangService {
|
||||
values.push(params.menuCode);
|
||||
}
|
||||
|
||||
// 카테고리 필터 (하위 카테고리 포함)
|
||||
if (params.categoryId) {
|
||||
whereConditions.push(`category_id IN (
|
||||
WITH RECURSIVE category_tree AS (
|
||||
SELECT category_id FROM multi_lang_category WHERE category_id = $${paramIndex}
|
||||
UNION ALL
|
||||
SELECT c.category_id FROM multi_lang_category c
|
||||
INNER JOIN category_tree ct ON c.parent_id = ct.category_id
|
||||
)
|
||||
SELECT category_id FROM category_tree
|
||||
)`);
|
||||
values.push(params.categoryId);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// 검색 조건 (OR)
|
||||
if (params.searchText) {
|
||||
whereConditions.push(
|
||||
@@ -717,12 +732,13 @@ export class MultiLangService {
|
||||
lang_key: string;
|
||||
description: string | null;
|
||||
is_active: string | null;
|
||||
category_id: number | null;
|
||||
created_date: Date | null;
|
||||
created_by: string | null;
|
||||
updated_date: Date | null;
|
||||
updated_by: string | null;
|
||||
}>(
|
||||
`SELECT key_id, company_code, usage_note, lang_key, description, is_active,
|
||||
`SELECT key_id, company_code, usage_note, lang_key, description, is_active, category_id,
|
||||
created_date, created_by, updated_date, updated_by
|
||||
FROM multi_lang_key_master
|
||||
${whereClause}
|
||||
@@ -737,6 +753,7 @@ export class MultiLangService {
|
||||
langKey: key.lang_key,
|
||||
description: key.description || undefined,
|
||||
isActive: key.is_active || "Y",
|
||||
categoryId: key.category_id || undefined,
|
||||
createdDate: key.created_date || undefined,
|
||||
createdBy: key.created_by || undefined,
|
||||
updatedDate: key.updated_date || undefined,
|
||||
|
||||
@@ -35,6 +35,7 @@ interface LangKey {
|
||||
langKey: string;
|
||||
description: string;
|
||||
isActive: string;
|
||||
categoryId?: number;
|
||||
}
|
||||
|
||||
interface LangText {
|
||||
@@ -102,9 +103,14 @@ export default function I18nPage() {
|
||||
};
|
||||
|
||||
// 다국어 키 목록 조회
|
||||
const fetchLangKeys = async () => {
|
||||
const fetchLangKeys = async (categoryId?: number | null) => {
|
||||
try {
|
||||
const response = await apiClient.get("/multilang/keys");
|
||||
const params = new URLSearchParams();
|
||||
if (categoryId) {
|
||||
params.append("categoryId", categoryId.toString());
|
||||
}
|
||||
const url = `/multilang/keys${params.toString() ? `?${params.toString()}` : ""}`;
|
||||
const response = await apiClient.get(url);
|
||||
const data = response.data;
|
||||
if (data.success) {
|
||||
setLangKeys(data.data);
|
||||
@@ -481,6 +487,13 @@ export default function I18nPage() {
|
||||
initializeData();
|
||||
}, []);
|
||||
|
||||
// 카테고리 변경 시 키 목록 다시 조회
|
||||
useEffect(() => {
|
||||
if (!loading) {
|
||||
fetchLangKeys(selectedCategory?.categoryId);
|
||||
}
|
||||
}, [selectedCategory?.categoryId]);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
id: "select",
|
||||
@@ -879,7 +892,7 @@ export default function I18nPage() {
|
||||
companyCode={user?.companyCode || ""}
|
||||
isSuperAdmin={user?.companyCode === "*"}
|
||||
onSuccess={() => {
|
||||
fetchLangKeys();
|
||||
fetchLangKeys(selectedCategory?.categoryId);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -26,7 +26,8 @@ function CategoryNode({
|
||||
onSelectCategory,
|
||||
onDoubleClickCategory,
|
||||
}: CategoryNodeProps) {
|
||||
const [isExpanded, setIsExpanded] = useState(true);
|
||||
// 기본값: 접힌 상태로 시작
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const hasChildren = category.children && category.children.length > 0;
|
||||
const isSelected = selectedCategoryId === category.categoryId;
|
||||
|
||||
|
||||
@@ -1460,44 +1460,102 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
|
||||
try {
|
||||
// 모든 컴포넌트에서 라벨 정보 추출
|
||||
const labels: Array<{ componentId: string; label: string; type?: string }> = [];
|
||||
const addedLabels = new Set<string>(); // 중복 방지
|
||||
|
||||
const addLabel = (componentId: string, label: string, type: string) => {
|
||||
const key = `${label}_${type}`;
|
||||
if (label && label.trim() && !addedLabels.has(key)) {
|
||||
addedLabels.add(key);
|
||||
labels.push({ componentId, label: label.trim(), type });
|
||||
}
|
||||
};
|
||||
|
||||
const extractLabels = (components: ComponentData[]) => {
|
||||
components.forEach((comp) => {
|
||||
const anyComp = comp as any;
|
||||
const config = anyComp.componentConfig;
|
||||
|
||||
// 라벨 추출
|
||||
if (anyComp.label && typeof anyComp.label === "string" && anyComp.label.trim()) {
|
||||
labels.push({
|
||||
componentId: comp.id,
|
||||
label: anyComp.label.trim(),
|
||||
type: "label",
|
||||
});
|
||||
// 1. 기본 라벨 추출
|
||||
if (anyComp.label && typeof anyComp.label === "string") {
|
||||
addLabel(comp.id, anyComp.label, "label");
|
||||
}
|
||||
|
||||
// 제목 추출 (컨테이너, 카드 등)
|
||||
if (anyComp.title && typeof anyComp.title === "string" && anyComp.title.trim()) {
|
||||
labels.push({
|
||||
componentId: comp.id,
|
||||
label: anyComp.title.trim(),
|
||||
type: "title",
|
||||
});
|
||||
// 2. 제목 추출 (컨테이너, 카드 등)
|
||||
if (anyComp.title && typeof anyComp.title === "string") {
|
||||
addLabel(comp.id, anyComp.title, "title");
|
||||
}
|
||||
|
||||
// 버튼 텍스트 추출
|
||||
if (anyComp.componentConfig?.text && typeof anyComp.componentConfig.text === "string") {
|
||||
labels.push({
|
||||
componentId: comp.id,
|
||||
label: anyComp.componentConfig.text.trim(),
|
||||
type: "button",
|
||||
});
|
||||
// 3. 버튼 텍스트 추출
|
||||
if (config?.text && typeof config.text === "string") {
|
||||
addLabel(comp.id, config.text, "button");
|
||||
}
|
||||
|
||||
// placeholder 추출
|
||||
if (anyComp.placeholder && typeof anyComp.placeholder === "string" && anyComp.placeholder.trim()) {
|
||||
labels.push({
|
||||
componentId: comp.id,
|
||||
label: anyComp.placeholder.trim(),
|
||||
type: "placeholder",
|
||||
// 4. placeholder 추출
|
||||
if (anyComp.placeholder && typeof anyComp.placeholder === "string") {
|
||||
addLabel(comp.id, anyComp.placeholder, "placeholder");
|
||||
}
|
||||
|
||||
// 5. 테이블 컬럼 헤더 추출 (table-list, split-panel-layout 등)
|
||||
if (config?.columns && Array.isArray(config.columns)) {
|
||||
config.columns.forEach((col: any, index: number) => {
|
||||
if (col.displayName && typeof col.displayName === "string") {
|
||||
addLabel(`${comp.id}_col_${index}`, col.displayName, "column");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 6. 분할 패널 - 좌측/우측 패널 컬럼 추출
|
||||
if (config?.leftPanel?.columns && Array.isArray(config.leftPanel.columns)) {
|
||||
config.leftPanel.columns.forEach((col: any, index: number) => {
|
||||
if (col.displayName && typeof col.displayName === "string") {
|
||||
addLabel(`${comp.id}_left_col_${index}`, col.displayName, "column");
|
||||
}
|
||||
});
|
||||
}
|
||||
if (config?.rightPanel?.columns && Array.isArray(config.rightPanel.columns)) {
|
||||
config.rightPanel.columns.forEach((col: any, index: number) => {
|
||||
if (col.displayName && typeof col.displayName === "string") {
|
||||
addLabel(`${comp.id}_right_col_${index}`, col.displayName, "column");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 7. 검색 필터 필드 추출
|
||||
if (config?.filter?.filters && Array.isArray(config.filter.filters)) {
|
||||
config.filter.filters.forEach((filter: any, index: number) => {
|
||||
if (filter.label && typeof filter.label === "string") {
|
||||
addLabel(`${comp.id}_filter_${index}`, filter.label, "filter");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 8. 폼 필드 라벨 추출 (input-form 등)
|
||||
if (config?.fields && Array.isArray(config.fields)) {
|
||||
config.fields.forEach((field: any, index: number) => {
|
||||
if (field.label && typeof field.label === "string") {
|
||||
addLabel(`${comp.id}_field_${index}`, field.label, "field");
|
||||
}
|
||||
if (field.placeholder && typeof field.placeholder === "string") {
|
||||
addLabel(`${comp.id}_field_ph_${index}`, field.placeholder, "placeholder");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 9. 탭 라벨 추출
|
||||
if (config?.tabs && Array.isArray(config.tabs)) {
|
||||
config.tabs.forEach((tab: any, index: number) => {
|
||||
if (tab.label && typeof tab.label === "string") {
|
||||
addLabel(`${comp.id}_tab_${index}`, tab.label, "tab");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 10. 액션 버튼 추출
|
||||
if (config?.actions?.actions && Array.isArray(config.actions.actions)) {
|
||||
config.actions.actions.forEach((action: any, index: number) => {
|
||||
if (action.label && typeof action.label === "string") {
|
||||
addLabel(`${comp.id}_action_${index}`, action.label, "action");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user