diff --git a/system1-factory/api/controllers/purchaseRequestController.js b/system1-factory/api/controllers/purchaseRequestController.js index 2e6ae05..d89cc82 100644 --- a/system1-factory/api/controllers/purchaseRequestController.js +++ b/system1-factory/api/controllers/purchaseRequestController.js @@ -225,9 +225,14 @@ const PurchaseRequestController = { if (existing.length > 0) { itemId = existing[0].item_id; } else { + // 신규 품목 사진 저장 (마스터에) + let itemPhotoPath = null; + if (item.item_photo) { + itemPhotoPath = await saveBase64Image(item.item_photo, 'item', 'consumables'); + } const [ins] = await conn.query( - `INSERT INTO consumable_items (item_name, spec, maker, category, is_active) VALUES (?, ?, ?, ?, 1)`, - [item.item_name.trim(), item.spec || null, item.maker || null, item.category || 'consumable'] + `INSERT INTO consumable_items (item_name, spec, maker, category, photo_path, is_active) VALUES (?, ?, ?, ?, ?, 1)`, + [item.item_name.trim(), item.spec || null, item.maker || null, item.category || 'consumable', itemPhotoPath] ); itemId = ins.insertId; newItemRegistered = true; @@ -261,6 +266,25 @@ const PurchaseRequestController = { } }, + // 품목 마스터 사진 등록/업데이트 + updateItemPhoto: async (req, res) => { + try { + const { photo } = req.body; + if (!photo) return res.status(400).json({ success: false, message: '사진을 첨부해주세요.' }); + const itemPhotoPath = await saveBase64Image(photo, 'item', 'consumables'); + if (!itemPhotoPath) return res.status(500).json({ success: false, message: '사진 저장에 실패했습니다.' }); + + const { getDb } = require('../dbPool'); + const db = await getDb(); + await db.query('UPDATE consumable_items SET photo_path = ? WHERE item_id = ?', [itemPhotoPath, req.params.id]); + koreanSearch.clearCache(); + res.json({ success: true, data: { photo_path: itemPhotoPath }, message: '품목 사진이 등록되었습니다.' }); + } catch (err) { + logger.error('updateItemPhoto error:', err); + res.status(500).json({ success: false, message: '서버 오류가 발생했습니다.' }); + } + }, + // 스마트 검색 (초성 + 별칭 + substring) search: async (req, res) => { try { diff --git a/system1-factory/api/routes/purchaseRequestRoutes.js b/system1-factory/api/routes/purchaseRequestRoutes.js index ca46250..3e31498 100644 --- a/system1-factory/api/routes/purchaseRequestRoutes.js +++ b/system1-factory/api/routes/purchaseRequestRoutes.js @@ -7,6 +7,7 @@ const requirePage = createRequirePage(getDb); // 보조 데이터 router.get('/consumable-items', ctrl.getConsumableItems); +router.put('/consumable-items/:id/photo', ctrl.updateItemPhoto); router.get('/vendors', ctrl.getVendors); router.get('/search', ctrl.search); diff --git a/system1-factory/api/services/imageUploadService.js b/system1-factory/api/services/imageUploadService.js index 73e9dc7..d494525 100644 --- a/system1-factory/api/services/imageUploadService.js +++ b/system1-factory/api/services/imageUploadService.js @@ -24,7 +24,8 @@ const UPLOAD_DIRS = { issues: path.join(__dirname, '../uploads/issues'), equipments: path.join(__dirname, '../uploads/equipments'), purchase_requests: path.join(__dirname, '../uploads/purchase_requests'), - purchase_received: path.join(__dirname, '../uploads/purchase_received') + purchase_received: path.join(__dirname, '../uploads/purchase_received'), + consumables: path.join(__dirname, '../uploads/consumables') }; const UPLOAD_DIR = UPLOAD_DIRS.issues; // 기존 호환성 유지 const MAX_SIZE = { width: 1920, height: 1920 }; diff --git a/system1-factory/web/css/purchase-mobile.css b/system1-factory/web/css/purchase-mobile.css index 506e37b..ccc8550 100644 --- a/system1-factory/web/css/purchase-mobile.css +++ b/system1-factory/web/css/purchase-mobile.css @@ -184,6 +184,26 @@ display: none; } .pm-search-results.open { display: block; } +.pm-search-thumb { + width: 36px; + height: 36px; + border-radius: 6px; + object-fit: cover; + flex-shrink: 0; + background: #f3f4f6; +} +.pm-search-thumb-empty { + width: 36px; + height: 36px; + border-radius: 6px; + background: #f3f4f6; + display: flex; + align-items: center; + justify-content: center; + color: #d1d5db; + font-size: 14px; + flex-shrink: 0; +} .pm-search-item { padding: 10px 12px; font-size: 14px; @@ -255,6 +275,25 @@ font-size: 12px; flex-shrink: 0; } +.pm-cart-thumb { + width: 40px; + height: 40px; + border-radius: 6px; + object-fit: cover; + flex-shrink: 0; + background: #f3f4f6; +} +.pm-cart-photo-btn { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 3px 8px; + border: 1px dashed #d1d5db; + border-radius: 4px; + font-size: 11px; + color: #6b7280; + cursor: pointer; +} .pm-cart-remove { width: 24px; height: 24px; diff --git a/system1-factory/web/pages/purchase/request-mobile.html b/system1-factory/web/pages/purchase/request-mobile.html index 6e51717..c555a9a 100644 --- a/system1-factory/web/pages/purchase/request-mobile.html +++ b/system1-factory/web/pages/purchase/request-mobile.html @@ -6,8 +6,8 @@