|
<!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>
|