feat: add item list mode configuration and screen code handling
- Introduced `itemListMode` to the process work standard configuration, allowing users to select between displaying all items or only registered items. - Added `screenCode` to automatically set the screen ID when in registered mode. - Updated the `ProcessWorkStandardComponent` to handle the new configuration and adjust item fetching logic accordingly. - Enhanced the `ProcessWorkStandardConfigPanel` to include a select input for item list mode, improving user experience and configurability. These changes aim to enhance the flexibility and usability of the process work standard component. Made-with: Cursor
This commit is contained in:
parent
bd08b341f0
commit
429f1ba6ee
|
|
@ -17,25 +17,37 @@ interface ProcessWorkStandardComponentProps {
|
||||||
formData?: Record<string, any>;
|
formData?: Record<string, any>;
|
||||||
isPreview?: boolean;
|
isPreview?: boolean;
|
||||||
tableName?: string;
|
tableName?: string;
|
||||||
|
screenId?: number | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ProcessWorkStandardComponent({
|
export function ProcessWorkStandardComponent({
|
||||||
config: configProp,
|
config: configProp,
|
||||||
isPreview,
|
isPreview,
|
||||||
|
screenId,
|
||||||
}: ProcessWorkStandardComponentProps) {
|
}: ProcessWorkStandardComponentProps) {
|
||||||
|
const resolvedConfig = useMemo(() => {
|
||||||
|
const merged = {
|
||||||
|
...configProp,
|
||||||
|
};
|
||||||
|
if (merged.itemListMode === "registered" && !merged.screenCode && screenId) {
|
||||||
|
merged.screenCode = `screen_${screenId}`;
|
||||||
|
}
|
||||||
|
return merged;
|
||||||
|
}, [configProp, screenId]);
|
||||||
|
|
||||||
const config: ProcessWorkStandardConfig = useMemo(
|
const config: ProcessWorkStandardConfig = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...defaultConfig,
|
...defaultConfig,
|
||||||
...configProp,
|
...resolvedConfig,
|
||||||
dataSource: { ...defaultConfig.dataSource, ...configProp?.dataSource },
|
dataSource: { ...defaultConfig.dataSource, ...resolvedConfig?.dataSource },
|
||||||
phases: configProp?.phases?.length
|
phases: resolvedConfig?.phases?.length
|
||||||
? configProp.phases
|
? resolvedConfig.phases
|
||||||
: defaultConfig.phases,
|
: defaultConfig.phases,
|
||||||
detailTypes: configProp?.detailTypes?.length
|
detailTypes: resolvedConfig?.detailTypes?.length
|
||||||
? configProp.detailTypes
|
? resolvedConfig.detailTypes
|
||||||
: defaultConfig.detailTypes,
|
: defaultConfig.detailTypes,
|
||||||
}),
|
}),
|
||||||
[configProp]
|
[resolvedConfig]
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
@ -46,7 +58,8 @@ export function ProcessWorkStandardComponent({
|
||||||
selectedDetailsByPhase,
|
selectedDetailsByPhase,
|
||||||
selection,
|
selection,
|
||||||
loading,
|
loading,
|
||||||
fetchItems,
|
isRegisteredMode,
|
||||||
|
loadItems,
|
||||||
selectItem,
|
selectItem,
|
||||||
selectProcess,
|
selectProcess,
|
||||||
fetchWorkItemDetails,
|
fetchWorkItemDetails,
|
||||||
|
|
@ -112,8 +125,8 @@ export function ProcessWorkStandardComponent({
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleInit = useCallback(() => {
|
const handleInit = useCallback(() => {
|
||||||
fetchItems();
|
loadItems();
|
||||||
}, [fetchItems]);
|
}, [loadItems]);
|
||||||
|
|
||||||
const splitRatio = config.splitRatio || 30;
|
const splitRatio = config.splitRatio || 30;
|
||||||
|
|
||||||
|
|
@ -144,7 +157,7 @@ export function ProcessWorkStandardComponent({
|
||||||
items={items}
|
items={items}
|
||||||
routings={routings}
|
routings={routings}
|
||||||
selection={selection}
|
selection={selection}
|
||||||
onSearch={(keyword) => fetchItems(keyword)}
|
onSearch={(keyword) => loadItems(keyword)}
|
||||||
onSelectItem={selectItem}
|
onSelectItem={selectItem}
|
||||||
onSelectProcess={selectProcess}
|
onSelectProcess={selectProcess}
|
||||||
onInit={handleInit}
|
onInit={handleInit}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { ProcessWorkStandardConfig, WorkPhaseDefinition, DetailTypeDefinition } from "./types";
|
import { ProcessWorkStandardConfig, WorkPhaseDefinition, DetailTypeDefinition } from "./types";
|
||||||
import { defaultConfig } from "./config";
|
import { defaultConfig } from "./config";
|
||||||
|
|
||||||
|
|
@ -81,6 +82,30 @@ export function ProcessWorkStandardConfigPanel({
|
||||||
<div className="space-y-5 p-4">
|
<div className="space-y-5 p-4">
|
||||||
<h3 className="text-sm font-semibold">공정 작업기준 설정</h3>
|
<h3 className="text-sm font-semibold">공정 작업기준 설정</h3>
|
||||||
|
|
||||||
|
{/* 품목 목록 모드 */}
|
||||||
|
<section className="space-y-3">
|
||||||
|
<p className="text-xs font-medium text-muted-foreground">품목 목록 모드</p>
|
||||||
|
<div>
|
||||||
|
<Select
|
||||||
|
value={config.itemListMode || "all"}
|
||||||
|
onValueChange={(v) => update({ itemListMode: v as "all" | "registered" })}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="h-8 text-xs">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="all">전체 품목</SelectItem>
|
||||||
|
<SelectItem value="registered">등록 품목만</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<p className="mt-1 text-[10px] text-muted-foreground">
|
||||||
|
{config.itemListMode === "registered"
|
||||||
|
? "품목별 라우팅 탭에서 등록한 품목만 표시됩니다. screenCode는 화면 ID 기준으로 자동 설정됩니다."
|
||||||
|
: "모든 품목을 표시합니다."}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
{/* 데이터 소스 설정 */}
|
{/* 데이터 소스 설정 */}
|
||||||
<section className="space-y-3">
|
<section className="space-y-3">
|
||||||
<p className="text-xs font-medium text-muted-foreground">데이터 소스 설정</p>
|
<p className="text-xs font-medium text-muted-foreground">데이터 소스 설정</p>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export class ProcessWorkStandardRenderer extends AutoRegisteringComponentRendere
|
||||||
static componentDefinition = V2ProcessWorkStandardDefinition;
|
static componentDefinition = V2ProcessWorkStandardDefinition;
|
||||||
|
|
||||||
render(): React.ReactElement {
|
render(): React.ReactElement {
|
||||||
const { formData, isPreview, config, tableName } = this.props as Record<
|
const { formData, isPreview, config, tableName, screenId } = this.props as Record<
|
||||||
string,
|
string,
|
||||||
unknown
|
unknown
|
||||||
>;
|
>;
|
||||||
|
|
@ -20,6 +20,7 @@ export class ProcessWorkStandardRenderer extends AutoRegisteringComponentRendere
|
||||||
formData={formData as Record<string, unknown>}
|
formData={formData as Record<string, unknown>}
|
||||||
tableName={tableName as string}
|
tableName={tableName as string}
|
||||||
isPreview={isPreview as boolean}
|
isPreview={isPreview as boolean}
|
||||||
|
screenId={screenId as number | string}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,4 +31,6 @@ export const defaultConfig: ProcessWorkStandardConfig = {
|
||||||
splitRatio: 30,
|
splitRatio: 30,
|
||||||
leftPanelTitle: "품목 및 공정 선택",
|
leftPanelTitle: "품목 및 공정 선택",
|
||||||
readonly: false,
|
readonly: false,
|
||||||
|
itemListMode: "all",
|
||||||
|
screenCode: "",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,9 @@ export function useProcessWorkStandard(config: ProcessWorkStandardConfig) {
|
||||||
processName: null,
|
processName: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 품목 목록 조회
|
const isRegisteredMode = config.itemListMode === "registered";
|
||||||
|
|
||||||
|
// 품목 목록 조회 (전체 모드)
|
||||||
const fetchItems = useCallback(
|
const fetchItems = useCallback(
|
||||||
async (search?: string) => {
|
async (search?: string) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -59,6 +61,53 @@ export function useProcessWorkStandard(config: ProcessWorkStandardConfig) {
|
||||||
[config.dataSource]
|
[config.dataSource]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 등록 품목 조회 (등록 모드)
|
||||||
|
const fetchRegisteredItems = useCallback(
|
||||||
|
async (search?: string) => {
|
||||||
|
const screenCode = config.screenCode;
|
||||||
|
if (!screenCode) {
|
||||||
|
console.warn("screenCode가 설정되지 않았습니다");
|
||||||
|
setItems([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const ds = config.dataSource;
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
tableName: ds.itemTable,
|
||||||
|
nameColumn: ds.itemNameColumn,
|
||||||
|
codeColumn: ds.itemCodeColumn,
|
||||||
|
routingTable: ds.routingVersionTable,
|
||||||
|
routingFkColumn: ds.routingFkColumn,
|
||||||
|
...(search ? { search } : {}),
|
||||||
|
});
|
||||||
|
const res = await apiClient.get(
|
||||||
|
`${API_BASE}/registered-items/${encodeURIComponent(screenCode)}?${params}`
|
||||||
|
);
|
||||||
|
if (res.data?.success) {
|
||||||
|
setItems(res.data.data || []);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("등록 품목 조회 실패", err);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[config.dataSource, config.screenCode]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 모드에 따라 적절한 함수 호출
|
||||||
|
const loadItems = useCallback(
|
||||||
|
async (search?: string) => {
|
||||||
|
if (isRegisteredMode) {
|
||||||
|
await fetchRegisteredItems(search);
|
||||||
|
} else {
|
||||||
|
await fetchItems(search);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[isRegisteredMode, fetchItems, fetchRegisteredItems]
|
||||||
|
);
|
||||||
|
|
||||||
// 라우팅 + 공정 조회
|
// 라우팅 + 공정 조회
|
||||||
const fetchRoutings = useCallback(
|
const fetchRoutings = useCallback(
|
||||||
async (itemCode: string) => {
|
async (itemCode: string) => {
|
||||||
|
|
@ -340,7 +389,10 @@ export function useProcessWorkStandard(config: ProcessWorkStandardConfig) {
|
||||||
selection,
|
selection,
|
||||||
loading,
|
loading,
|
||||||
saving,
|
saving,
|
||||||
|
isRegisteredMode,
|
||||||
fetchItems,
|
fetchItems,
|
||||||
|
fetchRegisteredItems,
|
||||||
|
loadItems,
|
||||||
selectItem,
|
selectItem,
|
||||||
selectProcess,
|
selectProcess,
|
||||||
fetchWorkItems,
|
fetchWorkItems,
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,10 @@ export interface ProcessWorkStandardConfig {
|
||||||
splitRatio?: number;
|
splitRatio?: number;
|
||||||
leftPanelTitle?: string;
|
leftPanelTitle?: string;
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
|
/** 품목 목록 모드: all=전체, registered=등록된 품목만 */
|
||||||
|
itemListMode?: "all" | "registered";
|
||||||
|
/** 등록 모드 시 화면 코드 (자동 설정됨) */
|
||||||
|
screenCode?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -121,6 +125,7 @@ export interface ProcessWorkStandardComponentProps {
|
||||||
formData?: Record<string, any>;
|
formData?: Record<string, any>;
|
||||||
isPreview?: boolean;
|
isPreview?: boolean;
|
||||||
tableName?: string;
|
tableName?: string;
|
||||||
|
screenId?: number | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 선택 상태
|
// 선택 상태
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue