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

494 lines
17 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(() => {
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';
// 格式化日期时间,去掉 T
const formatDateTime = (dateTime) => {
if (!dateTime || dateTime === '-') return '-';
return dateTime.replace('T', ' ');
};
return `
<tr>
<td>${order.factory}</td>
<td>${order.orderNo}</td>
<td>${order.productModel || '-'}</td>
<td>${order.orderQty}</td>
<td>${formatDateTime(order.productionStartTime)}</td>
<td>${formatDateTime(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('确认失败,请稍后重试');
}
}
};
})();