Project

General

Profile

Feature #139 » 設備管理_0716.html

鎮源 游, 07/16/2026 03:44 AM

 
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>設備主檔 - 狀態變更維護控制系統</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<style>
:root {
--primary-blue: #1e88e5;
--btn-blue: #2563eb;
--btn-green: #10b981;
--border-color: #e2e8f0;
--text-main: #1e293b;
--text-sub: #64748b;
/* 狀態色彩 */
--status-running: #10b981; /* 運轉中 - 綠 */
--status-standby: #2563eb; /* 待機 - 藍 */
--status-repair: #f59e0b; /* 維護中/故障 - 橘 */
}

* { box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif; }
body { background-color: #f1f5f9; margin: 0; padding: 20px; color: var(--text-main); }

.container { max-width: 1450px; margin: 0 auto; background: #ffffff; border-radius: 8px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1); padding: 24px; }
/* 標題與頂部工具列 */
.header-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; border-bottom: 2px solid var(--border-color); padding-bottom: 12px; }
.main-title { font-size: 1.5rem; font-weight: bold; color: #0f172a; margin: 0; }

/* 快速篩選區 */
.filter-section { margin-bottom: 20px; background: #f8fafc; padding: 16px; border-radius: 8px; border: 1px solid var(--border-color); }
.filter-label { font-size: 0.85rem; font-weight: bold; color: var(--text-sub); margin-bottom: 8px; }
.filter-group { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
.filter-tag {
background-color: #ffffff; border: 1px solid #cbd5e1; border-radius: 6px;
padding: 6px 16px; font-size: 0.85rem; cursor: pointer; font-weight: 500; transition: all 0.2s;
}
.filter-tag.active { background-color: #3b82f6; color: white; border-color: #2563eb; }

/* 搜尋列 */
.search-row { display: flex; gap: 10px; margin-bottom: 20px; }
.search-input { flex: 1; padding: 10px 14px; border: 1px solid #cbd5e1; border-radius: 6px; font-size: 0.9rem; }
.btn-search { background-color: #2563eb; color: white; padding: 0 20px; border-radius: 6px; font-weight: bold; border: none; cursor: pointer; }
.btn-clear { background-color: #64748b; color: white; padding: 0 20px; border-radius: 6px; font-weight: bold; border: none; cursor: pointer; }

/* 表格樣式 */
.table-responsive { width: 100%; overflow-x: auto; border: 1px solid var(--border-color); border-radius: 8px; background: white; }
table { width: 100%; border-collapse: collapse; text-align: left; font-size: 0.9rem; }
th { background-color: #f8fafc; color: var(--text-sub); padding: 14px 16px; font-weight: 600; border-bottom: 2px solid var(--border-color); }
td { padding: 14px 16px; border-bottom: 1px solid var(--border-color); vertical-align: middle; }
tr:hover { background-color: #f8fafc; }

/* 狀態 Badge */
.status-badge { display: inline-block; padding: 4px 12px; border-radius: 4px; color: white; font-size: 0.8rem; font-weight: bold; text-align: center; }
.status-running { background-color: var(--status-running); }
.status-standby { background-color: var(--status-standby); }
.status-repair { background-color: var(--status-repair); }

/* 模具標籤樣式 */
.mold-badge { background-color: #f5f3ff; color: #7c3aed; border: 1px solid #ddd6fe; padding: 4px 8px; border-radius: 6px; font-size: 0.8rem; font-weight: 600; display: inline-flex; align-items: center; gap: 4px; }
.mold-na { color: #94a3b8; font-style: italic; }

/* 操作按鈕 */
.btn-action {
border: none; border-radius: 4px; padding: 6px 12px; font-size: 0.85rem; font-weight: 500;
cursor: pointer; display: inline-flex; align-items: center; gap: 6px; transition: background 0.2s;
}
.btn-change { background-color: var(--btn-blue); color: white; }
.btn-change:hover { background-color: #1d4ed8; }
/* 狀態變更維護欄位:同一欄多筆規則垂直排列 */
.rule-container { display: flex; flex-direction: column; gap: 4px; }
.rule-item { font-size: 0.85rem; color: #334155; font-weight: 500; background: #f1f5f9; padding: 2px 8px; border-radius: 4px; display: inline-block; width: max-content; }
.rule-na { color: #94a3b8; font-style: italic; }

/* 彈出視窗 (Modal) */
.modal-overlay {
display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(15, 23, 42, 0.6); z-index: 1000; justify-content: center; align-items: center; padding: 20px;
}
.modal-overlay.open { display: flex; }
.modal-card { background: white; width: 100%; max-width: 850px; border-radius: 8px; overflow: hidden; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1); animation: slideUp 0.2s ease-out; }
@keyframes slideUp { from { transform: translateY(15px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }

.modal-header { padding: 18px 24px; border-bottom: 1px solid var(--border-color); display: flex; justify-content: space-between; align-items: center; }
.modal-title { font-size: 1.25rem; font-weight: bold; color: var(--text-main); }
.modal-body { padding: 24px; max-height: 75vh; overflow-y: auto; }
.form-row { margin-bottom: 18px; }
.form-label { display: block; font-size: 0.9rem; font-weight: bold; margin-bottom: 8px; }
.form-label span { color: #ef4444; margin-right: 4px; }
.form-select, .form-textarea { width: 100%; border: 1px solid #cbd5e1; border-radius: 6px; padding: 10px 12px; font-size: 0.9rem; outline: none; background-color: #fff; }
/* 歷史紀錄表格 */
.modal-table-title { font-size: 0.95rem; font-weight: bold; margin: 24px 0 10px 0; }
.modal-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.modal-table th { padding: 10px; background-color: #f8fafc; border-bottom: 1px solid var(--border-color); }
.modal-table td { padding: 10px; border-bottom: 1px solid var(--border-color); }

.modal-footer { padding: 16px 24px; border-top: 1px solid var(--border-color); display: flex; justify-content: flex-end; gap: 12px; background: #f8fafc; }
.footer-btn-confirm { background-color: var(--btn-green); color: white; padding: 8px 18px; font-weight: bold; border: none; border-radius: 4px; cursor: pointer; }
.footer-btn-cancel { background-color: white; border: 1px solid #cbd5e1; color: var(--text-sub); padding: 8px 18px; border-radius: 4px; cursor: pointer; }
</style>
</head>
<body>

<div class="container">
<div class="header-row">
<div class="main-title">設備管理</div>
</div>

<div class="filter-section">
<div class="filter-label">按類別快速篩選:</div>
<div class="filter-group" id="type-filters">
<div class="filter-tag active" onclick="filterType('ALL', this)">全部</div>
<div class="filter-tag" onclick="filterType('生產設備', this)">生產設備</div>
<div class="filter-tag" onclick="filterType('模具生產設備', this)">模具生產設備</div>
<div class="filter-tag" onclick="filterType('IoT', this)">IoT</div>
<div class="filter-tag" onclick="filterType('車床', this)">車床</div>
</div>

<div class="search-row">
<input type="text" class="search-input" id="deviceSearchInput" placeholder="搜尋設備編號...">
<button class="btn-search" onclick="searchDevices()">搜尋</button>
<button class="btn-clear" onclick="clearSearch()">清空</button>
</div>
</div>

<div class="table-responsive">
<table>
<thead>
<tr>
<th>設備編號</th>
<th>設備名稱</th>
<th>設備類型</th>
<th>目前模具</th>
<th>線別</th>
<th>所屬區域</th>
<th>狀態</th>
<th>狀態變更維護 (可轉移規則)</th>
<th>操作</th>
</tr>
</thead>
<tbody id="device-table-body">
<tr id="row-HL-M-13" data-type="生產設備">
<td><b>HL-M-13</b></td>
<td>凸面拋光機(4軸)</td>
<td>生產設備</td>
<td><span class="mold-na">N/A</span></td>
<td>手動線</td>
<td>第一廠區</td>
<td><span class="status-badge status-running">運轉中</span></td>
<td>
<div class="rule-container">
<div class="rule-item">維護中 ➔ 正常運行</div>
</div>
</td>
<td>
<button class="btn-action btn-change" onclick="openStatusModal('HL-M-13', '凸面拋光機(4軸)', '運轉中')">
<i class="fas fa-exchange-alt"></i> 變更狀態
</button>
</td>
</tr>
<tr id="row-HL-M-14" data-type="生產設備">
<td><b>HL-M-14</b></td>
<td>拋光機(單軸)</td>
<td>生產設備</td>
<td><span class="mold-na">N/A</span></td>
<td>手動線</td>
<td>第一廠區</td>
<td><span class="status-badge status-running">運轉中</span></td>
<td><span class="rule-na">N/A</span></td>
<td>
<button class="btn-action btn-change" onclick="openStatusModal('HL-M-14', '拋光機(單軸)', '運轉中')">
<i class="fas fa-exchange-alt"></i> 變更狀態
</button>
</td>
</tr>
<tr id="row-HL-M-17" data-type="模具生產設備">
<td><b>HL-M-17</b></td>
<td>超音波清洗機(小)</td>
<td><span style="color: #7c3aed; font-weight: 600;">模具生產設備</span></td>
<td><span class="mold-badge"><i class="fas fa-shapes"></i> MOLD-2026-A1</span></td>
<td>手動線</td>
<td>第二廠區</td>
<td><span class="status-badge status-standby">待機</span></td>
<td>
<div class="rule-container">
<div class="rule-item">待機 ➔ 正常運行</div>
</div>
</td>
<td>
<button class="btn-action btn-change" onclick="openStatusModal('HL-M-17', '超音波清洗機(小)', '待機')">
<i class="fas fa-exchange-alt"></i> 變更狀態
</button>
</td>
</tr>
<tr id="row-Keratometer" data-type="生產設備">
<td><b>Keratometer</b></td>
<td>手動弧度儀</td>
<td>生產設備</td>
<td><span class="mold-na">N/A</span></td>
<td>手動線</td>
<td>第一廠區</td>
<td><span class="status-badge status-standby">待機</span></td>
<td>
<div class="rule-container">
<div class="rule-item">故障 ➔ 維護中</div>
</div>
</td>
<td>
<button class="btn-action btn-change" onclick="openStatusModal('Keratometer', '手動弧度儀', '待機')">
<i class="fas fa-exchange-alt"></i> 變更狀態
</button>
</td>
</tr>
<tr id="row-HL-M-18" data-type="模具生產設備">
<td><b>HL-M-18</b></td>
<td>超音波清洗機(大)</td>
<td><span style="color: #7c3aed; font-weight: 600;">模具生產設備</span></td>
<td><span class="mold-badge"><i class="fas fa-shapes"></i> MOLD-2026-B3</span></td>
<td>自動線</td>
<td>第二廠區</td>
<td><span class="status-badge status-standby">待機</span></td>
<td><span class="rule-na">N/A</span></td>
<td>
<button class="btn-action btn-change" onclick="openStatusModal('HL-M-18', '超音波清洗機(大)', '待機')">
<i class="fas fa-exchange-alt"></i> 變更狀態
</button>
</td>
</tr>
<tr id="row-HL-M-19" data-type="生產設備">
<td><b>HL-M-19</b></td>
<td>超音波清洗機(大)</td>
<td>生產設備</td>
<td><span class="mold-na">N/A</span></td>
<td>自動線</td>
<td>第一廠區</td>
<td><span class="status-badge status-standby">待機</span></td>
<td>
<div class="rule-container">
<div class="rule-item">正常運行 ➔ 維護中</div>
</div>
</td>
<td>
<button class="btn-action btn-change" onclick="openStatusModal('HL-M-19', '超音波清洗機(大)', '待機')">
<i class="fas fa-exchange-alt"></i> 變更狀態
</button>
</td>
</tr>
<tr id="row-HL-M-27" data-type="模具生產設備">
<td><b>HL-M-27</b></td>
<td>PJ-Plasma II</td>
<td><span style="color: #7c3aed; font-weight: 600;">模具生產設備</span></td>
<td><span class="mold-badge"><i class="fas fa-shapes"></i> MOLD-PL-V2</span></td>
<td>自動線</td>
<td>第三廠區</td>
<td><span class="status-badge status-standby">待機</span></td>
<td>
<div class="rule-container">
<div class="rule-item">維護中 ➔ 正常運行</div>
<div class="rule-item">故障 ➔ 維護中</div>
</div>
</td>
<td>
<button class="btn-action btn-change" onclick="openStatusModal('HL-M-27', 'PJ-Plasma II', '待機')">
<i class="fas fa-exchange-alt"></i> 變更狀態
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>

<div id="statusModal" class="modal-overlay">
<div class="modal-card">
<div class="modal-header">
<div class="modal-title" id="modalTitle">設備狀態變更:</div>
<i class="fas fa-times" style="cursor: pointer; color: var(--text-sub);" onclick="closeModal()"></i>
</div>
<div class="modal-body">
<div class="form-row">
<label class="form-label">當前狀態:</label>
<span id="currentStatusBadge" class="status-badge">運轉中</span>
</div>
<div class="form-row">
<label class="form-label"><span>*</span>目標狀態:</label>
<select class="form-select" id="targetStatusSelect">
<option value="" disabled selected>請選擇切換目標狀態</option>
</select>
</div>

<div class="form-row">
<label class="form-label"><span>*</span>變更原因/紀錄:</label>
<textarea class="form-textarea" id="changeReasonInput" rows="3" placeholder="請說明狀態變更的原因"></textarea>
</div>

<div class="modal-table-title">最近變更紀錄:</div>
<table class="modal-table">
<thead>
<tr>
<th>狀態</th>
<th>原因/備註</th>
<th>變更時間</th>
<th>變更人員</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="status-badge status-running">運轉中</span></td>
<td>sysAction</td>
<td>2026-07-08 16:35:36</td>
<td>阮氏商</td>
</tr>
<tr>
<td><span class="status-badge status-standby">待機</span></td>
<td>sysAction</td>
<td>2026-07-08 15:10:12</td>
<td>阮氏商</td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button class="footer-btn-confirm" onclick="submitStatusChange()">確認</button>
<button class="footer-btn-cancel" onclick="closeModal()">取消</button>
</div>
</div>
</div>

<script>
let activeDeviceId = '';

function openStatusModal(deviceId, deviceName, currentStatus) {
activeDeviceId = deviceId;
document.getElementById('modalTitle').innerText = `設備狀態變更:${deviceName}`;
const badge = document.getElementById('currentStatusBadge');
badge.innerText = currentStatus;
badge.className = 'status-badge ' + (currentStatus === '運轉中' ? 'status-running' : 'status-standby');

const targetRow = document.getElementById(`row-${deviceId}`);
const ruleItems = targetRow.querySelectorAll('.rule-item');
const selectEl = document.getElementById('targetStatusSelect');
selectEl.innerHTML = '<option value="" disabled selected>請選擇切換目標狀態</option>';

if (ruleItems.length > 0) {
ruleItems.forEach(item => {
const text = item.innerText;
const parts = text.split('');
if (parts.length === 2) {
const fromStatus = parts[0].trim();
const toStatus = parts[1].trim();
const opt = document.createElement('option');
opt.value = toStatus;
opt.innerText = `${toStatus} (依規則:${fromStatus}${toStatus})`;
selectEl.appendChild(opt);
}
});
} else {
const defaultStatuses = ['運轉中', '待機', '維護中'];
defaultStatuses.forEach(s => {
if (s !== currentStatus) {
const opt = document.createElement('option');
opt.value = s;
opt.innerText = s;
selectEl.appendChild(opt);
}
});
}

document.getElementById('changeReasonInput').value = '';
document.getElementById('statusModal').classList.add('open');
}

function closeModal() { document.getElementById('statusModal').classList.remove('open'); }

function submitStatusChange() {
const targetStatus = document.getElementById('targetStatusSelect').value;
const reason = document.getElementById('changeReasonInput').value.trim();

if (!targetStatus || !reason) { alert('請填寫所有必要欄位!'); return; }

const targetRow = document.getElementById(`row-${activeDeviceId}`);
if (targetRow) {
// 因新增「線別」且刪除「備註」欄位,狀態現在位於第 7 欄 (td:nth-child(7))
const statusCell = targetRow.querySelector('td:nth-child(7)');
let badgeClass = 'status-running';
if (targetStatus === '待機') badgeClass = 'status-standby';
if (targetStatus === '維護中') badgeClass = 'status-repair';
statusCell.innerHTML = `<span class="status-badge ${badgeClass}">${targetStatus}</span>`;
const actionBtn = targetRow.querySelector('.btn-change');
actionBtn.setAttribute('onclick', `openStatusModal('${activeDeviceId}', '${targetRow.querySelector('td:nth-child(2)').innerText}', '${targetStatus}')`);
}

alert('變更確認成功!');
closeModal();
}

function filterType(type, el) {
el.parentElement.querySelectorAll('.filter-tag').forEach(t => t.classList.remove('active'));
el.classList.add('active');
document.querySelectorAll('#device-table-body tr').forEach(row => {
row.style.display = (type === 'ALL' || row.getAttribute('data-type') === type) ? 'table-row' : 'none';
});
}

function searchDevices() {
const query = document.getElementById('deviceSearchInput').value.trim().toLowerCase();
document.querySelectorAll('#device-table-body tr').forEach(row => {
const id = row.querySelector('td:first-child').innerText.toLowerCase();
row.style.display = id.includes(query) ? 'table-row' : 'none';
});
}

function clearSearch() {
document.getElementById('deviceSearchInput').value = '';
document.querySelectorAll('#device-table-body tr').forEach(row => {
row.style.display = 'table-row';
});
}
</script>

</body>
</html>
    (1-1/1)