ERP/frontend/js/components/work-order.js

488 lines
17 KiB
JavaScript
Raw Normal View History

2025-11-24 01:32:37 +00:00
(() => {
async function render() {
return `
<div class="page-container">
<div class="page-header">
<h1>生产工单下发中心</h1>
<button id="add-work-order-btn" class="btn btn-primary">+ 添加工单</button>
</div>
<div class="filter-section">
<div class="filter-row">
<div class="filter-item">
<label>工厂/项目名称</label>
<input type="text" id="factory-filter" placeholder="请输入工厂/项目名称" />
</div>
<div class="filter-item">
<label>工单</label>
<input type="text" id="order-filter" placeholder="请输入工单号" />
</div>
<div class="filter-item">
<label>型号</label>
<input type="text" id="model-filter" placeholder="请输入型号" />
</div>
<div class="filter-item">
<label>生产日期</label>
<input type="date" id="date-filter" />
</div>
<div class="filter-actions">
<button id="search-btn" class="btn btn-primary">查询</button>
<button id="reset-btn" class="btn btn-secondary">重置</button>
</div>
</div>
</div>
<div class="table-container">
<table class="data-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="work-order-tbody">
<tr>
<td colspan="9" style="text-align:center;padding:40px;">暂无数据</td>
</tr>
</tbody>
</table>
</div>
<div class="pagination">
<button id="prev-page" class="btn btn-secondary" disabled>上一页</button>
<span id="page-info"> 1 / 1 </span>
<button id="next-page" class="btn btn-secondary" disabled>下一页</button>
</div>
</div>
<!-- 添加/编辑工单弹窗 -->
<div id="work-order-modal" class="modal" style="display:none;">
<div class="modal-content">
<div class="modal-header">
<h2 id="modal-title">添加工单</h2>
<button class="modal-close" id="close-modal">&times;</button>
</div>
<div class="modal-body">
<form id="work-order-form">
<div class="form-grid">
<div class="field">
<label>工厂/项目名称 <span class="required">*</span></label>
<input type="text" id="form-factory" class="input" required placeholder="请输入工厂/项目名称" />
</div>
<div class="field">
<label>工单/订单号 <span class="required">*</span></label>
<input type="text" id="form-order-no" class="input" required placeholder="请输入工单/订单号" />
</div>
<div class="field">
<label>产品型号</label>
<input type="text" id="form-product-model" class="input" placeholder="请输入产品型号" />
</div>
<div class="field">
<label>订单数量 <span class="required">*</span></label>
<input type="number" id="form-order-qty" class="input" required placeholder="请输入订单数量" min="1" />
</div>
<div class="field">
<label>订单生产开始时间</label>
<input type="datetime-local" id="form-start-time" class="input" />
</div>
<div class="field">
<label>订单生产结束时间</label>
<input type="datetime-local" id="form-end-time" class="input" />
</div>
<div class="field full-width">
<label>备注</label>
<textarea id="form-remark" class="input" rows="3" placeholder="请输入备注信息"></textarea>
</div>
</div>
<input type="hidden" id="form-order-id" />
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" id="cancel-modal">取消</button>
<button type="button" class="btn btn-primary" id="save-work-order">保存</button>
</div>
</div>
</div>
`;
}
Router.register('/production-mgmt/work-order', async () => {
const html = await render();
setTimeout(async () => {
await initPage();
}, 0);
return html;
});
// 监听路由变化,离开工单页面时清理定时器
let lastPath = '';
window.addEventListener('hashchange', () => {
const currentPath = location.hash.replace('#', '');
if (lastPath === '/production-mgmt/work-order' && currentPath !== '/production-mgmt/work-order') {
stopAutoRefresh();
}
lastPath = currentPath;
});
let currentPage = 1;
let totalPages = 1;
let workOrders = [];
let currentUser = null;
let refreshInterval = null;
async function initPage() {
// 获取当前用户信息
try {
currentUser = await API.me();
} catch (error) {
console.error('获取用户信息失败:', error);
}
// 根据用户角色显示/隐藏添加按钮
const addBtn = document.getElementById('add-work-order-btn');
if (addBtn && currentUser?.role !== 'superadmin') {
addBtn.style.display = 'none';
}
initEventListeners();
loadWorkOrders();
// 启动自动刷新每10秒刷新一次
startAutoRefresh();
}
function startAutoRefresh() {
// 清除已存在的定时器
if (refreshInterval) {
clearInterval(refreshInterval);
}
// 每10秒自动刷新一次工单列表
refreshInterval = setInterval(() => {
loadWorkOrders();
}, 10000);
}
function stopAutoRefresh() {
if (refreshInterval) {
clearInterval(refreshInterval);
refreshInterval = null;
}
}
function initEventListeners() {
const searchBtn = document.getElementById('search-btn');
const resetBtn = document.getElementById('reset-btn');
const prevBtn = document.getElementById('prev-page');
const nextBtn = document.getElementById('next-page');
const addBtn = document.getElementById('add-work-order-btn');
const modal = document.getElementById('work-order-modal');
const closeModal = document.getElementById('close-modal');
const cancelModal = document.getElementById('cancel-modal');
const saveBtn = document.getElementById('save-work-order');
searchBtn?.addEventListener('click', () => {
currentPage = 1;
loadWorkOrders();
});
resetBtn?.addEventListener('click', () => {
document.getElementById('factory-filter').value = '';
document.getElementById('order-filter').value = '';
document.getElementById('model-filter').value = '';
document.getElementById('date-filter').value = '';
currentPage = 1;
loadWorkOrders();
});
prevBtn?.addEventListener('click', () => {
if (currentPage > 1) {
currentPage--;
loadWorkOrders();
}
});
nextBtn?.addEventListener('click', () => {
if (currentPage < totalPages) {
currentPage++;
loadWorkOrders();
}
});
// 添加工单按钮
addBtn?.addEventListener('click', () => {
openModal();
});
// 关闭弹窗
closeModal?.addEventListener('click', () => {
closeModalWindow();
});
cancelModal?.addEventListener('click', () => {
closeModalWindow();
});
// 点击弹窗外部关闭
modal?.addEventListener('click', (e) => {
if (e.target === modal) {
closeModalWindow();
}
});
// 保存工单
saveBtn?.addEventListener('click', () => {
saveWorkOrder();
});
}
async function loadWorkOrders() {
const tbody = document.getElementById('work-order-tbody');
const pageInfo = document.getElementById('page-info');
const prevBtn = document.getElementById('prev-page');
const nextBtn = document.getElementById('next-page');
// 获取筛选条件
const factory = document.getElementById('factory-filter')?.value || '';
const order = document.getElementById('order-filter')?.value || '';
const model = document.getElementById('model-filter')?.value || '';
const date = document.getElementById('date-filter')?.value || '';
try {
// 调用后端API获取数据
const params = new URLSearchParams();
if (factory) params.append('factory', factory);
if (order) params.append('order', order);
if (date) params.append('date', date);
const response = await fetch(`/api/work-orders?${params.toString()}`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' }
});
const result = await response.json();
if (result.ok) {
workOrders = result.data || [];
totalPages = 1;
} else {
throw new Error(result.error || '加载失败');
}
const isSuperAdmin = currentUser?.role === 'superadmin';
const isAdmin = currentUser?.role === 'admin' || isSuperAdmin;
const colspanCount = isSuperAdmin ? 9 : (isAdmin ? 9 : 8);
if (workOrders.length === 0) {
tbody.innerHTML = `<tr><td colspan="${colspanCount}" style="text-align:center;padding:40px;">暂无数据</td></tr>`;
} else {
tbody.innerHTML = workOrders.map(order => {
const statusClass = order.status === 'confirmed' ? 'status-confirmed' : 'status-issued';
const statusText = order.statusText || '已下发';
const isConfirmed = order.status === 'confirmed';
return `
<tr>
<td>${order.factory}</td>
<td>${order.orderNo}</td>
<td>${order.productModel || '-'}</td>
<td>${order.orderQty}</td>
<td>${order.productionStartTime || '-'}</td>
<td>${order.productionEndTime || '-'}</td>
<td><span class="status-badge ${statusClass}">${statusText}</span></td>
<td>${order.remark || '-'}</td>
${isSuperAdmin ? `
<td>
<button class="btn-icon" onclick="editWorkOrderById('${order.id}')" title="编辑"></button>
<button class="btn-icon btn-danger" onclick="deleteWorkOrder('${order.id}')" title="删除">🗑</button>
</td>
` : isAdmin ? `
<td>
${!isConfirmed ? `<button class="btn-icon btn-confirm" onclick="confirmWorkOrder('${order.id}')" title="确认">✓</button>` : '<span style="color:var(--text-2);font-size:12px;">已确认</span>'}
</td>
` : ''}
</tr>
`;
}).join('');
}
// 根据用户角色显示/隐藏操作列表头
const thead = document.querySelector('.data-table thead tr');
const operationTh = thead?.querySelector('th:last-child');
if (operationTh) {
operationTh.style.display = (isSuperAdmin || isAdmin) ? '' : 'none';
}
// 更新分页信息
pageInfo.textContent = `${currentPage} 页 / 共 ${totalPages}`;
prevBtn.disabled = currentPage <= 1;
nextBtn.disabled = currentPage >= totalPages;
} catch (error) {
console.error('加载工单数据失败:', error);
API.toast('加载数据失败,请稍后重试');
const isSuperAdmin = currentUser?.role === 'superadmin';
const isAdmin = currentUser?.role === 'admin' || isSuperAdmin;
const colspanCount = isSuperAdmin ? 9 : (isAdmin ? 9 : 8);
tbody.innerHTML = `<tr><td colspan="${colspanCount}" style="text-align:center;padding:40px;color:var(--danger);">加载失败</td></tr>`;
}
}
// 打开弹窗
function openModal(order = null) {
const modal = document.getElementById('work-order-modal');
const modalTitle = document.getElementById('modal-title');
const form = document.getElementById('work-order-form');
form.reset();
if (order) {
// 编辑模式
modalTitle.textContent = '编辑工单';
document.getElementById('form-order-id').value = order.id;
document.getElementById('form-factory').value = order.factory;
document.getElementById('form-order-no').value = order.orderNo;
document.getElementById('form-product-model').value = order.productModel || '';
document.getElementById('form-order-qty').value = order.orderQty;
document.getElementById('form-start-time').value = order.productionStartTime || '';
document.getElementById('form-end-time').value = order.productionEndTime || '';
document.getElementById('form-remark').value = order.remark || '';
} else {
// 添加模式
modalTitle.textContent = '添加工单';
document.getElementById('form-order-id').value = '';
}
modal.style.display = 'flex';
}
// 关闭弹窗
function closeModalWindow() {
const modal = document.getElementById('work-order-modal');
modal.style.display = 'none';
}
// 保存工单
async function saveWorkOrder() {
const orderId = document.getElementById('form-order-id').value;
const factory = document.getElementById('form-factory').value.trim();
const orderNo = document.getElementById('form-order-no').value.trim();
const productModel = document.getElementById('form-product-model').value.trim();
const orderQty = document.getElementById('form-order-qty').value;
const startTime = document.getElementById('form-start-time').value;
const endTime = document.getElementById('form-end-time').value;
const remark = document.getElementById('form-remark').value.trim();
// 验证必填项
if (!factory || !orderNo || !orderQty) {
API.toast('请填写所有必填项');
return;
}
const data = {
factory,
orderNo,
productModel,
orderQty: parseInt(orderQty),
productionStartTime: startTime,
productionEndTime: endTime,
remark
};
try {
let response;
if (orderId) {
// 编辑 - 更新现有工单
response = await fetch(`/api/work-orders/${orderId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
} else {
// 添加 - 新增工单
response = await fetch('/api/work-orders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
}
const result = await response.json();
if (result.ok) {
API.toast(result.message || (orderId ? '工单更新成功' : '工单添加成功'));
closeModalWindow();
// 确保数据保存后再刷新列表
await loadWorkOrders();
} else {
API.toast(result.error || '保存失败');
}
} catch (error) {
console.error('保存工单失败:', error);
API.toast('保存失败,请稍后重试');
}
}
// 暴露全局函数供按钮调用
window.editWorkOrderById = function(id) {
const order = workOrders.find(o => o.id === id);
if (order) {
openModal(order);
}
};
window.deleteWorkOrder = async function(id) {
if (confirm('确定要删除这条工单吗?')) {
try {
const response = await fetch(`/api/work-orders/${id}`, {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' }
});
const result = await response.json();
if (result.ok) {
API.toast(result.message || '工单删除成功');
await loadWorkOrders();
} else {
API.toast(result.error || '删除失败');
}
} catch (error) {
console.error('删除工单失败:', error);
API.toast('删除失败,请稍后重试');
}
}
};
window.confirmWorkOrder = async function(id) {
if (confirm('确定要确认这条工单吗?')) {
try {
const response = await fetch(`/api/work-orders/${id}/confirm`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
const result = await response.json();
if (result.ok) {
API.toast(result.message || '工单确认成功');
await loadWorkOrders();
} else {
API.toast(result.error || '确认失败');
}
} catch (error) {
console.error('确认工单失败:', error);
API.toast('确认失败,请稍后重试');
}
}
};
})();