优化细节
This commit is contained in:
parent
8a1eda390a
commit
3751854815
@ -103,6 +103,10 @@
|
|||||||
<span class="child-icon">🛒</span>
|
<span class="child-icon">🛒</span>
|
||||||
<span>物料清单-采购</span>
|
<span>物料清单-采购</span>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="#/plan-mgmt/customer-order" class="nav-child" data-route="plan-mgmt-customer-order">
|
||||||
|
<span class="child-icon">📋</span>
|
||||||
|
<span>客户订单</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -221,6 +225,7 @@
|
|||||||
<script src="./js/components/production.js"></script>
|
<script src="./js/components/production.js"></script>
|
||||||
<script src="./js/components/work-order.js"></script>
|
<script src="./js/components/work-order.js"></script>
|
||||||
<script src="./js/components/material-purchase.js"></script>
|
<script src="./js/components/material-purchase.js"></script>
|
||||||
|
<script src="./js/components/customer-order.js"></script>
|
||||||
<script src="./js/components/export.js"></script>
|
<script src="./js/components/export.js"></script>
|
||||||
<script src="./js/components/settings.js"></script>
|
<script src="./js/components/settings.js"></script>
|
||||||
<script src="./js/components/notifications.js"></script>
|
<script src="./js/components/notifications.js"></script>
|
||||||
|
|||||||
@ -30,6 +30,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 根据用户角色控制菜单显示
|
||||||
|
function updateMenuVisibility(user) {
|
||||||
|
// 查找计划管理菜单组(包含标题"计划管理"的nav-group)
|
||||||
|
const navGroups = document.querySelectorAll('.nav-group');
|
||||||
|
let planMgmtGroup = null;
|
||||||
|
|
||||||
|
navGroups.forEach(group => {
|
||||||
|
const title = group.querySelector('.nav-group-title');
|
||||||
|
if (title && title.textContent.trim() === '计划管理') {
|
||||||
|
planMgmtGroup = group;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (planMgmtGroup) {
|
||||||
|
// 只有超级管理员可以看到计划管理菜单
|
||||||
|
if (user && user.role === 'superadmin') {
|
||||||
|
planMgmtGroup.style.display = '';
|
||||||
|
} else {
|
||||||
|
planMgmtGroup.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 创建水印
|
// 创建水印
|
||||||
function createWatermark(username) {
|
function createWatermark(username) {
|
||||||
// 检查水印是否启用
|
// 检查水印是否启用
|
||||||
@ -91,6 +114,9 @@
|
|||||||
document.getElementById('overlay').classList.add('hidden');
|
document.getElementById('overlay').classList.add('hidden');
|
||||||
updateUserDisplay(currentUser);
|
updateUserDisplay(currentUser);
|
||||||
|
|
||||||
|
// 根据用户角色控制菜单显示
|
||||||
|
updateMenuVisibility(currentUser);
|
||||||
|
|
||||||
// 初始化通知系统(超级管理员和管理员)
|
// 初始化通知系统(超级管理员和管理员)
|
||||||
if (currentUser && (currentUser.role === 'superadmin' || currentUser.role === 'admin') && window.NotificationSystem) {
|
if (currentUser && (currentUser.role === 'superadmin' || currentUser.role === 'admin') && window.NotificationSystem) {
|
||||||
window.NotificationSystem.init();
|
window.NotificationSystem.init();
|
||||||
@ -102,6 +128,7 @@
|
|||||||
API.me().then(user => {
|
API.me().then(user => {
|
||||||
currentUser = user;
|
currentUser = user;
|
||||||
updateUserDisplay(user);
|
updateUserDisplay(user);
|
||||||
|
updateMenuVisibility(user);
|
||||||
|
|
||||||
// 初始化通知系统(超级管理员和管理员)
|
// 初始化通知系统(超级管理员和管理员)
|
||||||
if (user && (user.role === 'superadmin' || user.role === 'admin') && window.NotificationSystem) {
|
if (user && (user.role === 'superadmin' || user.role === 'admin') && window.NotificationSystem) {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
Router.register('/settings', async () => {
|
Router.register('/settings', async () => {
|
||||||
const me = await API.me().catch(()=>({}));
|
const me = await API.me().catch(()=>({}));
|
||||||
const users = (me && me.role === 'superadmin') ? await API.adminUsers().catch(()=>({list:[]})) : {list:[]};
|
const users = (me && me.role === 'superadmin') ? await API.adminUsers().catch(()=>({list:[]})) : {list:[]};
|
||||||
const userList = (users.list||[]).map(u=>`<li style="display:flex;justify-content:space-between;align-items:center"><div><span style="margin-right:8px">${u.username}</span><span class="badge">${u.role}</span></div><button class="btn btn-secondary" data-delete-user="${u.username}" style="padding:4px 8px;font-size:12px">删除</button></li>`).join('') || '<li>暂无用户</li>';
|
const userList = (users.list||[]).map(u=>`<li style="display:flex;justify-content:space-between;align-items:center"><div><span style="margin-right:8px">${u.username}</span><span class="badge">${u.role}</span>${u.factory ? `<span class="badge" style="margin-left:8px;background:var(--info)">${u.factory}</span>` : ''}</div><button class="btn btn-secondary" data-delete-user="${u.username}" style="padding:4px 8px;font-size:12px">删除</button></li>`).join('') || '<li>暂无用户</li>';
|
||||||
const html = `
|
const html = `
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
@ -14,7 +14,10 @@ Router.register('/settings', async () => {
|
|||||||
|
|
||||||
<div style="margin-bottom:24px">
|
<div style="margin-bottom:24px">
|
||||||
<div style="font-weight:500;margin-bottom:8px;color:var(--text-2)">当前登录用户</div>
|
<div style="font-weight:500;margin-bottom:8px;color:var(--text-2)">当前登录用户</div>
|
||||||
<div id="user-card" class="badge" style="font-size:16px;padding:8px 16px">${(me && me.username) ? me.username : '未登录'}</div>
|
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
|
||||||
|
<div id="user-card" class="badge" style="font-size:16px;padding:8px 16px">${(me && me.username) ? me.username : '未登录'}</div>
|
||||||
|
${(me && me.factory) ? `<div class="badge" style="font-size:14px;padding:6px 12px;background:var(--info)">🏭 ${me.factory}</div>` : ''}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="padding:20px;background:var(--surface);border-radius:8px;border:1px solid var(--border)">
|
<div style="padding:20px;background:var(--surface);border-radius:8px;border:1px solid var(--border)">
|
||||||
@ -58,6 +61,10 @@ Router.register('/settings', async () => {
|
|||||||
<label>密码</label>
|
<label>密码</label>
|
||||||
<input id="new-password" type="password" class="input" placeholder="输入密码(至少6位)" />
|
<input id="new-password" type="password" class="input" placeholder="输入密码(至少6位)" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>所属工厂</label>
|
||||||
|
<input id="new-factory" class="input" placeholder="输入所属工厂" />
|
||||||
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>角色</label>
|
<label>角色</label>
|
||||||
<select id="new-role" class="input">
|
<select id="new-role" class="input">
|
||||||
@ -87,6 +94,24 @@ Router.register('/settings', async () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 修改用户工厂 -->
|
||||||
|
<div style="padding:20px;background:var(--surface);border-radius:8px;border:1px solid var(--border);margin-bottom:24px">
|
||||||
|
<div style="font-weight:600;margin-bottom:16px;font-size:15px">🏭 修改用户所属工厂</div>
|
||||||
|
<div class="grid cols-2" style="gap:16px">
|
||||||
|
<div class="field">
|
||||||
|
<label>选择用户</label>
|
||||||
|
<select id="factory-user" class="input">${(users.list||[]).map(u=>`<option value="${u.username}" data-factory="${u.factory || ''}">${u.username}${u.factory ? ` (${u.factory})` : ''}</option>`).join('')}</select>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>新工厂</label>
|
||||||
|
<input id="factory-name" class="input" placeholder="输入新的工厂名称" value="${(users.list||[])[0]?.factory || ''}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<button id="update-factory-btn" class="btn btn-primary" style="width:100%">更新工厂</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 用户列表 -->
|
<!-- 用户列表 -->
|
||||||
<div style="padding:20px;background:var(--surface);border-radius:8px;border:1px solid var(--border)">
|
<div style="padding:20px;background:var(--surface);border-radius:8px;border:1px solid var(--border)">
|
||||||
<div style="font-weight:600;margin-bottom:12px;font-size:15px">📋 用户列表</div>
|
<div style="font-weight:600;margin-bottom:12px;font-size:15px">📋 用户列表</div>
|
||||||
@ -277,16 +302,22 @@ Router.register('/settings', async () => {
|
|||||||
addUserBtn?.addEventListener('click', async () => {
|
addUserBtn?.addEventListener('click', async () => {
|
||||||
const usernameEl = document.getElementById('new-username');
|
const usernameEl = document.getElementById('new-username');
|
||||||
const passwordEl = document.getElementById('new-password');
|
const passwordEl = document.getElementById('new-password');
|
||||||
|
const factoryEl = document.getElementById('new-factory');
|
||||||
const roleEl = document.getElementById('new-role');
|
const roleEl = document.getElementById('new-role');
|
||||||
|
|
||||||
const username = usernameEl?.value?.trim();
|
const username = usernameEl?.value?.trim();
|
||||||
const password = passwordEl?.value;
|
const password = passwordEl?.value;
|
||||||
|
const factory = factoryEl?.value?.trim();
|
||||||
const role = roleEl?.value || 'admin';
|
const role = roleEl?.value || 'admin';
|
||||||
|
|
||||||
if (!username || !password) {
|
if (!username || !password) {
|
||||||
return API.toast('请输入用户名和密码');
|
return API.toast('请输入用户名和密码');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!factory) {
|
||||||
|
return API.toast('请输入所属工厂');
|
||||||
|
}
|
||||||
|
|
||||||
if (password.length < 6) {
|
if (password.length < 6) {
|
||||||
return API.toast('密码长度至少6位');
|
return API.toast('密码长度至少6位');
|
||||||
}
|
}
|
||||||
@ -297,7 +328,7 @@ Router.register('/settings', async () => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
body: JSON.stringify({ username, password, role })
|
body: JSON.stringify({ username, password, factory, role })
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
@ -307,6 +338,7 @@ Router.register('/settings', async () => {
|
|||||||
// 清空输入框
|
// 清空输入框
|
||||||
if (usernameEl) usernameEl.value = '';
|
if (usernameEl) usernameEl.value = '';
|
||||||
if (passwordEl) passwordEl.value = '';
|
if (passwordEl) passwordEl.value = '';
|
||||||
|
if (factoryEl) factoryEl.value = '';
|
||||||
// 刷新页面以更新用户列表
|
// 刷新页面以更新用户列表
|
||||||
setTimeout(() => Router.navigate('/settings'), 1000);
|
setTimeout(() => Router.navigate('/settings'), 1000);
|
||||||
} else {
|
} else {
|
||||||
@ -337,6 +369,57 @@ Router.register('/settings', async () => {
|
|||||||
change.disabled = false;
|
change.disabled = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 更新用户工厂
|
||||||
|
const factoryUserSelect = document.getElementById('factory-user');
|
||||||
|
const factoryNameInput = document.getElementById('factory-name');
|
||||||
|
|
||||||
|
// 当选择用户时,自动填充当前工厂
|
||||||
|
factoryUserSelect?.addEventListener('change', () => {
|
||||||
|
const selectedOption = factoryUserSelect.options[factoryUserSelect.selectedIndex];
|
||||||
|
const currentFactory = selectedOption.getAttribute('data-factory') || '';
|
||||||
|
if (factoryNameInput) {
|
||||||
|
factoryNameInput.value = currentFactory;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateFactoryBtn = document.getElementById('update-factory-btn');
|
||||||
|
updateFactoryBtn?.addEventListener('click', async () => {
|
||||||
|
const username = factoryUserSelect?.value;
|
||||||
|
const factory = factoryNameInput?.value?.trim();
|
||||||
|
|
||||||
|
if (!username) {
|
||||||
|
return API.toast('请选择用户');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!factory) {
|
||||||
|
return API.toast('请输入工厂名称');
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFactoryBtn.disabled = true;
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/admin/update-user-factory', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
credentials: 'include',
|
||||||
|
body: JSON.stringify({ username, factory })
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
if (res.ok && data.ok) {
|
||||||
|
API.toast(data.message || '工厂更新成功');
|
||||||
|
// 刷新页面以更新用户列表
|
||||||
|
setTimeout(() => Router.navigate('/settings'), 1000);
|
||||||
|
} else {
|
||||||
|
API.toast(data.error || '更新失败');
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
API.toast('更新失败:' + e.message);
|
||||||
|
} finally {
|
||||||
|
updateFactoryBtn.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 水印开关
|
// 水印开关
|
||||||
const watermarkToggle = document.getElementById('watermark-toggle');
|
const watermarkToggle = document.getElementById('watermark-toggle');
|
||||||
|
|||||||
@ -116,6 +116,8 @@
|
|||||||
Router.register('/production-mgmt/work-order', async () => {
|
Router.register('/production-mgmt/work-order', async () => {
|
||||||
const html = await render();
|
const html = await render();
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
|
// 等待DOM完全渲染
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 50));
|
||||||
await initPage();
|
await initPage();
|
||||||
}, 0);
|
}, 0);
|
||||||
return html;
|
return html;
|
||||||
@ -249,6 +251,12 @@
|
|||||||
const prevBtn = document.getElementById('prev-page');
|
const prevBtn = document.getElementById('prev-page');
|
||||||
const nextBtn = document.getElementById('next-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 factory = document.getElementById('factory-filter')?.value || '';
|
||||||
const order = document.getElementById('order-filter')?.value || '';
|
const order = document.getElementById('order-filter')?.value || '';
|
||||||
@ -346,6 +354,7 @@
|
|||||||
const modal = document.getElementById('work-order-modal');
|
const modal = document.getElementById('work-order-modal');
|
||||||
const modalTitle = document.getElementById('modal-title');
|
const modalTitle = document.getElementById('modal-title');
|
||||||
const form = document.getElementById('work-order-form');
|
const form = document.getElementById('work-order-form');
|
||||||
|
const factoryInput = document.getElementById('form-factory');
|
||||||
|
|
||||||
form.reset();
|
form.reset();
|
||||||
|
|
||||||
@ -353,7 +362,7 @@
|
|||||||
// 编辑模式
|
// 编辑模式
|
||||||
modalTitle.textContent = '编辑工单';
|
modalTitle.textContent = '编辑工单';
|
||||||
document.getElementById('form-order-id').value = order.id;
|
document.getElementById('form-order-id').value = order.id;
|
||||||
document.getElementById('form-factory').value = order.factory;
|
factoryInput.value = order.factory;
|
||||||
document.getElementById('form-order-no').value = order.orderNo;
|
document.getElementById('form-order-no').value = order.orderNo;
|
||||||
document.getElementById('form-product-model').value = order.productModel || '';
|
document.getElementById('form-product-model').value = order.productModel || '';
|
||||||
document.getElementById('form-order-qty').value = order.orderQty;
|
document.getElementById('form-order-qty').value = order.orderQty;
|
||||||
@ -364,6 +373,18 @@
|
|||||||
// 添加模式
|
// 添加模式
|
||||||
modalTitle.textContent = '添加工单';
|
modalTitle.textContent = '添加工单';
|
||||||
document.getElementById('form-order-id').value = '';
|
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';
|
modal.style.display = 'flex';
|
||||||
|
|||||||
@ -85,6 +85,7 @@ const Router = (() => {
|
|||||||
'work-order': '生产工单下发中心',
|
'work-order': '生产工单下发中心',
|
||||||
'plan-mgmt': '计划管理',
|
'plan-mgmt': '计划管理',
|
||||||
'material-purchase': '物料清单-采购',
|
'material-purchase': '物料清单-采购',
|
||||||
|
'customer-order': '客户订单',
|
||||||
export: '导出',
|
export: '导出',
|
||||||
settings: '设置'
|
settings: '设置'
|
||||||
};
|
};
|
||||||
|
|||||||
313
server/app.py
313
server/app.py
@ -81,6 +81,10 @@ def init_db():
|
|||||||
c.execute('ALTER TABLE users ADD COLUMN avatar TEXT')
|
c.execute('ALTER TABLE users ADD COLUMN avatar TEXT')
|
||||||
except Exception:
|
except Exception:
|
||||||
pass # 列已存在
|
pass # 列已存在
|
||||||
|
try:
|
||||||
|
c.execute('ALTER TABLE users ADD COLUMN factory TEXT')
|
||||||
|
except Exception:
|
||||||
|
pass # 列已存在
|
||||||
try:
|
try:
|
||||||
c.execute('ALTER TABLE mac_batches ADD COLUMN platform TEXT DEFAULT "pdd"')
|
c.execute('ALTER TABLE mac_batches ADD COLUMN platform TEXT DEFAULT "pdd"')
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -184,6 +188,23 @@ def init_db():
|
|||||||
deleted INTEGER DEFAULT 0,
|
deleted INTEGER DEFAULT 0,
|
||||||
deleted_at TEXT
|
deleted_at TEXT
|
||||||
)''')
|
)''')
|
||||||
|
c.execute('''CREATE TABLE IF NOT EXISTS customer_orders(
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
order_date TEXT NOT NULL,
|
||||||
|
order_no TEXT NOT NULL,
|
||||||
|
customer_name TEXT NOT NULL,
|
||||||
|
material TEXT NOT NULL,
|
||||||
|
quantity INTEGER NOT NULL,
|
||||||
|
unit_price REAL NOT NULL,
|
||||||
|
created_by TEXT,
|
||||||
|
created_at TEXT,
|
||||||
|
updated_at TEXT
|
||||||
|
)''')
|
||||||
|
# 为已存在的表添加列(如果不存在)
|
||||||
|
try:
|
||||||
|
c.execute('ALTER TABLE customer_orders ADD COLUMN customer_name TEXT')
|
||||||
|
except Exception:
|
||||||
|
pass # 列已存在
|
||||||
# 为已存在的表添加列(如果不存在)
|
# 为已存在的表添加列(如果不存在)
|
||||||
try:
|
try:
|
||||||
c.execute('ALTER TABLE work_orders ADD COLUMN product_model TEXT')
|
c.execute('ALTER TABLE work_orders ADD COLUMN product_model TEXT')
|
||||||
@ -308,6 +329,52 @@ def notify_admins(action, detail=''):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def notify_admins_by_factory(action, detail='', factory=None):
|
||||||
|
"""为指定工厂的管理员创建通知(超级管理员操作时使用)"""
|
||||||
|
try:
|
||||||
|
user_id = session.get('user_id')
|
||||||
|
if not user_id:
|
||||||
|
return
|
||||||
|
|
||||||
|
conn = get_db()
|
||||||
|
c = conn.cursor()
|
||||||
|
|
||||||
|
# 获取当前用户信息
|
||||||
|
c.execute('SELECT username, role FROM users WHERE id=?', (user_id,))
|
||||||
|
user = c.fetchone()
|
||||||
|
if not user:
|
||||||
|
conn.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
# 只有超级管理员的操作才通知管理员
|
||||||
|
if user['role'] != 'superadmin':
|
||||||
|
conn.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
# 如果指定了工厂,只通知该工厂的管理员;否则通知所有管理员
|
||||||
|
if factory:
|
||||||
|
c.execute('SELECT id FROM users WHERE role=? AND factory=?', ('admin', factory))
|
||||||
|
else:
|
||||||
|
c.execute('SELECT id FROM users WHERE role=?', ('admin',))
|
||||||
|
|
||||||
|
admins = c.fetchall()
|
||||||
|
|
||||||
|
# 使用北京时间(UTC+8)
|
||||||
|
from datetime import timezone, timedelta
|
||||||
|
beijing_tz = timezone(timedelta(hours=8))
|
||||||
|
now = datetime.now(beijing_tz).isoformat()
|
||||||
|
|
||||||
|
for admin in admins:
|
||||||
|
c.execute('INSERT INTO notifications(user_id, username, action, detail, ts, read) VALUES(?,?,?,?,?,?)', (
|
||||||
|
admin['id'], user['username'], action, detail, now, 0
|
||||||
|
))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def get_redis():
|
def get_redis():
|
||||||
global _redis_client
|
global _redis_client
|
||||||
if not redis:
|
if not redis:
|
||||||
@ -547,13 +614,18 @@ def login():
|
|||||||
def me():
|
def me():
|
||||||
uid = session.get('user_id')
|
uid = session.get('user_id')
|
||||||
if not uid:
|
if not uid:
|
||||||
return jsonify({'username': None, 'role': None, 'avatar': None})
|
return jsonify({'username': None, 'role': None, 'avatar': None, 'factory': None})
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
c = conn.cursor()
|
c = conn.cursor()
|
||||||
c.execute('SELECT username, role, avatar FROM users WHERE id=?', (uid,))
|
c.execute('SELECT username, role, avatar, factory FROM users WHERE id=?', (uid,))
|
||||||
row = c.fetchone()
|
row = c.fetchone()
|
||||||
conn.close()
|
conn.close()
|
||||||
return jsonify({'username': row['username'], 'role': row['role'], 'avatar': row['avatar'] if row['avatar'] else None})
|
return jsonify({
|
||||||
|
'username': row['username'],
|
||||||
|
'role': row['role'],
|
||||||
|
'avatar': row['avatar'] if row['avatar'] else None,
|
||||||
|
'factory': row['factory'] if row['factory'] else None
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@app.post('/api/auth/logout')
|
@app.post('/api/auth/logout')
|
||||||
@ -1623,7 +1695,7 @@ def list_shipments():
|
|||||||
def list_users():
|
def list_users():
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
c = conn.cursor()
|
c = conn.cursor()
|
||||||
c.execute('SELECT username, role FROM users ORDER BY id ASC')
|
c.execute('SELECT username, role, factory FROM users ORDER BY id ASC')
|
||||||
rows = [dict(r) for r in c.fetchall()]
|
rows = [dict(r) for r in c.fetchall()]
|
||||||
conn.close()
|
conn.close()
|
||||||
return jsonify({'list': rows})
|
return jsonify({'list': rows})
|
||||||
@ -1675,6 +1747,37 @@ def change_password():
|
|||||||
return jsonify({'ok': True})
|
return jsonify({'ok': True})
|
||||||
|
|
||||||
|
|
||||||
|
@app.post('/api/admin/update-user-factory')
|
||||||
|
@require_login
|
||||||
|
@require_any_role('superadmin')
|
||||||
|
def update_user_factory():
|
||||||
|
"""更新用户所属工厂"""
|
||||||
|
data = request.get_json() or {}
|
||||||
|
username = data.get('username')
|
||||||
|
factory = (data.get('factory') or '').strip()
|
||||||
|
|
||||||
|
if not username:
|
||||||
|
return jsonify({'error': '用户名不能为空'}), 400
|
||||||
|
|
||||||
|
if not factory:
|
||||||
|
return jsonify({'error': '工厂名称不能为空'}), 400
|
||||||
|
|
||||||
|
conn = get_db()
|
||||||
|
c = conn.cursor()
|
||||||
|
c.execute('SELECT id FROM users WHERE username=?', (username,))
|
||||||
|
row = c.fetchone()
|
||||||
|
if not row:
|
||||||
|
conn.close()
|
||||||
|
return jsonify({'error': '用户不存在'}), 404
|
||||||
|
|
||||||
|
c.execute('UPDATE users SET factory=? WHERE id=?', (factory, row['id']))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
log('update_user_factory', f'username={username}, factory={factory}')
|
||||||
|
return jsonify({'ok': True, 'message': f'已更新用户 {username} 的所属工厂为 {factory}'})
|
||||||
|
|
||||||
|
|
||||||
@app.post('/api/admin/add-user')
|
@app.post('/api/admin/add-user')
|
||||||
@require_login
|
@require_login
|
||||||
@require_any_role('superadmin')
|
@require_any_role('superadmin')
|
||||||
@ -1684,10 +1787,14 @@ def add_user():
|
|||||||
username = (data.get('username') or '').strip()
|
username = (data.get('username') or '').strip()
|
||||||
password = data.get('password')
|
password = data.get('password')
|
||||||
role = (data.get('role') or 'admin').strip()
|
role = (data.get('role') or 'admin').strip()
|
||||||
|
factory = (data.get('factory') or '').strip()
|
||||||
|
|
||||||
if not username or not password:
|
if not username or not password:
|
||||||
return jsonify({'error': '用户名和密码不能为空'}), 400
|
return jsonify({'error': '用户名和密码不能为空'}), 400
|
||||||
|
|
||||||
|
if not factory:
|
||||||
|
return jsonify({'error': '所属工厂不能为空'}), 400
|
||||||
|
|
||||||
if role not in ['admin', 'superadmin']:
|
if role not in ['admin', 'superadmin']:
|
||||||
return jsonify({'error': '角色必须是 admin 或 superadmin'}), 400
|
return jsonify({'error': '角色必须是 admin 或 superadmin'}), 400
|
||||||
|
|
||||||
@ -1702,12 +1809,12 @@ def add_user():
|
|||||||
|
|
||||||
# 创建新用户
|
# 创建新用户
|
||||||
try:
|
try:
|
||||||
c.execute('INSERT INTO users(username, password_hash, role) VALUES(?,?,?)',
|
c.execute('INSERT INTO users(username, password_hash, role, factory) VALUES(?,?,?,?)',
|
||||||
(username, generate_password_hash(password), role))
|
(username, generate_password_hash(password), role, factory))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
log('add_user', f'username={username}, role={role}')
|
log('add_user', f'username={username}, role={role}, factory={factory}')
|
||||||
return jsonify({'ok': True, 'message': f'用户 {username} 创建成功'})
|
return jsonify({'ok': True, 'message': f'用户 {username} 创建成功'})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
conn.close()
|
conn.close()
|
||||||
@ -2780,12 +2887,26 @@ def get_work_orders():
|
|||||||
order_no = request.args.get('order', '')
|
order_no = request.args.get('order', '')
|
||||||
date = request.args.get('date', '')
|
date = request.args.get('date', '')
|
||||||
|
|
||||||
|
# 获取当前用户信息
|
||||||
|
user_id = session.get('user_id')
|
||||||
|
user_role = session.get('role')
|
||||||
|
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
c = conn.cursor()
|
c = conn.cursor()
|
||||||
|
|
||||||
|
# 获取用户所属工厂
|
||||||
|
c.execute('SELECT factory FROM users WHERE id=?', (user_id,))
|
||||||
|
user_row = c.fetchone()
|
||||||
|
user_factory = user_row['factory'] if user_row and user_row['factory'] else None
|
||||||
|
|
||||||
query = 'SELECT * FROM work_orders WHERE 1=1'
|
query = 'SELECT * FROM work_orders WHERE 1=1'
|
||||||
params = []
|
params = []
|
||||||
|
|
||||||
|
# 如果是管理员(非超级管理员),只能看到自己工厂的订单
|
||||||
|
if user_role == 'admin' and user_factory:
|
||||||
|
query += ' AND factory = ?'
|
||||||
|
params.append(user_factory)
|
||||||
|
|
||||||
if factory:
|
if factory:
|
||||||
query += ' AND factory LIKE ?'
|
query += ' AND factory LIKE ?'
|
||||||
params.append(f'%{factory}%')
|
params.append(f'%{factory}%')
|
||||||
@ -2840,13 +2961,28 @@ def create_work_order():
|
|||||||
production_end_time = data.get('productionEndTime', '')
|
production_end_time = data.get('productionEndTime', '')
|
||||||
remark = data.get('remark', '').strip()
|
remark = data.get('remark', '').strip()
|
||||||
|
|
||||||
if not factory or not order_no or not order_qty:
|
# 获取当前用户信息
|
||||||
return jsonify({'error': '请填写所有必填项'}), 400
|
user_id = session.get('user_id')
|
||||||
|
user_role = session.get('role')
|
||||||
|
username = session.get('username', '')
|
||||||
|
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
c = conn.cursor()
|
c = conn.cursor()
|
||||||
|
|
||||||
username = session.get('username', '')
|
# 如果是管理员(非超级管理员),强制使用用户自己的工厂
|
||||||
|
if user_role == 'admin':
|
||||||
|
c.execute('SELECT factory FROM users WHERE id=?', (user_id,))
|
||||||
|
user_row = c.fetchone()
|
||||||
|
if user_row and user_row['factory']:
|
||||||
|
factory = user_row['factory']
|
||||||
|
else:
|
||||||
|
conn.close()
|
||||||
|
return jsonify({'error': '您的账户未设置所属工厂,请联系超级管理员'}), 400
|
||||||
|
|
||||||
|
if not factory or not order_no or not order_qty:
|
||||||
|
conn.close()
|
||||||
|
return jsonify({'error': '请填写所有必填项'}), 400
|
||||||
|
|
||||||
now = get_beijing_time()
|
now = get_beijing_time()
|
||||||
|
|
||||||
c.execute('''INSERT INTO work_orders(
|
c.execute('''INSERT INTO work_orders(
|
||||||
@ -2863,17 +2999,17 @@ def create_work_order():
|
|||||||
|
|
||||||
log('create_work_order', f'工单号: {order_no}, 工厂: {factory}, 型号: {product_model}')
|
log('create_work_order', f'工单号: {order_no}, 工厂: {factory}, 型号: {product_model}')
|
||||||
|
|
||||||
# 如果是超级管理员添加工单,通知所有管理员
|
# 如果是超级管理员添加工单,通知该工厂的管理员
|
||||||
notify_admins('添加工单', f'工单号: {order_no}, 工厂: {factory}, 数量: {order_qty}')
|
notify_admins_by_factory('添加工单', f'工单号: {order_no}, 工厂: {factory}, 数量: {order_qty}', factory=factory)
|
||||||
|
|
||||||
return jsonify({'ok': True, 'id': order_id, 'message': '工单创建成功'})
|
return jsonify({'ok': True, 'id': order_id, 'message': '工单创建成功'})
|
||||||
|
|
||||||
|
|
||||||
@app.put('/api/work-orders/<int:order_id>')
|
@app.put('/api/work-orders/<int:order_id>')
|
||||||
@require_login
|
@require_login
|
||||||
@require_any_role('admin','superadmin')
|
@require_any_role('superadmin')
|
||||||
def update_work_order(order_id):
|
def update_work_order(order_id):
|
||||||
"""更新工单"""
|
"""更新工单(仅超级管理员)"""
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
|
|
||||||
factory = data.get('factory', '').strip()
|
factory = data.get('factory', '').strip()
|
||||||
@ -2910,16 +3046,16 @@ def update_work_order(order_id):
|
|||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
log('update_work_order', f'工单ID: {order_id}, 工单号: {order_no}, 型号: {product_model}')
|
log('update_work_order', f'工单ID: {order_id}, 工单号: {order_no}, 型号: {product_model}')
|
||||||
notify_superadmin('更新工单', f'工单号: {order_no}, 工厂: {factory}, 型号: {product_model}')
|
notify_admins_by_factory('更新工单', f'工单号: {order_no}, 工厂: {factory}, 型号: {product_model}', factory=factory)
|
||||||
|
|
||||||
return jsonify({'ok': True, 'message': '工单更新成功'})
|
return jsonify({'ok': True, 'message': '工单更新成功'})
|
||||||
|
|
||||||
|
|
||||||
@app.delete('/api/work-orders/<int:order_id>')
|
@app.delete('/api/work-orders/<int:order_id>')
|
||||||
@require_login
|
@require_login
|
||||||
@require_any_role('admin','superadmin')
|
@require_any_role('superadmin')
|
||||||
def delete_work_order(order_id):
|
def delete_work_order(order_id):
|
||||||
"""删除工单"""
|
"""删除工单(仅超级管理员)"""
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
c = conn.cursor()
|
c = conn.cursor()
|
||||||
|
|
||||||
@ -2939,7 +3075,7 @@ def delete_work_order(order_id):
|
|||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
log('delete_work_order', f'工单ID: {order_id}, 工单号: {order_no}')
|
log('delete_work_order', f'工单ID: {order_id}, 工单号: {order_no}')
|
||||||
notify_superadmin('删除工单', f'工单号: {order_no}, 工厂: {factory}')
|
notify_admins_by_factory('删除工单', f'工单号: {order_no}, 工厂: {factory}', factory=factory)
|
||||||
|
|
||||||
return jsonify({'ok': True, 'message': '工单删除成功'})
|
return jsonify({'ok': True, 'message': '工单删除成功'})
|
||||||
|
|
||||||
@ -2949,6 +3085,10 @@ def delete_work_order(order_id):
|
|||||||
@require_any_role('admin','superadmin')
|
@require_any_role('admin','superadmin')
|
||||||
def confirm_work_order(order_id):
|
def confirm_work_order(order_id):
|
||||||
"""确认工单"""
|
"""确认工单"""
|
||||||
|
# 获取当前用户信息
|
||||||
|
user_id = session.get('user_id')
|
||||||
|
user_role = session.get('role')
|
||||||
|
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
c = conn.cursor()
|
c = conn.cursor()
|
||||||
|
|
||||||
@ -2964,6 +3104,16 @@ def confirm_work_order(order_id):
|
|||||||
factory = row['factory']
|
factory = row['factory']
|
||||||
current_status = row['status']
|
current_status = row['status']
|
||||||
|
|
||||||
|
# 如果是管理员(非超级管理员),检查工单是否属于自己的工厂
|
||||||
|
if user_role == 'admin':
|
||||||
|
c.execute('SELECT factory FROM users WHERE id=?', (user_id,))
|
||||||
|
user_row = c.fetchone()
|
||||||
|
user_factory = user_row['factory'] if user_row and user_row['factory'] else None
|
||||||
|
|
||||||
|
if not user_factory or factory != user_factory:
|
||||||
|
conn.close()
|
||||||
|
return jsonify({'error': '您只能确认自己工厂的工单'}), 403
|
||||||
|
|
||||||
# 如果已经确认,返回提示
|
# 如果已经确认,返回提示
|
||||||
if current_status == 'confirmed':
|
if current_status == 'confirmed':
|
||||||
conn.close()
|
conn.close()
|
||||||
@ -3020,6 +3170,7 @@ def convert_material_purchase_to_camel(row):
|
|||||||
|
|
||||||
@app.get('/api/material-purchase/list')
|
@app.get('/api/material-purchase/list')
|
||||||
@require_login
|
@require_login
|
||||||
|
@require_any_role('superadmin')
|
||||||
def list_material_purchase():
|
def list_material_purchase():
|
||||||
"""获取物料清单列表(不包括已删除的)"""
|
"""获取物料清单列表(不包括已删除的)"""
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
@ -3034,6 +3185,7 @@ def list_material_purchase():
|
|||||||
|
|
||||||
@app.get('/api/material-purchase/recycle-bin')
|
@app.get('/api/material-purchase/recycle-bin')
|
||||||
@require_login
|
@require_login
|
||||||
|
@require_any_role('superadmin')
|
||||||
def list_material_purchase_recycle_bin():
|
def list_material_purchase_recycle_bin():
|
||||||
"""获取回收站列表"""
|
"""获取回收站列表"""
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
@ -3048,7 +3200,7 @@ def list_material_purchase_recycle_bin():
|
|||||||
|
|
||||||
@app.post('/api/material-purchase/add')
|
@app.post('/api/material-purchase/add')
|
||||||
@require_login
|
@require_login
|
||||||
@require_any_role('admin', 'superadmin')
|
@require_any_role('superadmin')
|
||||||
def add_material_purchase():
|
def add_material_purchase():
|
||||||
"""新增物料需求"""
|
"""新增物料需求"""
|
||||||
data = request.get_json() or {}
|
data = request.get_json() or {}
|
||||||
@ -3089,7 +3241,7 @@ def add_material_purchase():
|
|||||||
|
|
||||||
@app.post('/api/material-purchase/update')
|
@app.post('/api/material-purchase/update')
|
||||||
@require_login
|
@require_login
|
||||||
@require_any_role('admin', 'superadmin')
|
@require_any_role('superadmin')
|
||||||
def update_material_purchase():
|
def update_material_purchase():
|
||||||
"""更新物料需求"""
|
"""更新物料需求"""
|
||||||
data = request.get_json() or {}
|
data = request.get_json() or {}
|
||||||
@ -3126,7 +3278,7 @@ def update_material_purchase():
|
|||||||
|
|
||||||
@app.post('/api/material-purchase/delete')
|
@app.post('/api/material-purchase/delete')
|
||||||
@require_login
|
@require_login
|
||||||
@require_any_role('admin', 'superadmin')
|
@require_any_role('superadmin')
|
||||||
def delete_material_purchase():
|
def delete_material_purchase():
|
||||||
"""删除物料需求(移到回收站)"""
|
"""删除物料需求(移到回收站)"""
|
||||||
data = request.get_json() or {}
|
data = request.get_json() or {}
|
||||||
@ -3153,7 +3305,7 @@ def delete_material_purchase():
|
|||||||
|
|
||||||
@app.post('/api/material-purchase/restore')
|
@app.post('/api/material-purchase/restore')
|
||||||
@require_login
|
@require_login
|
||||||
@require_any_role('admin', 'superadmin')
|
@require_any_role('superadmin')
|
||||||
def restore_material_purchase():
|
def restore_material_purchase():
|
||||||
"""从回收站恢复"""
|
"""从回收站恢复"""
|
||||||
data = request.get_json() or {}
|
data = request.get_json() or {}
|
||||||
@ -3178,7 +3330,7 @@ def restore_material_purchase():
|
|||||||
|
|
||||||
@app.post('/api/material-purchase/permanent-delete')
|
@app.post('/api/material-purchase/permanent-delete')
|
||||||
@require_login
|
@require_login
|
||||||
@require_any_role('admin', 'superadmin')
|
@require_any_role('superadmin')
|
||||||
def permanent_delete_material_purchase():
|
def permanent_delete_material_purchase():
|
||||||
"""永久删除"""
|
"""永久删除"""
|
||||||
data = request.get_json() or {}
|
data = request.get_json() or {}
|
||||||
@ -3201,7 +3353,7 @@ def permanent_delete_material_purchase():
|
|||||||
|
|
||||||
@app.post('/api/material-purchase/empty-recycle-bin')
|
@app.post('/api/material-purchase/empty-recycle-bin')
|
||||||
@require_login
|
@require_login
|
||||||
@require_any_role('admin', 'superadmin')
|
@require_any_role('superadmin')
|
||||||
def empty_recycle_bin():
|
def empty_recycle_bin():
|
||||||
"""清空回收站"""
|
"""清空回收站"""
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
@ -3219,7 +3371,7 @@ def empty_recycle_bin():
|
|||||||
|
|
||||||
@app.route('/api/validate/material-purchase-file', methods=['POST'])
|
@app.route('/api/validate/material-purchase-file', methods=['POST'])
|
||||||
@require_login
|
@require_login
|
||||||
@require_any_role('admin', 'superadmin')
|
@require_any_role('superadmin')
|
||||||
def validate_material_purchase_file():
|
def validate_material_purchase_file():
|
||||||
"""验证物料清单Excel文件格式"""
|
"""验证物料清单Excel文件格式"""
|
||||||
f = request.files.get('file')
|
f = request.files.get('file')
|
||||||
@ -3274,7 +3426,7 @@ def validate_material_purchase_file():
|
|||||||
|
|
||||||
@app.post('/api/upload/material-purchase-file')
|
@app.post('/api/upload/material-purchase-file')
|
||||||
@require_login
|
@require_login
|
||||||
@require_any_role('admin', 'superadmin')
|
@require_any_role('superadmin')
|
||||||
def upload_material_purchase_file():
|
def upload_material_purchase_file():
|
||||||
"""上传物料清单Excel文件"""
|
"""上传物料清单Excel文件"""
|
||||||
f = request.files.get('file')
|
f = request.files.get('file')
|
||||||
@ -3398,6 +3550,115 @@ def upload_material_purchase_file():
|
|||||||
return jsonify({'error': f'导入失败:{str(e)}'}), 500
|
return jsonify({'error': f'导入失败:{str(e)}'}), 500
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== 客户订单 API ====================
|
||||||
|
|
||||||
|
@app.get('/api/customer-orders')
|
||||||
|
@require_login
|
||||||
|
@require_any_role('superadmin')
|
||||||
|
def get_customer_orders():
|
||||||
|
"""获取客户订单列表"""
|
||||||
|
conn = get_db()
|
||||||
|
c = conn.cursor()
|
||||||
|
c.execute('''SELECT id, order_date, order_no, customer_name, material, quantity, unit_price,
|
||||||
|
created_by, created_at, updated_at
|
||||||
|
FROM customer_orders
|
||||||
|
ORDER BY order_date DESC, id DESC''')
|
||||||
|
rows = c.fetchall()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
orders = []
|
||||||
|
for row in rows:
|
||||||
|
orders.append({
|
||||||
|
'id': row['id'],
|
||||||
|
'order_date': row['order_date'],
|
||||||
|
'order_no': row['order_no'],
|
||||||
|
'customer_name': row['customer_name'] if 'customer_name' in row.keys() else '',
|
||||||
|
'material': row['material'],
|
||||||
|
'quantity': row['quantity'],
|
||||||
|
'unit_price': row['unit_price'],
|
||||||
|
'created_by': row['created_by'],
|
||||||
|
'created_at': row['created_at'],
|
||||||
|
'updated_at': row['updated_at']
|
||||||
|
})
|
||||||
|
|
||||||
|
return jsonify({'list': orders})
|
||||||
|
|
||||||
|
|
||||||
|
@app.post('/api/customer-orders')
|
||||||
|
@require_login
|
||||||
|
@require_any_role('superadmin')
|
||||||
|
def create_customer_order():
|
||||||
|
"""创建客户订单"""
|
||||||
|
data = request.get_json() or {}
|
||||||
|
|
||||||
|
order_date = data.get('order_date', '').strip()
|
||||||
|
order_no = data.get('order_no', '').strip()
|
||||||
|
customer_name = data.get('customer_name', '').strip()
|
||||||
|
material = data.get('material', '').strip()
|
||||||
|
quantity = data.get('quantity', 0)
|
||||||
|
unit_price = data.get('unit_price', 0)
|
||||||
|
|
||||||
|
if not order_date or not order_no or not customer_name or not material:
|
||||||
|
return jsonify({'error': '请填写所有必填项'}), 400
|
||||||
|
|
||||||
|
if quantity <= 0:
|
||||||
|
return jsonify({'error': '订单数量必须大于0'}), 400
|
||||||
|
|
||||||
|
if unit_price < 0:
|
||||||
|
return jsonify({'error': '单价不能为负数'}), 400
|
||||||
|
|
||||||
|
username = session.get('username', '')
|
||||||
|
now = get_beijing_time()
|
||||||
|
|
||||||
|
conn = get_db()
|
||||||
|
c = conn.cursor()
|
||||||
|
|
||||||
|
c.execute('''INSERT INTO customer_orders(
|
||||||
|
order_date, order_no, customer_name, material, quantity, unit_price,
|
||||||
|
created_by, created_at, updated_at
|
||||||
|
) VALUES(?,?,?,?,?,?,?,?,?)''', (
|
||||||
|
order_date, order_no, customer_name, material, quantity, unit_price,
|
||||||
|
username, now, now
|
||||||
|
))
|
||||||
|
|
||||||
|
order_id = c.lastrowid
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
log('create_customer_order', f'客户: {customer_name}, 订单号: {order_no}, 物料: {material}, 数量: {quantity}')
|
||||||
|
|
||||||
|
return jsonify({'ok': True, 'id': order_id, 'message': '订单创建成功'})
|
||||||
|
|
||||||
|
|
||||||
|
@app.delete('/api/customer-orders/<int:order_id>')
|
||||||
|
@require_login
|
||||||
|
@require_any_role('superadmin')
|
||||||
|
def delete_customer_order(order_id):
|
||||||
|
"""删除客户订单"""
|
||||||
|
conn = get_db()
|
||||||
|
c = conn.cursor()
|
||||||
|
|
||||||
|
# 获取订单信息用于日志
|
||||||
|
c.execute('SELECT order_no, customer_name, material FROM customer_orders WHERE id=?', (order_id,))
|
||||||
|
row = c.fetchone()
|
||||||
|
|
||||||
|
if not row:
|
||||||
|
conn.close()
|
||||||
|
return jsonify({'error': '订单不存在'}), 404
|
||||||
|
|
||||||
|
order_no = row['order_no']
|
||||||
|
customer_name = row['customer_name'] if 'customer_name' in row.keys() else ''
|
||||||
|
material = row['material']
|
||||||
|
|
||||||
|
c.execute('DELETE FROM customer_orders WHERE id=?', (order_id,))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
log('delete_customer_order', f'订单ID: {order_id}, 客户: {customer_name}, 订单号: {order_no}')
|
||||||
|
|
||||||
|
return jsonify({'ok': True, 'message': '订单删除成功'})
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def not_found(e):
|
def not_found(e):
|
||||||
# 如果请求的是 HTML 页面(通过 Accept header 判断),返回 index.html
|
# 如果请求的是 HTML 页面(通过 Accept header 判断),返回 index.html
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user