Project

General

Profile

Feature #198 » FLOW_2檢驗.html

鎮源 游, 09/08/2026 01:58 AM

 
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MES Mobile Flow - 檢驗流程</title>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght=400;500;700&display=swap" rel="stylesheet">
<style>
:root {
--primary-color: #1e70e4;
--bg-gray: #f4f6f9;
--card-bg: #ffffff;
--text-dark: #333333;
--text-muted: #666666;
--border-radius: 16px;
--shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
--iqc-color: #0284c7;
--fqc-color: #0d9488;
--oqc-color: #7c3aed;
}

* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Noto Sans TC', sans-serif;
}

body {
background-color: #e0e0e0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}

.phone-container {
width: 430px;
height: 880px;
background-color: var(--bg-gray);
border-radius: 40px;
box-shadow: 0 12px 30px rgba(0,0,0,0.25);
border: 12px solid #1a1a1a;
position: relative;
overflow: hidden;
display: flex;
flex-direction: column;
}

.phone-header {
height: 30px;
background-color: #1a1a1a;
width: 100%;
position: absolute;
top: 0;
z-index: 999;
}

.app-bar {
background-color: #334155;
color: #ffffff;
text-align: center;
padding: 18px 0;
font-size: 1.1rem;
font-weight: 500;
letter-spacing: 1px;
margin-top: 8px;
position: relative;
}

.app-content {
flex: 1;
padding: 20px;
display: flex;
flex-direction: column;
justify-content: space-between;
overflow-y: auto;
height: calc(100% - 66px);
}

.flow-step {
display: none;
flex-direction: column;
height: 100%;
justify-content: space-between;
}

.flow-step.active {
display: flex;
}

.card-group {
display: flex;
flex-direction: column;
gap: 14px;
width: 100%;
overflow-y: auto;
padding-bottom: 8px;
}

.info-card {
background: var(--card-bg);
border-radius: var(--border-radius);
padding: 16px;
box-shadow: var(--shadow);
}

.info-label {
color: var(--text-muted);
font-size: 0.85rem;
margin-bottom: 4px;
}

.info-value {
color: var(--text-dark);
font-size: 1.2rem;
font-weight: 700;
}

.input-group {
background: var(--card-bg);
border-radius: var(--border-radius);
padding: 20px 16px;
box-shadow: var(--shadow);
margin-top: 20px;
display: flex;
flex-direction: column;
gap: 16px;
}

.input-item {
display: flex;
flex-direction: column;
}

.input-label {
font-size: 0.85rem;
color: var(--text-dark);
font-weight: 600;
margin-bottom: 6px;
}

.input-control {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 10px;
font-size: 1rem;
outline: none;
transition: border-color 0.2s;
}

.input-control:focus {
border-color: var(--primary-color);
}

.menu-btn {
background: var(--card-bg);
border: none;
border-radius: var(--border-radius);
padding: 18px;
font-size: 1.15rem;
font-weight: 600;
color: var(--text-dark);
box-shadow: var(--shadow);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
position: relative;
transition: transform 0.1s;
width: 100%;
}

.menu-btn:active {
transform: scale(0.98);
}

.menu-btn::before {
content: '';
position: absolute;
left: 0;
top: 15%;
height: 70%;
width: 6px;
border-radius: 0 4px 4px 0;
}

.btn-prod::before { background-color: #10b981; }
.btn-equip::before { background-color: #0284c7; }
.btn-inspect::before { background-color: #f59e0b; }

.btn-iqc::before { background-color: var(--iqc-color); }
.btn-fqc::before { background-color: var(--fqc-color); }
.btn-oqc::before { background-color: var(--oqc-color); }

/* 單據卡片 */
.ticket-card {
background: var(--card-bg);
border-radius: var(--border-radius);
padding: 16px;
box-shadow: var(--shadow);
border-left: 5px solid var(--primary-color);
cursor: pointer;
transition: transform 0.1s;
}

.ticket-card:active {
transform: scale(0.98);
}

.ticket-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}

.ticket-no {
font-weight: 700;
color: var(--primary-color);
font-size: 1rem;
}

.badge {
background: #e0f2fe;
color: #0369a1;
padding: 2px 8px;
border-radius: 12px;
font-size: 0.75rem;
font-weight: 600;
}

/* 檢驗填寫卡片 */
.inspect-item-card {
background: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 12px;
padding: 14px;
margin-bottom: 10px;
}

.inspect-item-title {
font-weight: 700;
color: #1e293b;
font-size: 0.95rem;
margin-bottom: 6px;
display: flex;
justify-content: space-between;
}

.inspect-spec {
font-size: 0.8rem;
color: #64748b;
margin-bottom: 10px;
}

.radio-group {
display: flex;
gap: 12px;
margin-top: 8px;
}

.radio-label {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.9rem;
cursor: pointer;
}

.btn-submit {
background-color: var(--primary-color);
color: white;
border: none;
border-radius: var(--border-radius);
padding: 16px;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
width: 100%;
box-shadow: 0 4px 12px rgba(30, 112, 228, 0.3);
margin-top: 10px;
}

.btn-back {
background-color: #e2e8f0;
color: #475569;
border: none;
border-radius: var(--border-radius);
padding: 14px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
width: 100%;
text-align: center;
margin-top: 10px;
}

.logo-area {
text-align: center;
margin-top: 30px;
}

.logo-box {
width: 80px;
height: 80px;
background-color: #cbd5e1;
border-radius: 20px;
margin: 0 auto 16px auto;
display: flex;
justify-content: center;
align-items: center;
color: #475569;
font-weight: bold;
border: 2px dashed #94a3b8;
}

.system-title {
color: var(--primary-color);
font-size: 1.5rem;
font-weight: 700;
}
</style>
</head>
<body>

<div class="phone-container">
<div class="phone-header"></div>
<div class="app-bar" id="appBarTitle">系統入口</div>

<div class="app-content">
<!-- STEP 1: 登入 -->
<div class="flow-step active" id="step-login">
<div>
<div class="logo-area">
<div class="logo-box">MES</div>
<div class="system-title">工廠智慧管理系統</div>
</div>
<div class="input-group">
<div class="input-item">
<label class="input-label">工號 USER ID</label>
<input type="text" class="input-control" value="RJ" id="userIdInput">
</div>
<div class="input-item">
<label class="input-label">密碼 PASSWORD</label>
<input type="password" class="input-control" value="••••••••" id="passwordInput">
</div>
</div>
</div>
<button class="btn-submit" onclick="navigateTo('step-main-menu', '選擇功能類別')">登入系統</button>
</div>

<!-- STEP 2: 功能選擇類別 (顯示品質檢驗) -->
<div class="flow-step" id="step-main-menu">
<div class="card-group">
<div class="info-card">
<div class="info-label">當前登入人員</div>
<div class="info-value user-name-display">RJ</div>
</div>
<button class="menu-btn btn-prod" onclick="alert('前往生產管理')">
<span>生產管理</span>
</button>
<button class="menu-btn btn-equip" onclick="alert('前往設備管理')">
<span>設備管理</span>
</button>
<button class="menu-btn btn-inspect" onclick="navigateTo('step-inspect-category', '選擇檢驗類別')">
<span>品質檢驗</span>
</button>
</div>
<button class="btn-back" onclick="navigateTo('step-login', '系統入口')">登出</button>
</div>

<!-- STEP 3: 選擇檢驗類別 (IQC / FQC / OQC) -->
<div class="flow-step" id="step-inspect-category">
<div class="card-group">
<div class="info-card">
<div class="info-label">品質檢驗模組</div>
<div class="info-value">請選擇檢驗作業種類</div>
</div>
<button class="menu-btn btn-iqc" onclick="selectCategory('IQC')">
<span>IQC 進料檢驗</span>
</button>
<button class="menu-btn btn-fqc" onclick="selectCategory('FQC')">
<span>FQC 成品檢驗</span>
</button>
<button class="menu-btn btn-oqc" onclick="selectCategory('OQC')">
<span>OQC 出貨檢驗</span>
</button>
</div>
<button class="btn-back" onclick="navigateTo('step-main-menu', '選擇功能類別')">返回主選單</button>
</div>

<!-- STEP 4: 檢驗單據列表 -->
<div class="flow-step" id="step-ticket-list">
<div class="card-group" id="ticketListContainer">
<!-- 動態載入單據 -->
</div>
<button class="btn-back" onclick="navigateTo('step-inspect-category', '選擇檢驗類別')">返回類別選擇</button>
</div>

<!-- STEP 5: 點擊單據直接開始填寫表單 -->
<div class="flow-step" id="step-inspect-form">
<div class="card-group">
<div class="info-card" style="padding: 14px;" id="formHeaderCard">
<div style="font-weight:700; color:var(--primary-color); font-size:1.05rem; margin-bottom:4px;" id="infoTicketNo">-</div>
<div style="font-size:0.85rem; color:#475569;" id="infoMeta">-</div>
</div>

<div id="inspectItemsContainer"></div>

<div class="input-item">
<label class="input-label">現場檢驗備註</label>
<input type="text" class="input-control" placeholder="請輸入備註說明 (選填)" id="inspectRemark">
</div>
</div>

<div>
<button class="btn-submit" onclick="submitInspection()">💾 完成並送出檢驗結果</button>
<button class="btn-back" onclick="navigateTo('step-ticket-list', currentCategory + ' 檢驗單列表')">取消返回</button>
</div>
</div>

</div>
</div>

<script>
let currentCategory = '';
let currentTicket = null;

// 模擬各類型檢驗單據
const mockData = {
'IQC': [
{ ticketNo: 'IQC-20260908-01', refNo: 'PO-20260901-01', prodName: 'T8 LED 燈管', batchNo: 'B20260901', qty: 10 },
{ ticketNo: 'IQC-20260908-02', refNo: 'PO-20260901-08', prodName: '鋁合金散熱片', batchNo: 'B20260902', qty: 25 }
],
'FQC': [
{ ticketNo: 'FQC-20260908-01', refNo: 'WO-20260830-05', prodName: '藍牙耳機-標準版', batchNo: 'FB202608', qty: 5 },
{ ticketNo: 'FQC-20260908-02', refNo: 'WO-20260901-12', prodName: '鋰電池模組-A型', batchNo: 'FB202609', qty: 10 }
],
'OQC': [
{ ticketNo: 'OQC-20260908-01', refNo: 'DO-20260905-02', prodName: '智慧手錶-高配版', batchNo: 'OB202609', qty: 12 }
]
};

// 預設檢驗項目
const inspectItemsTemplate = [
{ itemName: '外觀刮傷與瑕疵檢查', condition: 'Boolean', spec: '規範:全機無明顯刮傷/凹痕', defaultVal: 'PASS' },
{ itemName: '關鍵電壓/尺寸測試', condition: 'RANGE', spec: '規範:11.5V ~ 12.5V', defaultVal: '12.0' },
{ itemName: '功能組裝確認', condition: 'Boolean', spec: '規範:零組件結構無鬆脫', defaultVal: 'PASS' }
];

// 選擇 IQC / FQC / OQC 種類
function selectCategory(category) {
currentCategory = category;
const container = document.getElementById('ticketListContainer');
container.innerHTML = '';

const list = mockData[category] || [];
if (list.length === 0) {
container.innerHTML = '<div class="info-card" style="text-align:center; color:#94a3b8;">目前無待檢驗單據</div>';
} else {
list.forEach(t => {
const card = document.createElement('div');
card.className = 'ticket-card';
card.onclick = () => startDirectInspection(t);
card.innerHTML = `
<div class="ticket-header">
<span class="ticket-no">${t.ticketNo}</span>
<span class="badge">${category}</span>
</div>
<div style="font-size:0.85rem; color:var(--text-muted); display:grid; grid-template-columns: 1fr 1fr; gap:6px;">
<div>來源:${t.refNo}</div>
<div>抽檢數:<b>${t.qty} 件</b></div>
<div style="grid-column: span 2;">產品:${t.prodName}</div>
<div style="grid-column: span 2;">批號:${t.batchNo}</div>
</div>
`;
container.appendChild(card);
});
}

navigateTo('step-ticket-list', `${category} 檢驗單列表`);
}

// 點擊單據 -> 直接開始檢驗 (跳過詳細資訊頁)
function startDirectInspection(ticket) {
currentTicket = ticket;

document.getElementById('infoTicketNo').innerText = `單號:${ticket.ticketNo}`;
document.getElementById('infoMeta').innerText = `產品:${ticket.prodName} | 批號:${ticket.batchNo} (${ticket.qty} 件)`;

const container = document.getElementById('inspectItemsContainer');
container.innerHTML = '';

inspectItemsTemplate.forEach((item, idx) => {
const itemDiv = document.createElement('div');
itemDiv.className = 'inspect-item-card';
let inputHtml = '';
if(item.condition === 'Boolean') {
inputHtml = `
<div class="radio-group">
<label class="radio-label"><input type="radio" name="item_${idx}" value="PASS" checked> 合格 (PASS)</label>
<label class="radio-label"><input type="radio" name="item_${idx}" value="FAIL"> 不合格 (FAIL)</label>
</div>
`;
} else {
inputHtml = `
<input type="text" class="input-control" value="${item.defaultVal}" placeholder="請輸入實測數值">
`;
}

itemDiv.innerHTML = `
<div class="inspect-item-title">
<span>${idx + 1}. ${item.itemName}</span>
<span style="font-size:0.75rem; color:#0369a1; background:#e0f2fe; padding:2px 6px; border-radius:4px;">${item.condition}</span>
</div>
<div class="inspect-spec">${item.spec}</div>
${inputHtml}
`;
container.appendChild(itemDiv);
});

navigateTo('step-inspect-form', `填寫 ${currentCategory} 檢驗單`);
}

function navigateTo(stepId, title) {
document.querySelectorAll('.flow-step').forEach(step => step.classList.remove('active'));
const targetStep = document.getElementById(stepId);
if (targetStep) targetStep.classList.add('active');

document.getElementById('appBarTitle').innerText = title;

if(stepId === 'step-main-menu') {
const userId = document.getElementById('userIdInput').value || 'RJ';
document.querySelectorAll('.user-name-display').forEach(el => el.innerText = userId);
}
}

function submitInspection() {
alert(`🎉 [${currentTicket.ticketNo}] 檢驗紀錄已成功儲存送出!`);
navigateTo('step-ticket-list', `${currentCategory} 檢驗單列表`);
}
</script>
</body>
</html>
(1-1/2)