feat: 新增IM即时通讯(浮窗)、工作流、打印模块及工作台增强

- IM: 新增浮窗聊天(ImFloatWindow)、管理页(monitor/config/service/message)、SSE推送
- 工作流: 新增待办/我的流程页面及后端服务
- 打印: 新增打印模板、出库单打印(PrintPage)、模板种子脚本
- 工作台: 增强快捷入口与工作台数据
- 修复: TagsView页签关闭、CrudPage通用表格增强
- 移除导航菜单中的即时通讯入口,改为右下角浮窗
This commit is contained in:
2026-08-16 00:19:24 +08:00
parent 7235265749
commit 1bec470647
49 changed files with 5791 additions and 51 deletions
+104
View File
@@ -0,0 +1,104 @@
/**
* 创建缫丝厂组织树 + 为全部用户绑定所属组织
* 用法:node server/scripts/seed-org.mjs
* 依赖:管理员 13800000000/123456 可用
*/
const BASE = process.env.F9MES_API || 'http://localhost:5136';
// 组织树:name -> { type, children }
const ORGS = [
{ name: '缫丝总厂', type: 0, children: [
{ name: '选茧车间', type: 2 },
{ name: '煮茧车间', type: 2 },
{ name: '缫丝一车间', type: 2 },
{ name: '缫丝二车间', type: 2 },
{ name: '复摇车间', type: 2 },
{ name: '质检部', type: 1 },
{ name: '设备动力部', type: 1 },
{ name: '仓储部', type: 1 },
{ name: '综合管理部', type: 1 },
]},
];
const USER_ORG_PLAN = {
1: '综合管理部', // 管理员
2: '缫丝一车间', // 李工(车间主管)
// 3~22 随机用户:按 id 循环分配
};
async function api(h, method, url, body) {
const res = await fetch(BASE + url, {
method, headers: h, body: body ? JSON.stringify(body) : undefined,
});
const text = await res.text();
let json = null;
try { json = JSON.parse(text); } catch { /* ignore */ }
return { ok: res.ok, json, text };
}
async function main() {
const login = await api({ 'Content-Type': 'application/json' }, 'POST', '/api/auth/login',
{ phone: '13800000000', password: '123456' });
if (!login.ok || login.json?.code !== 0) throw new Error('管理员登录失败: ' + login.text.slice(0, 200));
const token = login.json.data.token;
const h = { Authorization: 'Bearer ' + token, 'Content-Type': 'application/json' };
// ---- 1. 建组织树(若已存在同名则跳过)----
const orgs = await api(h, 'GET', '/api/data/BaseSys_Org/all');
const existing = (Array.isArray(orgs.json?.data) ? orgs.json.data : orgs.json?.data?.data ?? []);
const byName = new Map(existing.map((o) => [o.name, o]));
const nameToId = {}; // 组织名 -> id(含父子路径的 key 冗余,便于解析)
const addOrg = async (o, parentId, level, sort) => {
if (byName.has(o.name)) {
nameToId[o.name] = byName.get(o.name).id;
nameToId[`${parentId}/${o.name}`] = byName.get(o.name).id;
return;
}
const r = await api(h, 'POST', '/api/data/BaseSys_Org/add', {
parentId, name: o.name, orgType: o.type, sort, level,
path: parentId === 0 ? '' : `/${parentId}`,
});
if (!r.ok || r.json?.code !== 0) throw new Error(`建组织失败 ${o.name}: ${r.text.slice(0, 200)}`);
nameToId[o.name] = r.json.data.id;
nameToId[`${parentId}/${o.name}`] = r.json.data.id;
byName.set(o.name, r.json.data);
console.log(` 组织: ${' '.repeat(level)}${o.name} (id=${r.json.data.id})`);
};
console.log('=== 创建组织树 ===');
for (const root of ORGS) {
await addOrg(root, 0, 0, 1);
for (let i = 0; i < root.children.length; i++) {
await addOrg(root.children[i], nameToId[root.name], 1, i + 1);
}
}
// ---- 2. 绑定用户组织(先清空旧绑定,保证幂等)----
console.log('=== 绑定用户组织 ===');
const users = await api(h, 'GET', '/api/data/BaseSys_User/all');
const userList = (Array.isArray(users.json?.data) ? users.json.data : users.json?.data?.data ?? []);
const oldBinds = await api(h, 'GET', '/api/data/BaseSys_UserOrg/all');
const oldList = (Array.isArray(oldBinds.json?.data) ? oldBinds.json.data : oldBinds.json?.data?.data ?? []);
const delIds = oldList.map((b) => b.id);
if (delIds.length) await api(h, 'POST', '/api/data/BaseSys_UserOrg/deleteRange', delIds);
const orgIdOf = (name) => nameToId[name];
// 有效部门列表(车间+部门,不含总厂)
const leafNames = ORGS[0].children.map((c) => c.name);
let bindCount = 0;
for (const u of userList) {
let orgName = USER_ORG_PLAN[u.id];
if (!orgName) orgName = leafNames[(u.id - 3 + leafNames.length) % leafNames.length];
const orgId = orgIdOf(orgName);
if (!orgId) { console.log(` 跳过 ${u.name}(无组织 ${orgName}`); continue; }
const r = await api(h, 'POST', '/api/data/BaseSys_UserOrg/add', { userId: u.id, orgId });
if (r.ok && r.json?.code === 0) { bindCount++; console.log(` ${u.name} (id=${u.id}) -> ${orgName}`); }
else console.log(` 失败 ${u.name}: ${r.text.slice(0, 100)}`);
}
console.log(`\n完成:绑定 ${bindCount} 个用户`);
}
main().catch((e) => { console.error('ERR: ' + e.message); process.exit(1); });