515 lines
18 KiB
JavaScript
515 lines
18 KiB
JavaScript
(() => {
|
||
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">×</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 () => {
|
||
// 等待DOM完全渲染
|
||
await new Promise(resolve => setTimeout(resolve, 50));
|
||
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');
|
||
|
||
// 检查DOM元素是否存在
|
||
if (!tbody || !pageInfo || !prevBtn || !nextBtn) {
|
||
console.warn('工单页面DOM元素未就绪,跳过加载');
|
||
return;
|
||
}
|
||
|
||
// 获取筛选条件
|
||
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');
|
||
const factoryInput = document.getElementById('form-factory');
|
||
|
||
form.reset();
|
||
|
||
if (order) {
|
||
// 编辑模式
|
||
modalTitle.textContent = '编辑工单';
|
||
document.getElementById('form-order-id').value = order.id;
|
||
factoryInput.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 = '';
|
||
|
||
// 如果是管理员,自动填充工厂并设为只读
|
||
if (currentUser && currentUser.role === 'admin' && currentUser.factory) {
|
||
factoryInput.value = currentUser.factory;
|
||
factoryInput.readOnly = true;
|
||
factoryInput.style.backgroundColor = 'var(--surface)';
|
||
factoryInput.style.cursor = 'not-allowed';
|
||
} else {
|
||
factoryInput.readOnly = false;
|
||
factoryInput.style.backgroundColor = '';
|
||
factoryInput.style.cursor = '';
|
||
}
|
||
}
|
||
|
||
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('确认失败,请稍后重试');
|
||
}
|
||
}
|
||
};
|
||
})();
|