import { ASSET_SCHEMA, UI_TEXT } from '../../core/schema'; import { dynamicSort, renderPageHeader, calculateAssetAge } from '../../core/utils'; import { setupTableSorting, SortState } from '../../core/tableHandler'; import { renderFilterBar, applyCommonFilters } from '../../core/filterHandler'; import { state } from '../../core/state'; import { IMAGE_LOCATIONS } from '../../components/Modal/SharedData'; declare var Chart: any; let pcFlowChartInstance: any = null; // ─── 100점 만점 감점형 성능 점수 계산 (CPU + RAM + GPU + 연식) ─── function calculatePcScoreDeductive(cpu: string, ram: string, gpu: string, purchaseDate: string): number { let score = 100; if (!cpu) cpu = ''; if (!ram) ram = ''; if (!gpu) gpu = ''; const cpuUpper = cpu.toUpperCase(); const ramUpper = ram.toUpperCase(); const gpuUpper = gpu.toUpperCase(); // 1. CPU 등급 감점 (최대 -30점) let cpuDeduction = 0; if (cpuUpper.includes('I9') || cpuUpper.includes('RYZEN 9') || cpuUpper.includes('RYZEN9')) { cpuDeduction = 0; } else if (cpuUpper.includes('I7') || cpuUpper.includes('RYZEN 7') || cpuUpper.includes('RYZEN7')) { cpuDeduction = 5; } else if (cpuUpper.includes('I5') || cpuUpper.includes('RYZEN 5') || cpuUpper.includes('RYZEN5')) { cpuDeduction = 15; } else if (cpuUpper.includes('I3') || cpuUpper.includes('RYZEN 3') || cpuUpper.includes('RYZEN3')) { cpuDeduction = 25; } else { cpuDeduction = 30; } score -= cpuDeduction; // 2. CPU 세대 노후 감점 (최대 -15점) let genDeduction = 0; const intelMatch = cpuUpper.match(/I\d-?(\d+)/); let gen = 0; if (intelMatch && intelMatch[1]) { const numStr = intelMatch[1]; if (numStr.length === 5) gen = parseInt(numStr.substring(0, 2), 10); else if (numStr.length === 4) gen = parseInt(numStr.substring(0, 1), 10); } const amdMatch = cpuUpper.match(/RYZEN\s?\d\s?-?(\d+)/); let amdGen = 0; if (amdMatch && amdMatch[1] && !intelMatch) { const numStr = amdMatch[1]; if (numStr.length === 4) amdGen = parseInt(numStr.substring(0, 1), 10); } if (intelMatch) { if (gen >= 12) genDeduction = 0; else if (gen >= 10) genDeduction = 5; else if (gen >= 8) genDeduction = 10; else genDeduction = 15; } else if (amdMatch) { if (amdGen >= 5) genDeduction = 0; else if (amdGen >= 3) genDeduction = 5; else genDeduction = 10; } else { genDeduction = 15; } score -= genDeduction; // 3. RAM 용량 감점 (최대 -25점) const ramMatch = ramUpper.match(/(\d+)\s*GB/); let ramDeduction = 25; if (ramMatch && ramMatch[1]) { const ramVal = parseInt(ramMatch[1], 10); if (ramVal >= 32) ramDeduction = 0; else if (ramVal >= 16) ramDeduction = 10; else if (ramVal >= 8) ramDeduction = 20; else ramDeduction = 25; } score -= ramDeduction; // 4. GPU 성능 감점 (최대 -25점) let gpuDeduction = 25; if (!gpuUpper || gpuUpper === '-' || gpuUpper.trim() === '') { gpuDeduction = 25; } else if ( gpuUpper.includes('RTX 4090') || gpuUpper.includes('RTX 4080') || gpuUpper.includes('RTX 4070') || gpuUpper.includes('RTX 3090') || gpuUpper.includes('RTX 3080') || gpuUpper.includes('RTX A5000') || gpuUpper.includes('RTX A6000') || gpuUpper.includes('RTX A4000') ) { gpuDeduction = 0; } else if ( gpuUpper.includes('RTX 3070') || gpuUpper.includes('RTX 3060') || gpuUpper.includes('RTX 2060') || gpuUpper.includes('RTX A2000') || gpuUpper.includes('RTX A3000') || gpuUpper.includes('QUADRO') ) { gpuDeduction = 5; } else if ( gpuUpper.includes('GTX 1660') || gpuUpper.includes('GTX 1080') || gpuUpper.includes('GTX 1070') || gpuUpper.includes('GTX 1060') || gpuUpper.includes('RX 6700') || gpuUpper.includes('RX 6600') ) { gpuDeduction = 15; } else { gpuDeduction = 25; } score -= gpuDeduction; // 5. 연식(노후도) 감점 (최대 -15점) let age = 0; if (purchaseDate && purchaseDate !== '-') { let normalized = purchaseDate.replace(/\./g, '-').trim(); if (/^\d{6}$/.test(normalized)) { normalized = `${normalized.substring(0, 4)}-${normalized.substring(4, 6)}`; } const purchase = new Date(normalized); if (!isNaN(purchase.getTime())) { // 2026년 5월 31일 기준 경과연수 계산 const mockToday = new Date('2026-05-31'); const diffMs = mockToday.getTime() - purchase.getTime(); age = diffMs / (1000 * 60 * 60 * 24 * 365.25); age = Math.max(0, parseFloat(age.toFixed(1))); } } let ageDeduction = 0; if (age < 1) ageDeduction = 0; else if (age < 2) ageDeduction = 3; else if (age < 3) ageDeduction = 6; else if (age < 4) ageDeduction = 9; else if (age < 5) ageDeduction = 12; else ageDeduction = 15; score -= ageDeduction; return Math.max(10, score); } export interface ColumnDef { header: string; sortKey?: string; width?: string; align?: 'left' | 'center' | 'right'; className?: string; render: (asset: any) => string; } export interface ListViewConfig { title: string; dataSource: () => any[]; searchKeys: string[]; filterOptions: { keywordLabel: string; showCorp?: boolean; showDept?: boolean; showLoc?: boolean; showField?: boolean; showType?: boolean; showStatus?: boolean; }; columns: ColumnDef[]; onRowClick?: (asset: any) => void; emptyMessage?: string; persistentSortState?: SortState; } export function createListView(container: HTMLElement, config: ListViewConfig) { // 1. 컨테이너 초기화 및 헤더 렌더링 container.innerHTML = ''; renderPageHeader(container, config.title); const fullList = config.dataSource(); let sortState: SortState = config.persistentSortState || { key: '', direction: 'asc' }; if (!(state as any).listFilters) { (state as any).listFilters = {}; } const filterKey = config.title; if (!(state as any).listFilters[filterKey]) { (state as any).listFilters[filterKey] = { keyword: '', corp: '', dept: '', loc: '', field: '', type: '', status: '' }; } let currentFilters: any = (state as any).listFilters[filterKey]; // 서버 및 PC 탭이 아닐 경우 '자산 현황' 뷰 진입 방지 및 강제 'asset' 모드 const isServerOrPc = config.title === '서버' || config.title === 'PC'; if (!isServerOrPc) { (state as any).currentViewMode = 'asset'; } else if (!(state as any).currentViewMode) { (state as any).currentViewMode = 'system'; } // 2. 뷰 전환 토글 버튼 생성 const toggleWrapper = document.createElement('div'); toggleWrapper.className = 'view-toggle-container'; const showPcFlowBtn = config.title === 'PC'; toggleWrapper.innerHTML = `
| 일자 | 담당자 | 구분 | 사용자 | 인수자 | 자산번호 | 상세 |
|---|---|---|---|---|---|---|
| 분류 | 용도/자산명 | 관리자(정) | 관리자(부) | 상세위치 |
| 사용자 | 부서 (직무) | 상태 | 자산코드 |
|---|---|---|---|
| 사양 주의 자산이 없습니다. | |||
목록에서 자산을 선택하면
상세 정보와 배치도가 표시됩니다.