|
<!DOCTYPE html>
|
|
<html lang="zh-TW">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>模具檢驗規則與狀態卡控系統</title>
|
|
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<script>
|
|
tailwind.config = {
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
'teal-800': '#1e4646',
|
|
'teal-200': '#b2f0f0',
|
|
'teal-600': '#00a399',
|
|
'light-blue': '#4c9aff',
|
|
'dark-red': '#cc0000',
|
|
'gray-500': '#6b7280',
|
|
|
|
/* 模具狀態顏色 */
|
|
'status-ok': '#10b981', /* 生產中 */
|
|
'status-idle': '#2563eb', /* 待機庫存 */
|
|
'status-wip': '#f59e0b', /* 保養/維修 */
|
|
'status-ng': '#ef4444',
|
|
|
|
/* 背景色 */
|
|
'bg-ok': '#dcfce7',
|
|
'bg-idle': '#dbeafe',
|
|
'bg-wip': '#fef3c7',
|
|
'bg-ng': '#fee2e2',
|
|
|
|
/* 綁定單據相關 */
|
|
'bind-color': '#2563eb',
|
|
'bind-bg': '#eff6ff',
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
<style>
|
|
.filter-button {
|
|
padding: 8px 16px;
|
|
border-radius: 9999px;
|
|
font-weight: 600;
|
|
transition: all 0.2s;
|
|
cursor: pointer;
|
|
}
|
|
.action-button-mini {
|
|
padding: 4px 8px;
|
|
border-radius: 4px;
|
|
font-size: 0.75rem;
|
|
font-weight: 500;
|
|
transition: background-color 0.15s, opacity 0.15s;
|
|
}
|
|
.filter-button.active {
|
|
background-color: #3b82f6;
|
|
color: white;
|
|
}
|
|
.filter-button.inactive {
|
|
background-color: #e5e7eb;
|
|
color: #1f2937;
|
|
}
|
|
.required-label::after {
|
|
content: ' *';
|
|
color: #ef4444;
|
|
margin-left: 2px;
|
|
}
|
|
.detail-row-input {
|
|
width: 100%;
|
|
padding: 6px 8px;
|
|
border: 1px solid #d1d5db;
|
|
border-radius: 4px;
|
|
background-color: white;
|
|
}
|
|
.detail-input-pm {
|
|
padding: 8px;
|
|
border: 1px solid #d1d5db;
|
|
border-radius: 4px;
|
|
width: 100%;
|
|
}
|
|
.status-transition-tag {
|
|
background-color: #eff6ff;
|
|
color: #2563eb;
|
|
padding: 2px 8px;
|
|
border-radius: 4px;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
margin-right: 4px;
|
|
border: 1px solid #2563eb;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="bg-slate-100 p-10">
|
|
|
|
<div id="app">
|
|
|
|
<div v-if="viewMode === 'list'" class="main-content-wrapper">
|
|
|
|
<h1 class="text-3xl font-bold text-slate-800 mb-6 border-b-4 border-blue-200 pb-3">
|
|
🎛️ 模具檢驗規則與狀態轉移卡控設定
|
|
</h1>
|
|
|
|
<div class="bg-white p-6 rounded-2xl shadow-xl border border-gray-200">
|
|
|
|
<h2 class="text-xl font-bold text-gray-700 mb-4 border-b pb-2">
|
|
模具稼動與維護單據配置
|
|
</h2>
|
|
|
|
<div class="flex space-x-4 mb-6">
|
|
<button @click="activeFilter = '全部'" :class="['filter-button', activeFilter === '全部' ? 'active' : 'inactive']">全部</button>
|
|
<button @click="activeFilter = '光學模具'" :class="['filter-button', activeFilter === '光學模具' ? 'active' : 'inactive']">光學模具</button>
|
|
<button @click="activeFilter = '射出模具'" :class="['filter-button', activeFilter === '射出模具' ? 'active' : 'inactive']">射出模具</button>
|
|
<button @click="activeFilter = '沖壓模具'" :class="['filter-button', activeFilter === '沖壓模具' ? 'active' : 'inactive']">沖壓模具</button>
|
|
</div>
|
|
|
|
<div class="flex justify-between items-center mb-4">
|
|
<input v-model="searchKeyword" placeholder="搜尋模具編號/名稱..." class="p-2 border rounded-lg w-64">
|
|
</div>
|
|
|
|
<table class="min-w-full table-auto border border-gray-200 text-sm">
|
|
<thead class="bg-gray-100">
|
|
<tr>
|
|
<th class="px-3 py-2 border">模具編號</th>
|
|
<th class="px-3 py-2 border">模具名稱</th>
|
|
<th class="px-3 py-2 border">模具類型</th>
|
|
<th class="px-3 py-2 border">線別</th>
|
|
<th class="px-3 py-2 border w-32">表單配置</th>
|
|
<th class="px-3 py-2 border w-64">狀態變更維護 (已卡控流程)</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="mold in filteredMolds" :key="mold.id" class="hover:bg-blue-50">
|
|
<td class="px-3 py-2 border font-bold">{{ mold.no }}</td>
|
|
<td class="px-3 py-2 border">{{ mold.name }}</td>
|
|
<td class="px-3 py-2 border">{{ mold.type }}</td>
|
|
<td class="px-3 py-2 border">{{ mold.lineType }}</td>
|
|
|
|
<td class="px-3 py-2 border text-center">
|
|
<button @click="openDialog('bind_pm', mold)"
|
|
class="action-button-mini bg-blue-600 hover:bg-light-blue text-white w-full">
|
|
配置單據 ({{ mold.maintenanceForms ? Object.keys(mold.maintenanceForms).length : 0 }})
|
|
</button>
|
|
</td>
|
|
|
|
<td class="px-3 py-2 border text-center">
|
|
<div v-if="!mold.maintenanceForms || Object.keys(mold.maintenanceForms).length === 0" class="text-gray-400 italic text-sm">
|
|
無卡控點 (自由轉換)
|
|
</div>
|
|
<div v-else class="flex flex-wrap justify-center gap-1">
|
|
<span v-for="(formId, statusKey) in mold.maintenanceForms" :key="statusKey"
|
|
class="inline-block bg-orange-50 text-orange-700 border border-orange-200 text-xs font-semibold px-2 py-0.5 rounded">
|
|
{{ statusKey }}
|
|
</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="dialog.mode === 'bind_pm'" class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-60 z-40 p-4">
|
|
<div class="bg-white p-6 rounded-xl shadow-2xl w-11/12 max-w-xl border border-gray-200">
|
|
<h2 class="text-2xl font-bold text-blue-700 mb-4 border-b pb-2">
|
|
📋 配置模具狀態轉換卡控單據 ({{ dialog.data.no }})
|
|
</h2>
|
|
<div class="mb-6 space-y-4">
|
|
<p class="text-sm text-gray-600 mb-4">模具名稱:<span class="font-bold text-slate-800">{{ dialog.data.name }}</span></p>
|
|
|
|
<h3 class="text-lg font-bold text-gray-700 mb-3 border-t pt-3">檢驗單據卡控規則設定</h3>
|
|
|
|
<div class="space-y-3 max-h-64 overflow-y-auto p-2 border rounded-lg bg-gray-50">
|
|
<div v-for="(formId, status) in dialog.data.maintenanceForms" :key="status" class="flex items-center space-x-2 bg-white p-3 rounded shadow-sm border border-gray-200">
|
|
<div class="w-5/12">
|
|
<span class="status-transition-tag text-xs">{{ status }}</span>
|
|
</div>
|
|
<div class="flex-1">
|
|
<select v-model="dialog.data.maintenanceForms[status]" class="detail-row-input text-xs">
|
|
<option :value="null">-- 取消綁定 --</option>
|
|
<option v-for="form in pmFormPool" :key="form.id" :value="form.id">
|
|
{{ form.name }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
<button @click="removePMFormBinding(status)" class="text-dark-red hover:text-red-700 text-xs font-bold p-1">
|
|
刪除
|
|
</button>
|
|
</div>
|
|
<div v-if="!dialog.data.maintenanceForms || Object.keys(dialog.data.maintenanceForms).length === 0" class="p-3 text-center text-gray-500 text-sm">
|
|
目前沒有設定任何單據卡控點。
|
|
</div>
|
|
</div>
|
|
|
|
<h4 class="text-md font-semibold text-gray-700 pt-3 border-t">新增狀態卡控點</h4>
|
|
<div class="flex space-x-2">
|
|
<div class="w-1/3">
|
|
<select v-model="dialog.tempBinding.fromStatus" class="detail-row-input text-sm">
|
|
<option value="">-- 起始狀態 --</option>
|
|
<option v-for="status in statusOptions" :key="status" :value="status">{{ status }}</option>
|
|
</select>
|
|
</div>
|
|
<div class="w-1/3">
|
|
<select v-model="dialog.tempBinding.toStatus" class="detail-row-input text-sm">
|
|
<option value="">-- 目標狀態 --</option>
|
|
<option v-for="status in statusOptions" :key="status" :value="status">{{ status }}</option>
|
|
</select>
|
|
</div>
|
|
<div class="w-1/3">
|
|
<button @click="addPMFormBinding" class="px-3 py-1 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm h-full w-full">
|
|
+ 加入卡控
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="flex justify-end space-x-3 mt-4">
|
|
<button @click="closeDialog" class="px-5 py-2 bg-gray-200 text-gray-700 rounded hover:bg-gray-300">取消</button>
|
|
<button @click="saveMaintenanceBinding" class="px-5 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">確認儲存配置</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="viewMode === 'pm_entry'" class="main-content-wrapper">
|
|
<h1 class="text-3xl font-bold text-slate-800 mb-6 border-b-4 border-blue-200 pb-3">
|
|
📝 模具檢驗與維護數據填寫
|
|
</h1>
|
|
<div class="bg-white p-6 rounded-2xl shadow-xl border border-gray-200">
|
|
<h2 class="text-xl font-bold text-gray-700 mb-4">
|
|
工作單據:<span class="text-blue-600">{{ pmEntryForm.pmFormName }}</span>
|
|
</h2>
|
|
<div class="grid grid-cols-2 gap-4 mb-6 pb-4 border-b text-sm">
|
|
<div><span class="font-semibold text-gray-500">模具編號:</span><span class="text-blue-600 font-bold">{{ pmEntryForm.equipmentNo }}</span></div>
|
|
<div><span class="font-semibold text-gray-500">模具名稱:</span><span>{{ pmEntryForm.equipmentName }}</span></div>
|
|
<div><span class="font-semibold text-gray-500">表單類型:</span><span>{{ pmEntryForm.pmFormType }}</span></div>
|
|
<div><span class="font-semibold text-gray-500">檢驗人員:</span><input v-model="pmEntryForm.inspector" class="detail-input-pm w-2/3 py-1 px-2" placeholder="請輸入姓名"></div>
|
|
</div>
|
|
<table class="min-w-full table-auto border border-gray-200 text-sm">
|
|
<thead class="bg-gray-100">
|
|
<tr>
|
|
<th class="px-3 py-2 border w-1/4">檢驗項目</th>
|
|
<th class="px-3 py-2 border w-1/4">驗收標準</th>
|
|
<th class="px-3 py-2 border w-1/6">檢驗方法</th>
|
|
<th class="px-3 py-2 border w-1/6 required-label">判定結果</th>
|
|
<th class="px-3 py-2 border w-1/6">備註</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="(detail, index) in pmEntryForm.details" :key="index">
|
|
<td class="px-3 py-2 border font-medium">{{ detail.content }}</td>
|
|
<td class="px-3 py-2 border text-gray-600">{{ detail.standard }}</td>
|
|
<td class="px-3 py-2 border text-gray-500">{{ detail.method }}</td>
|
|
<td class="px-3 py-2 border">
|
|
<select v-model="detail.result" class="detail-input-pm py-1 px-2 text-sm">
|
|
<option value="">-- 選擇 --</option>
|
|
<option value="PASS">PASS (合格)</option>
|
|
<option value="FAIL">FAIL (不合格)</option>
|
|
</select>
|
|
</td>
|
|
<td class="px-3 py-2 border"><input v-model="detail.notes" type="text" class="detail-input-pm py-1 px-2 text-sm" placeholder="選填"></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<div class="flex justify-end space-x-3 mt-6">
|
|
<button @click="viewMode = 'list'" class="px-5 py-2 bg-gray-200 text-gray-700 rounded hover:bg-gray-300">返回列表</button>
|
|
<button @click="submitPMForm" class="px-5 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">儲存並執行狀態切換</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
const { createApp, ref, reactive, computed } = Vue;
|
|
|
|
createApp({
|
|
setup() {
|
|
const viewMode = ref('list');
|
|
const activeFilter = ref('全部');
|
|
const searchKeyword = ref('');
|
|
|
|
const statusOptions = ref(['生產中', '待機庫存', '保養中', '維修中']);
|
|
|
|
const mockMoldDetails = [
|
|
{ content: '模面外觀與刮傷檢驗', standard: '光學區無刮傷、麻點與銹斑', method: '強光燈顯微目視', result: '', notes: '' },
|
|
{ content: '入子/型腔磨損量測', standard: '設計公差 ±0.005 mm 以內', method: '三次元/千分表量測', result: '', notes: '' },
|
|
{ content: '射出嘴套與澆道清潔', standard: '無塑料殘留、無焦黑積碳', method: '目視與擦拭檢驗', result: '', notes: '' },
|
|
{ content: '模具冷水路暢通與氣密', standard: '無漏水、水壓阻力符合規範', method: '氣密水壓測試儀', result: '', notes: '' }
|
|
];
|
|
|
|
const pmFormPool = ref([
|
|
{ id: 1, name: '日點檢-下線洗模點檢表', type: '下線清洗', details: mockMoldDetails },
|
|
{ id: 2, name: '定期保養-超音波清洗與潤滑紀錄', type: '定期保養', details: mockMoldDetails },
|
|
{ id: 3, name: '模具大修維修檢驗報告', type: '故障維修', details: mockMoldDetails },
|
|
{ id: 4, name: '光學模面精密拋光校正單', type: '光學精密', details: mockMoldDetails },
|
|
]);
|
|
|
|
const moldRecords = ref([
|
|
{ id: 1, no: 'MOLD-2026-A1', name: '隱形眼鏡前弧成型模 A型', type: '光學模具', lineType: '手動線', status: '生產中', maintenanceForms: { '生產中->保養中': 1 } },
|
|
{ id: 2, no: 'MOLD-2026-B3', name: '隱形眼鏡後弧成型模 B型', type: '光學模具', lineType: '自動線', status: '生產中', maintenanceForms: {} },
|
|
{ id: 3, no: 'MOLD-PL-V2', name: 'PIM 載台特製模組', type: '射出模具', lineType: '自動線', status: '生產中', maintenanceForms: { '生產中->維修中': 3 } },
|
|
{ id: 4, no: 'MOLD-ST-99', name: '外殼固定鋼片沖壓模', type: '沖壓模具', lineType: '手動線', status: '待機庫存', maintenanceForms: { '待機庫存->保養中': 2 } },
|
|
]);
|
|
|
|
const dialog = reactive({
|
|
mode: null,
|
|
data: {},
|
|
tempBinding: { fromStatus: '', toStatus: '' },
|
|
});
|
|
|
|
const pmEntryForm = reactive({
|
|
equipmentNo: '', equipmentName: '', pmFormId: '', pmFormName: '', pmFormType: '', inspector: '', details: []
|
|
});
|
|
|
|
const getPMFormInfo = (id) => {
|
|
const form = pmFormPool.value.find(f => f.id === id);
|
|
return form ? { name: form.name, type: form.type } : { name: '未配置單據', type: 'N/A' };
|
|
};
|
|
|
|
const filteredMolds = computed(() => {
|
|
let records = moldRecords.value;
|
|
const keyword = searchKeyword.value.toLowerCase();
|
|
const filterType = activeFilter.value;
|
|
|
|
if (filterType !== '全部') records = records.filter(m => m.type === filterType);
|
|
if (keyword) {
|
|
records = records.filter(m =>
|
|
m.no.toLowerCase().includes(keyword) || m.name.toLowerCase().includes(keyword)
|
|
);
|
|
}
|
|
return records;
|
|
});
|
|
|
|
const addPMFormBinding = () => {
|
|
const fromS = dialog.tempBinding.fromStatus;
|
|
const toS = dialog.tempBinding.toStatus;
|
|
if (!fromS || !toS) { alert('請選擇起始與目標狀態!'); return; }
|
|
if (fromS === toS) { alert('起始與目標狀態不能相同!'); return; }
|
|
|
|
const key = `${fromS}->${toS}`;
|
|
if (!dialog.data.maintenanceForms) dialog.data.maintenanceForms = {};
|
|
dialog.data.maintenanceForms[key] = pmFormPool.value[0].id;
|
|
|
|
dialog.tempBinding.fromStatus = '';
|
|
dialog.tempBinding.toStatus = '';
|
|
};
|
|
|
|
const removePMFormBinding = (statusKey) => {
|
|
delete dialog.data.maintenanceForms[statusKey];
|
|
};
|
|
|
|
const saveMaintenanceBinding = () => {
|
|
const index = moldRecords.value.findIndex(m => m.id === dialog.data.id);
|
|
if (index !== -1) {
|
|
moldRecords.value[index].maintenanceForms = { ...dialog.data.maintenanceForms };
|
|
alert('配置成功儲存!');
|
|
}
|
|
closeDialog();
|
|
};
|
|
|
|
const openDialog = (mode, mold = {}) => {
|
|
dialog.mode = mode;
|
|
dialog.data = JSON.parse(JSON.stringify(mold));
|
|
if (!dialog.data.maintenanceForms) dialog.data.maintenanceForms = {};
|
|
dialog.tempBinding = { fromStatus: '', toStatus: '' };
|
|
};
|
|
|
|
const closeDialog = () => { dialog.mode = null; };
|
|
|
|
const submitPMForm = () => {
|
|
alert('單據填寫成功!已解除流程卡控。');
|
|
viewMode.value = 'list';
|
|
};
|
|
|
|
return {
|
|
Object,
|
|
viewMode,
|
|
activeFilter,
|
|
searchKeyword,
|
|
filteredMolds,
|
|
dialog,
|
|
pmFormPool,
|
|
pmEntryForm,
|
|
statusOptions,
|
|
openDialog,
|
|
closeDialog,
|
|
getPMFormInfo,
|
|
addPMFormBinding,
|
|
removePMFormBinding,
|
|
saveMaintenanceBinding,
|
|
submitPMForm
|
|
};
|
|
}
|
|
}).mount('#app');
|
|
</script>
|
|
</body>
|
|
</html>
|