diff --git a/.gitignore b/.gitignore index e5f8403..2cb85a7 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,9 @@ logs/ Thumbs.db .idea/ .vscode/ + +# ===== 工作区记忆 / 调试产物 ===== +.codebuddy/ +server/scripts/*.pdf +server/src/F9MES.Api/logs-err.txt +server/src/F9MES.Api/logs-out.txt diff --git a/openprint/src/core/headless/createHeadless.ts b/openprint/src/core/headless/createHeadless.ts index 1cec6a6..2233645 100644 --- a/openprint/src/core/headless/createHeadless.ts +++ b/openprint/src/core/headless/createHeadless.ts @@ -150,16 +150,31 @@ export function createHeadless(options: HeadlessOptions = {}): HeadlessInstance } } -/** 静默打印:瞬态 0 尺寸 iframe + srcdoc + window.print(),完成后立即移除 */ +/** 从渲染 HTML 中解析纸张尺寸(@page size 或 CSS 变量 --op-page-w/--op-page-h),默认 A4 */ +function parsePaperSize(html: string): { w: string; h: string } { + const fromAtPage = html.match(/@page\s*{[^}]*size:\s*([\d.]+)mm\s+([\d.]+)mm/i) + if (fromAtPage) return { w: `${fromAtPage[1]}mm`, h: `${fromAtPage[2]}mm` } + const fromVars = html.match(/--op-page-w:\s*([\d.]+)mm[^}]*?--op-page-h:\s*([\d.]+)mm/i) + if (fromVars) return { w: `${fromVars[1]}mm`, h: `${fromVars[2]}mm` } + return { w: '210mm', h: '297mm' } +} + +/** + * 静默打印:瞬态 iframe + srcdoc + window.print(),完成后立即移除。 + * 注意:iframe 必须带实际纸张尺寸并移出屏幕(不可 0×0,否则 Chrome/Edge + * 打印对话框以 0 视口渲染,预览为空白), + */ function silentPrint(html: string): Promise { return new Promise((resolve, reject) => { if (typeof document === 'undefined') { reject(new Error('silentPrint 需要浏览器环境')) return } + const { w, h } = parsePaperSize(html) const iframe = document.createElement('iframe') iframe.setAttribute('aria-hidden', 'true') - iframe.style.cssText = 'position:fixed;right:0;bottom:0;width:0;height:0;border:0;' + // 移到屏幕外但保留纸张尺寸视口,保证打印对话框预览可见 + iframe.style.cssText = `position:fixed;left:-9999px;top:0;width:${w};height:${h};border:0;` let removed = false const cleanup = () => { if (removed) return diff --git a/print-page.png b/print-page.png new file mode 100644 index 0000000..a4f6c71 Binary files /dev/null and b/print-page.png differ diff --git a/server/scripts/debug-outstock-save.mjs b/server/scripts/debug-outstock-save.mjs new file mode 100644 index 0000000..62c9147 --- /dev/null +++ b/server/scripts/debug-outstock-save.mjs @@ -0,0 +1,68 @@ +/** + * 复现原料出库单保存 500 错误 + * 用法:node server/scripts/debug-outstock-save.mjs + */ +const BASE = process.env.F9MES_API || 'http://localhost:5136'; +const PHONE = '13800000000'; +const PASSWORD = '123456'; + +async function main() { + const login = await fetch(BASE + '/api/auth/login', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ phone: PHONE, password: PASSWORD }), + }).then((r) => r.json()); + if (login.code !== 0) throw new Error('登录失败: ' + JSON.stringify(login)); + const h = { Authorization: 'Bearer ' + login.data.token, 'Content-Type': 'application/json' }; + + // 取第一个庄口 + const zk = await fetch(BASE + '/api/data/RawMaterial_Zhuangkou/all', { headers: h }).then((r) => r.json()); + const zkItems = (zk.data && (zk.data.data || zk.data.items)) || (Array.isArray(zk.data) ? zk.data : []); + if (!zkItems.length) throw new Error('无庄口'); + const zhuangkouId = zkItems[0].id; + console.log('使用庄口 id=' + zhuangkouId); + + // 取第一个组织(OutOrgId) + let outOrgId = 0; + try { + const org = await fetch(BASE + '/api/data/RawMaterial_Org/all', { headers: h }).then((r) => r.json()); + const orgItems = (org.data && (org.data.data || org.data.items)) || (Array.isArray(org.data) ? org.data : []); + if (orgItems.length) outOrgId = orgItems[0].id; + } catch { /* 无组织表则用0 */ } + console.log('使用组织 id=' + outOrgId); + + // 生成单号 + let billNo = 'YLC' + new Date().toISOString().slice(0, 10).replace(/-/g, '') + '-DBG'; + try { + const g = await fetch(BASE + '/api/meta/gencode/RawMaterial_OutStock/billNo', { method: 'POST', headers: h }).then((r) => r.json()); + if (g.code === 0 && g.data && g.data.value) billNo = g.data.value; + } catch (e) { console.log('gencode 失败,使用自造单号', e.message); } + console.log('使用单号 ' + billNo); + + // 模拟前端 payload(与 outstock-bill.vue handleSave 完全一致) + const payload = { + billNo, + zhuangkouId, + outOrgId, + outWeight: 100, + receiver: '张三', + outDate: '2026-08-16 14:30:00', + outType: 0, + status: 0, + }; + + const resp = await fetch(BASE + '/api/data/RawMaterial_OutStock/add', { + method: 'POST', + headers: h, + body: JSON.stringify(payload), + }); + const text = await resp.text(); + console.log('HTTP ' + resp.status); + console.log('BODY>>>'); + console.log(text.slice(0, 3000)); +} + +main().catch((e) => { + console.error('ERR: ' + e.message); + process.exit(1); +}); diff --git a/server/scripts/verify-iframe-size.mjs b/server/scripts/verify-iframe-size.mjs new file mode 100644 index 0000000..bcda66a --- /dev/null +++ b/server/scripts/verify-iframe-size.mjs @@ -0,0 +1,46 @@ +// 验证修复后 iframe 尺寸非 0,且打印内容完整 +import { createRequire } from 'module' +import fs from 'fs' + +const PW = 'C:/Users/范先生/AppData/Roaming/npm/node_modules/@playwright/cli/node_modules/playwright-core' +const require = createRequire(PW + '/package.json') +const { chromium } = require(PW) + +const B = 'http://localhost:5136' +const token = fs.readFileSync(new URL('_token.txt', import.meta.url), 'utf8').trim() +const url = `http://localhost:5227/?print=1&template=2&table=RawMaterial_OutStock&row=2&token=${encodeURIComponent(token)}&api=${B}` + +const browser = await chromium.launch({ headless: true, channel: 'msedge' }) +const page = await browser.newPage() +page.on('pageerror', (e) => console.log('[pageerror]', e.message)) +page.on('console', (m) => { if (m.type() === 'error') console.log('[console]', m.text()) }) +await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 }) + +for (let i = 0; i < 40; i++) { + const ok = await page.evaluate(() => { + const f = document.querySelector('iframe') + return !!(f && f.srcdoc && f.srcdoc.length > 1000) + }) + if (ok) break + await new Promise((r) => setTimeout(r, 500)) +} + +const info = await page.evaluate(() => { + const f = document.querySelector('iframe') + if (!f) return { hasIframe: false } + const rect = f.getBoundingClientRect() + const d = f.contentDocument + return { + hasIframe: true, + iframeCss: f.style.cssText, + rectW: rect.width, + rectH: rect.height, + srcdocLen: f.srcdoc.length, + pages: d ? d.querySelectorAll('.op-page').length : -1, + bodyText: d ? (d.body.textContent || '').replace(/\s+/g, ' ').slice(0, 200) : '', + body: d ? d.body.innerHTML.slice(0, 300) : '', + } +}) +console.log(JSON.stringify(info, null, 1)) +await browser.close() +console.log('DONE') diff --git a/server/src/F9MES.Api/Controllers/DataController.cs b/server/src/F9MES.Api/Controllers/DataController.cs index d71c6cd..ae31f91 100644 --- a/server/src/F9MES.Api/Controllers/DataController.cs +++ b/server/src/F9MES.Api/Controllers/DataController.cs @@ -282,14 +282,19 @@ public class DataController : ControllerBase return matches.Count == 1 ? matches[0] : null; } + /// 请求体 JSON 序列化选项:大小写不敏感 + 宽松日期解析(兼容 "yyyy-MM-dd HH:mm:ss") + private static readonly System.Text.Json.JsonSerializerOptions BodyJsonOptions = new() + { + PropertyNameCaseInsensitive = true, + Converters = { new Converters.FlexibleDateTimeConverter() } + }; + /// 将请求体 JSON 转换为目标实体对象 private object? ConvertBody(string table, object body) { if (!EntityCatalog.TryGet(table, out var entityType)) return null; - var json = System.Text.Json.JsonSerializer.Serialize(body, - new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }); - return System.Text.Json.JsonSerializer.Deserialize(json, entityType, - new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + var json = System.Text.Json.JsonSerializer.Serialize(body, BodyJsonOptions); + return System.Text.Json.JsonSerializer.Deserialize(json, entityType, BodyJsonOptions); } /// 提取请求体中出现的字段名(用于部分更新,属性名大小写不敏感) diff --git a/server/src/F9MES.Api/Controllers/MetaController.cs b/server/src/F9MES.Api/Controllers/MetaController.cs index 2a33db3..78798a4 100644 --- a/server/src/F9MES.Api/Controllers/MetaController.cs +++ b/server/src/F9MES.Api/Controllers/MetaController.cs @@ -57,7 +57,8 @@ public class MetaController : ControllerBase ["PackageId"] = ("Fims_Package", "PackageNo", null, null), ["GradeId"] = ("BaseCommon_GradeStandard", "Name", null, null), ["RepackId"] = ("RawMaterial_Repack", "PlanNo", null, null), - ["PurchaseId"] = ("RawMaterial_Purchase", "BillNo", null, null) + ["PurchaseId"] = ("RawMaterial_Purchase", "BillNo", null, null), + ["WarehouseId"] = ("RawMaterial_Warehouse", "Name", null, null) }; /// @@ -71,6 +72,8 @@ public class MetaController : ControllerBase ["RawMaterial_Repack"] = ("PlanNo", "RZ", "yyyyMMdd", 3, "", "-"), ["RawMaterial_OutStock"] = ("BillNo", "YLC", "yyyyMMdd", 4, "", "-"), ["RawMaterial_Purchase"] = ("BillNo", "CG", "yyyyMMdd", 4, "", "-"), + ["RawMaterial_Warehouse"] = ("Code", "CK", "yyyyMMdd", 3, "", "-"), + ["RawMaterial_Inspect"] = ("Code", "YJ", "yyyyMMdd", 3, "", "-"), ["Process_Trial"] = ("BillNo", "SY", "yyyyMMdd", 3, "", "-"), ["Process_ProcessZhuangkou"] = ("Code", "GYZ", "yyyy", 3, "-", "-"), ["Process_Sheet"] = ("SheetNo", "GYD", "yyyyMMdd", 3, "", "-"), diff --git a/server/src/F9MES.Api/Controllers/WorkBenchController.cs b/server/src/F9MES.Api/Controllers/WorkBenchController.cs index 5b17f51..e00ea32 100644 --- a/server/src/F9MES.Api/Controllers/WorkBenchController.cs +++ b/server/src/F9MES.Api/Controllers/WorkBenchController.cs @@ -78,8 +78,20 @@ public class WorkBenchController : ControllerBase trend.Add(new { date = day.ToString("MM-dd"), weight = sum }); } + // ===== 待办列表(工单 + 工作流审批)===== + var pendingOrders = await _db.Select() + .Where(w => w.Flag == 1 && (w.Status == 0 || w.Status == 1 || w.Status == 2 || w.Status == 4)) + .OrderByDescending(w => w.AddTime) + .Limit(8) + .ToListAsync(w => new { w.Id, w.OrderNo, w.ProcessZhuangkouId, w.Status, w.PlanStartDate }); + // ===== 庄口产量占比(近30天,Top 8)===== - var zkIds = trendRaw.Select(x => x.ProcessZhuangkouId).Distinct().ToList(); + var zkIds = trendRaw.Select(x => x.ProcessZhuangkouId) + .Concat(warnList.Select(x => x.ProcessZhuangkouId)) + .Concat(pendingOrders.Select(x => x.ProcessZhuangkouId)) + .Where(id => id > 0) + .Distinct() + .ToList(); var zkNames = new List(); if (zkIds.Count > 0) { @@ -88,22 +100,16 @@ public class WorkBenchController : ControllerBase .ToListAsync(); } - var nameMap = zkNames.ToDictionary(x => x.Id, x => string.IsNullOrEmpty(x.Name) ? $"庄口{x.Id}" : x.Name); + // 庄口名称优先取 Name,其次庄口编号 Code(明码),避免把内部 Id 展示出来 + var nameMap = zkNames.ToDictionary(x => x.Id, x => string.IsNullOrEmpty(x.Name) ? (string.IsNullOrEmpty(x.Code) ? $"庄口{x.Id}" : x.Code) : x.Name); var zhuangkou = trendRaw .GroupBy(x => x.ProcessZhuangkouId) - .Select(g => new { name = nameMap.GetValueOrDefault(g.Key) ?? $"庄口{g.Key}", value = g.Sum(x => x.OutputWeight) }) + .Select(g => new { name = nameMap.GetValueOrDefault(g.Key) ?? "未知庄口", value = g.Sum(x => x.OutputWeight) }) .OrderByDescending(x => x.value) .Take(8) .Select(x => (object)x) .ToList(); - // ===== 待办列表(工单 + 工作流审批)===== - var pendingOrders = await _db.Select() - .Where(w => w.Flag == 1 && (w.Status == 0 || w.Status == 1 || w.Status == 2 || w.Status == 4)) - .OrderByDescending(w => w.AddTime) - .Limit(8) - .ToListAsync(w => new { w.Id, w.OrderNo, w.ProcessZhuangkouId, w.Status, w.PlanStartDate }); - var todos = new List(); foreach (var o in pendingOrders) { diff --git a/server/src/F9MES.Api/Converters/FlexibleDateTimeConverter.cs b/server/src/F9MES.Api/Converters/FlexibleDateTimeConverter.cs new file mode 100644 index 0000000..c67243f --- /dev/null +++ b/server/src/F9MES.Api/Converters/FlexibleDateTimeConverter.cs @@ -0,0 +1,30 @@ +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace F9MES.Api.Converters; + +/// +/// 宽松 DateTime 转换器: +/// 兼容前端 el-date-picker 输出的 "yyyy-MM-dd HH:mm:ss"(空格分隔)以及标准 ISO 8601(T 分隔)格式。 +/// 序列化保持与默认一致的 ISO 8601 输出,仅放宽反序列化。 +/// +public class FlexibleDateTimeConverter : JsonConverter +{ + public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + var s = reader.GetString(); + if (!string.IsNullOrWhiteSpace(s) + && DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt)) + return dt; + return default; + } + if (reader.TokenType == JsonTokenType.Null) return default; + return reader.GetDateTime(); + } + + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + => writer.WriteStringValue(value); +} diff --git a/server/src/F9MES.Application/Init/InitDataService.cs b/server/src/F9MES.Application/Init/InitDataService.cs index 3e971f5..ece2632 100644 --- a/server/src/F9MES.Application/Init/InitDataService.cs +++ b/server/src/F9MES.Application/Init/InitDataService.cs @@ -149,6 +149,9 @@ public class InitDataService // 原料管理 MenuItem("/3", "原料庄口", "/rawmaterial/zhuangkou", "RawMaterial/zhuangkou"); MenuItem("/3", "原料入库", "/rawmaterial/instock", "RawMaterial/instock"); + MenuItem("/3", "原料检验", "/rawmaterial/inspect", "RawMaterial/inspect"); + MenuItem("/3", "原料仓库", "/rawmaterial/warehouse", "RawMaterial/warehouse"); + MenuItem("/3", "仓储环境", "/rawmaterial/envrecord", "RawMaterial/envrecord"); MenuItem("/3", "翻包管理", "/rawmaterial/repack", "RawMaterial/repack"); MenuItem("/3", "原料出库", "/rawmaterial/outstock", "RawMaterial/outstock"); MenuItem("/3", "原料库存", "/rawmaterial/stock", "RawMaterial/stock"); diff --git a/server/src/F9MES.Domain/BizBoss/BizBoss_Entities.cs b/server/src/F9MES.Domain/BizBoss/BizBoss_Entities.cs index 0863201..4010e16 100644 --- a/server/src/F9MES.Domain/BizBoss/BizBoss_Entities.cs +++ b/server/src/F9MES.Domain/BizBoss/BizBoss_Entities.cs @@ -17,8 +17,8 @@ public class BizBoss_CostAllocate : BaseEntity [Column] public long PeriodId { get; set; } - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } @@ -47,8 +47,8 @@ public class BizBoss_Profit : BaseEntity [Column] public long PeriodId { get; set; } - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } diff --git a/server/src/F9MES.Domain/Fims/Fims_Entities.cs b/server/src/F9MES.Domain/Fims/Fims_Entities.cs index d4e1c34..aea8bbb 100644 --- a/server/src/F9MES.Domain/Fims/Fims_Entities.cs +++ b/server/src/F9MES.Domain/Fims/Fims_Entities.cs @@ -17,8 +17,8 @@ public class Fims_Batch : BaseEntity [Column(DbType = "varchar(50)")] public string BatchNo { get; set; } = ""; - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } @@ -72,8 +72,8 @@ public class Fims_Package : BaseEntity [Column] public long BatchId { get; set; } - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } @@ -127,8 +127,8 @@ public class Fims_InStock : BaseEntity [Column(DbType = "varchar(50)")] public string BillNo { get; set; } = ""; - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } @@ -262,8 +262,8 @@ public class Fims_OutStockItem : BaseEntity [Table(Name = "Fims_Stock")] public class Fims_Stock : BaseEntity { - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } diff --git a/server/src/F9MES.Domain/FlexPay/FlexPay_Entities.cs b/server/src/F9MES.Domain/FlexPay/FlexPay_Entities.cs index 1edda2c..c149eae 100644 --- a/server/src/F9MES.Domain/FlexPay/FlexPay_Entities.cs +++ b/server/src/F9MES.Domain/FlexPay/FlexPay_Entities.cs @@ -127,8 +127,8 @@ public class FlexPay_PieceResult : BaseEntity [Column] public long EmployeeId { get; set; } - /// 关联工艺庄口ID - [Description("关联工艺庄口ID")] + /// 关联工艺庄口 + [Description("关联工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } diff --git a/server/src/F9MES.Domain/Lims/Lims_Entities.cs b/server/src/F9MES.Domain/Lims/Lims_Entities.cs index 5c40e3c..d952720 100644 --- a/server/src/F9MES.Domain/Lims/Lims_Entities.cs +++ b/server/src/F9MES.Domain/Lims/Lims_Entities.cs @@ -23,8 +23,8 @@ public class Lims_SpoolCheck : BaseEntity [Column] public DateTime CheckDate { get; set; } - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } @@ -83,8 +83,8 @@ public class Lims_BlackBoard : BaseEntity [Column] public DateTime CheckDate { get; set; } - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } @@ -138,8 +138,8 @@ public class Lims_Denier : BaseEntity [Column] public DateTime CheckDate { get; set; } - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } @@ -203,8 +203,8 @@ public class Lims_Moisture : BaseEntity [Column] public DateTime CheckDate { get; set; } - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } @@ -258,8 +258,8 @@ public class Lims_Abnormal : BaseEntity [Column] public int CheckType { get; set; } = 0; - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } @@ -303,8 +303,8 @@ public class Lims_GradeResult : BaseEntity [Column(DbType = "varchar(50)")] public string GradeNo { get; set; } = ""; - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } diff --git a/server/src/F9MES.Domain/ProHou/ProHou_Entities.cs b/server/src/F9MES.Domain/ProHou/ProHou_Entities.cs index 7730e00..ae25e3d 100644 --- a/server/src/F9MES.Domain/ProHou/ProHou_Entities.cs +++ b/server/src/F9MES.Domain/ProHou/ProHou_Entities.cs @@ -17,8 +17,8 @@ public class ProHou_Daily : BaseEntity [Column] public DateTime DailyDate { get; set; } - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } @@ -62,8 +62,8 @@ public class ProHou_SpoolLedger : BaseEntity [Column(DbType = "varchar(50)")] public string Code { get; set; } = ""; - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } @@ -147,8 +147,8 @@ public class ProHou_WeighBig : BaseEntity [Column(DbType = "varchar(50)")] public string BillNo { get; set; } = ""; - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } diff --git a/server/src/F9MES.Domain/ProPlan/ProPlan_Entities.cs b/server/src/F9MES.Domain/ProPlan/ProPlan_Entities.cs index 865b4eb..b56a6d9 100644 --- a/server/src/F9MES.Domain/ProPlan/ProPlan_Entities.cs +++ b/server/src/F9MES.Domain/ProPlan/ProPlan_Entities.cs @@ -17,8 +17,8 @@ public class ProPlan_WorkOrder : BaseEntity [Column(DbType = "varchar(50)")] public string OrderNo { get; set; } = ""; - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } @@ -82,8 +82,8 @@ public class ProPlan_Change : BaseEntity [Column(DbType = "varchar(50)")] public string ChangeNo { get; set; } = ""; - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } diff --git a/server/src/F9MES.Domain/ProQian/ProQian_Entities.cs b/server/src/F9MES.Domain/ProQian/ProQian_Entities.cs index 6db3557..4743175 100644 --- a/server/src/F9MES.Domain/ProQian/ProQian_Entities.cs +++ b/server/src/F9MES.Domain/ProQian/ProQian_Entities.cs @@ -122,8 +122,8 @@ public class ProQian_ShiftTime : BaseEntity [Column] public long EmployeeId { get; set; } - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } @@ -167,8 +167,8 @@ public class ProQian_CocoonBoiling : BaseEntity [Column] public DateTime BoilDate { get; set; } - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } @@ -197,8 +197,8 @@ public class ProQian_ThreadRecord : BaseEntity [Column] public DateTime RecordDate { get; set; } - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } diff --git a/server/src/F9MES.Domain/ProXuan/ProXuan_Entities.cs b/server/src/F9MES.Domain/ProXuan/ProXuan_Entities.cs index 56cf143..f146b0b 100644 --- a/server/src/F9MES.Domain/ProXuan/ProXuan_Entities.cs +++ b/server/src/F9MES.Domain/ProXuan/ProXuan_Entities.cs @@ -52,8 +52,8 @@ public class ProXuan_Daily : BaseEntity [Column] public long TeamId { get; set; } - /// 原料庄口ID - [Description("原料庄口ID")] + /// 原料庄口 + [Description("原料庄口")] [Column] public long ZhuangkouId { get; set; } @@ -97,8 +97,8 @@ public class ProXuan_Quality : BaseEntity [Column] public DateTime QualityDate { get; set; } - /// 原料庄口ID - [Description("原料庄口ID")] + /// 原料庄口 + [Description("原料庄口")] [Column] public long ZhuangkouId { get; set; } @@ -127,8 +127,8 @@ public class ProXuan_Quality : BaseEntity [Table(Name = "ProXuan_TempStock")] public class ProXuan_TempStock : BaseEntity { - /// 原料庄口ID - [Description("原料庄口ID")] + /// 原料庄口 + [Description("原料庄口")] [Column] public long ZhuangkouId { get; set; } diff --git a/server/src/F9MES.Domain/Process/Process_Entities.cs b/server/src/F9MES.Domain/Process/Process_Entities.cs index 59c10be..1de576b 100644 --- a/server/src/F9MES.Domain/Process/Process_Entities.cs +++ b/server/src/F9MES.Domain/Process/Process_Entities.cs @@ -23,13 +23,13 @@ public class Process_Trial : BaseEntity [Column] public int TrialType { get; set; } = 0; - /// 原料庄口ID - [Description("原料庄口ID")] + /// 原料庄口 + [Description("原料庄口")] [Column] public long ZhuangkouId { get; set; } - /// 工艺庄口ID(可选) - [Description("工艺庄口ID")] + /// 工艺庄口(可选) + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } @@ -93,8 +93,8 @@ public class Process_ProcessZhuangkou : BaseEntity [Column(DbType = "decimal(6,2)")] public decimal Progress { get; set; } - /// 关联原料庄口ID(可选) - [Description("关联原料庄口ID")] + /// 关联原料庄口(可选) + [Description("关联原料庄口")] [Column] public long ZhuangkouId { get; set; } @@ -183,8 +183,8 @@ public class Process_Sheet : BaseEntity [Column(DbType = "varchar(100)")] public string Name { get; set; } = ""; - /// 工艺庄口ID - [Description("工艺庄口ID")] + /// 工艺庄口 + [Description("工艺庄口")] [Column] public long ProcessZhuangkouId { get; set; } diff --git a/server/src/F9MES.Domain/Qums/Qums_Entities.cs b/server/src/F9MES.Domain/Qums/Qums_Entities.cs index f5d52a8..77fdd5d 100644 --- a/server/src/F9MES.Domain/Qums/Qums_Entities.cs +++ b/server/src/F9MES.Domain/Qums/Qums_Entities.cs @@ -17,8 +17,8 @@ public class Qums_GrainCount : BaseEntity [Column(DbType = "varchar(50)")] public string BillNo { get; set; } = ""; - /// 原料庄口ID - [Description("原料庄口ID")] + /// 原料庄口 + [Description("原料庄口")] [Column] public long ZhuangkouId { get; set; } diff --git a/server/src/F9MES.Domain/RawMaterial/RawMaterial_Entities.cs b/server/src/F9MES.Domain/RawMaterial/RawMaterial_Entities.cs index 2a755fa..cdbe8c0 100644 --- a/server/src/F9MES.Domain/RawMaterial/RawMaterial_Entities.cs +++ b/server/src/F9MES.Domain/RawMaterial/RawMaterial_Entities.cs @@ -11,6 +11,7 @@ namespace F9MES.Domain.RawMaterial; /// 原料庄口(编号规则:YLZ+yyyyMMdd+3位流水,如 YLZ20260815-001) [Table(Name = "RawMaterial_Zhuangkou")] +[Description("原料庄口")] public class RawMaterial_Zhuangkou : BaseEntity { /// 庄口编号(自动生成 YLZ20260815-001) @@ -72,6 +73,51 @@ public class RawMaterial_Zhuangkou : BaseEntity [Description("状态:0=收茧中 1=已入库 2=翻包中 3=已领完")] [Column] public int Status { get; set; } = 0; + + /// 综合等级(如 3A/2A/A,参照生丝等级) + [Description("综合等级")] + [Column(DbType = "varchar(20)")] + public string? Grade { get; set; } + + /// 上车茧率(%,缫丝适制性) + [Description("上车茧率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? LoadingCocoonRate { get; set; } + + /// 上茧率(%,好茧占比) + [Description("上茧率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? GoodCocoonRate { get; set; } + + /// 茧层率(%,茧层占全茧重比例) + [Description("茧层率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? ShellRate { get; set; } + + /// 出丝率(%,缫丝得丝率) + [Description("出丝率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? ReelableRate { get; set; } + + /// 解舒率(%,缫丝解舒性能) + [Description("解舒率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? Reelability { get; set; } + + /// 含水量/回潮率(%,干茧需防潮) + [Description("含水量/回潮率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? MoistureRate { get; set; } + + /// 建议储存仓库ID(RawMaterial_Warehouse) + [Description("建议储存仓库ID")] + [Column] + public long WarehouseId { get; set; } + + /// 建议储存期(月) + [Description("建议储存期(月)")] + [Column] + public int? ShelfLifeMonths { get; set; } } /// 原料入库单(磅码单过磅) @@ -83,8 +129,8 @@ public class RawMaterial_InStock : BaseEntity [Column(DbType = "varchar(50)")] public string BillNo { get; set; } = ""; - /// 原料庄口ID - [Description("原料庄口ID")] + /// 原料庄口 + [Description("原料庄口")] [Column] public long ZhuangkouId { get; set; } @@ -143,8 +189,8 @@ public class RawMaterial_Repack : BaseEntity [Column(DbType = "varchar(50)")] public string PlanNo { get; set; } = ""; - /// 原料庄口ID - [Description("原料庄口ID")] + /// 原料庄口 + [Description("原料庄口")] [Column] public long ZhuangkouId { get; set; } @@ -228,8 +274,8 @@ public class RawMaterial_OutStock : BaseEntity [Column(DbType = "varchar(50)")] public string BillNo { get; set; } = ""; - /// 原料庄口ID - [Description("原料庄口ID")] + /// 原料庄口 + [Description("原料庄口")] [Column] public long ZhuangkouId { get; set; } @@ -268,8 +314,8 @@ public class RawMaterial_OutStock : BaseEntity [Table(Name = "RawMaterial_Stock")] public class RawMaterial_Stock : BaseEntity { - /// 原料庄口ID - [Description("原料庄口ID")] + /// 原料庄口 + [Description("原料庄口")] [Column] public long ZhuangkouId { get; set; } @@ -378,3 +424,226 @@ public class RawMaterial_PurchaseItem : BaseEntity [Column(DbType = "decimal(14,2)")] public decimal InQuantity { get; set; } } + +/// 原料仓库(干茧仓/冷冻鲜茧仓(冷库)/下脚料仓) +[Table(Name = "RawMaterial_Warehouse")] +[Description("原料仓库")] +public class RawMaterial_Warehouse : BaseEntity +{ + /// 仓库编号 + [Description("仓库编号")] + [Column(DbType = "varchar(50)")] + public string Code { get; set; } = ""; + + /// 仓库名称 + [Description("仓库名称")] + [Column(DbType = "varchar(100)")] + public string Name { get; set; } = ""; + + /// 仓库类型:0=干茧仓 1=冷冻鲜茧仓(冷库) 2=下脚料仓 + [Description("仓库类型:0=干茧仓 1=冷冻鲜茧仓 2=下脚料仓")] + [Column] + public int WarehouseType { get; set; } = 0; + + /// 库区/仓位说明 + [Description("库区/仓位")] + [Column(DbType = "varchar(200)")] + public string? Area { get; set; } + + /// 温度下限(℃,0=不限) + [Description("温度下限(℃)")] + [Column(DbType = "decimal(8,2)")] + public decimal? TempMin { get; set; } + + /// 温度上限(℃,0=不限) + [Description("温度上限(℃)")] + [Column(DbType = "decimal(8,2)")] + public decimal? TempMax { get; set; } + + /// 湿度下限(%RH) + [Description("湿度下限(%RH)")] + [Column(DbType = "decimal(8,2)")] + public decimal? HumiMin { get; set; } + + /// 湿度上限(%RH,干茧防霉需控制在 60-70) + [Description("湿度上限(%RH)")] + [Column(DbType = "decimal(8,2)")] + public decimal? HumiMax { get; set; } + + /// 库容量(吨) + [Description("库容量(吨)")] + [Column(DbType = "decimal(12,2)")] + public decimal? Capacity { get; set; } + + /// 当前存量(吨) + [Description("当前存量(吨)")] + [Column(DbType = "decimal(12,2)")] + public decimal? CurrentLoad { get; set; } + + /// 负责人 + [Description("负责人")] + [Column(DbType = "varchar(50)")] + public string? Manager { get; set; } + + /// 状态:0=停用 1=启用 + [Description("状态:0=停用 1=启用")] + [Column] + public int Status { get; set; } = 1; +} + +/// 仓储环境记录(干茧仓防霉湿度、冷库鲜茧温度巡检) +[Table(Name = "RawMaterial_EnvRecord")] +[Description("仓储环境记录")] +public class RawMaterial_EnvRecord : BaseEntity +{ + /// 仓库ID(RawMaterial_Warehouse) + [Description("仓库ID")] + [Column] + public long WarehouseId { get; set; } + + /// 记录时间 + [Description("记录时间")] + [Column] + public DateTime RecordTime { get; set; } + + /// 实测温度(℃) + [Description("实测温度(℃)")] + [Column(DbType = "decimal(8,2)")] + public decimal? Temperature { get; set; } + + /// 实测湿度(%RH) + [Description("实测湿度(%RH)")] + [Column(DbType = "decimal(8,2)")] + public decimal? Humidity { get; set; } + + /// 达标状态:0=达标 1=温度超标 2=湿度超标 3=温湿度均超标 + [Description("达标状态:0=达标 1=温度超标 2=湿度超标 3=均超标")] + [Column] + public int AlarmStatus { get; set; } = 0; + + /// 处理措施 + [Description("处理措施")] + [Column(DbType = "varchar(200)")] + public string? Measure { get; set; } + + /// 记录人 + [Description("记录人")] + [Column(DbType = "varchar(50)")] + public string? Recorder { get; set; } +} + +/// 原料检验单(蚕茧入库质检与定级) +[Table(Name = "RawMaterial_Inspect")] +[Description("原料检验单")] +public class RawMaterial_Inspect : BaseEntity +{ + /// 检验单号(YJ+日期+流水) + [Description("检验单号(YJ+日期+流水)")] + [Column(DbType = "varchar(50)")] + public string Code { get; set; } = ""; + + /// 原料庄口ID(RawMaterial_Zhuangkou) + [Description("原料庄口ID")] + [Column] + public long ZhuangkouId { get; set; } + + /// 关联入库单ID(RawMaterial_InStock,可空) + [Description("关联入库单ID")] + [Column] + public long InStockId { get; set; } + + /// 检验日期 + [Description("检验日期")] + [Column] + public DateTime CheckDate { get; set; } + + /// 蚕茧类型:0=干茧 1=冷冻鲜茧 + [Description("蚕茧类型:0=干茧 1=冷冻鲜茧")] + [Column] + public int CocoonType { get; set; } = 0; + + /// 取样重量(kg) + [Description("取样重量(kg)")] + [Column(DbType = "decimal(14,2)")] + public decimal? SampleWeight { get; set; } + + /// 上车茧率(%) + [Description("上车茧率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? LoadingCocoonRate { get; set; } + + /// 上茧率(%) + [Description("上茧率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? GoodCocoonRate { get; set; } + + /// 下脚茧率(%) + [Description("下脚茧率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? WasteCocoonRate { get; set; } + + /// 茧层率(%) + [Description("茧层率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? ShellRate { get; set; } + + /// 出丝率(%) + [Description("出丝率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? ReelableRate { get; set; } + + /// 解舒率(%) + [Description("解舒率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? Reelability { get; set; } + + /// 含水量/回潮率(%) + [Description("含水量/回潮率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? MoistureRate { get; set; } + + /// 霉茧率(%) + [Description("霉茧率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? MoldyRate { get; set; } + + /// 内印茧率(%) + [Description("内印茧率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? InnerPrintRate { get; set; } + + /// 黄斑茧率(%) + [Description("黄斑茧率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? YellowSpotRate { get; set; } + + /// 柴印茧率(%) + [Description("柴印茧率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? ChaiPrintRate { get; set; } + + /// 双宫茧率(%) + [Description("双宫茧率(%)")] + [Column(DbType = "decimal(8,2)")] + public decimal? DoubleCocoonRate { get; set; } + + /// 综合等级(参照生丝等级 6A-2A/A) + [Description("综合等级")] + [Column(DbType = "varchar(20)")] + public string? Grade { get; set; } + + /// 检验结论:0=合格 1=降级 2=拒收 + [Description("检验结论:0=合格 1=降级 2=拒收")] + [Column] + public int Conclusion { get; set; } = 0; + + /// 检验员 + [Description("检验员")] + [Column(DbType = "varchar(50)")] + public string? Inspector { get; set; } + + /// 备注 + [Description("备注")] + [Column(DbType = "varchar(500)")] + public string? Remark { get; set; } +} diff --git a/server/src/F9MES.Domain/WmsFu/WmsFu_Entities.cs b/server/src/F9MES.Domain/WmsFu/WmsFu_Entities.cs index 9554b07..a5e90a7 100644 --- a/server/src/F9MES.Domain/WmsFu/WmsFu_Entities.cs +++ b/server/src/F9MES.Domain/WmsFu/WmsFu_Entities.cs @@ -18,8 +18,8 @@ public class WmsFu_Stock : BaseEntity [Column] public long MaterialId { get; set; } - /// 来源庄口ID(工艺庄口,可为0汇总) - [Description("来源庄口ID")] + /// 来源庄口(工艺庄口,可为0汇总) + [Description("来源庄口")] [Column] public long ProcessZhuangkouId { get; set; } @@ -53,8 +53,8 @@ public class WmsFu_InStock : BaseEntity [Column] public long MaterialId { get; set; } - /// 工艺庄口ID(来源) - [Description("工艺庄口ID(来源)")] + /// 工艺庄口(来源) + [Description("工艺庄口(来源)")] [Column] public long ProcessZhuangkouId { get; set; } diff --git a/web/src/components/BillPage.vue b/web/src/components/BillPage.vue new file mode 100644 index 0000000..3fc070a --- /dev/null +++ b/web/src/components/BillPage.vue @@ -0,0 +1,392 @@ + + + + + diff --git a/web/src/components/CrudPage.vue b/web/src/components/CrudPage.vue index 67e6965..a9b292f 100644 --- a/web/src/components/CrudPage.vue +++ b/web/src/components/CrudPage.vue @@ -47,7 +47,7 @@ 重置
- + 新增 @@ -124,7 +124,7 @@ size="small" @click="handleDetail(row)" >详情 - 编辑 + 编辑 删除 - - + + @@ -296,6 +297,8 @@ const tableName = computed(() => props.table || route.meta.tableName || '') const pageTitle = computed(() => props.title || route.meta.title || '') /** 详情页路由(:id 占位,由菜单映射配置) */ const detailRoute = computed(() => route.meta.detailRoute || '') +/** 单据页路由(:id 占位,由菜单映射配置;配置后新增/编辑跳转 ERP 风格单据界面) */ +const billRoute = computed(() => route.meta.billRoute || '') /** 打印模板配置(由菜单映射配置,含 templateName/title) */ const printTemplate = computed(() => route.meta.printTemplate || null) /** 工作流配置(由菜单映射配置,含 bizType) */ @@ -357,6 +360,22 @@ const dialogTitle = computed(() => isEdit.value ? `编辑${pageTitle.value}` : `新增${pageTitle.value}` ) +/** 弹窗宽度:按字段数量自适应,字段多时横向扩展 */ +const dialogWidth = computed(() => { + const n = formFields.value.length + if (n <= 6) return '720px' + if (n <= 12) return '1000px' + return '1240px' +}) + +/** 表单列宽:字段少用整行,字段多用多列,避免布局过于紧凑 */ +const formSpan = computed(() => { + const n = formFields.value.length + if (n <= 6) return 24 + if (n <= 12) return 12 + return 8 +}) + // ================= 控件类型 ================= const isTextType = (f) => f.propType === 'String' const isNumberType = (f) => @@ -485,6 +504,24 @@ function handleReset() { } // ================= 新增/编辑 ================= +/** 新增:配置了单据页则跳转单据页(无 id),否则打开弹窗 */ +function handleAdd() { + if (billRoute.value) { + router.push(billRoute.value) + return + } + openDialog(null) +} + +/** 编辑:配置了单据页则跳转单据页(带 id),否则打开弹窗 */ +function handleEdit(row) { + if (billRoute.value) { + router.push(`${billRoute.value}/${getRowId(row)}`) + return + } + openDialog(row) +} + async function openDialog(row) { isEdit.value = !!row form.value = {} @@ -673,3 +710,33 @@ onMounted(async () => { margin-top: 14px; } + + + diff --git a/web/src/config/bill-configs.js b/web/src/config/bill-configs.js new file mode 100644 index 0000000..34d884a --- /dev/null +++ b/web/src/config/bill-configs.js @@ -0,0 +1,384 @@ +/** + * 单据页配置:component 对应表名 → 单据页布局配置 + * 配置后由 BILL_MAP(table-map.js)把列表页"新增/编辑"接入全屏单据页 + * 字段名统一用 camelCase(与后端 meta/data 接口返回一致) + * span:24 整行 / 16 占 2/3 行 / 8 每行 3 列(默认) + */ +export const BILL_CONFIGS = { + // ================= Process 工艺管理 ================= + Process_Trial: { + title: '样茧试缫单', + codeField: 'billNo', + groups: [ + { title: '试缫信息', fields: ['billNo', 'trialType', 'zhuangkouId', 'processZhuangkouId', 'trialDate', 'trialMan'] }, + { title: '试缫数据', fields: [{ name: 'dataJson', span: 16 }] } + ] + }, + Process_ProcessZhuangkou: { + title: '工艺庄口', + codeField: 'code', + statusField: 'status', + groups: [ + { title: '基本信息', fields: ['code', 'name', 'shortName', 'displayName', 'isLocal', 'tags'] }, + { title: '生产信息', fields: ['zhuangkouId', 'progress', 'status', 'startDate', 'planEndDate', 'endDate'] } + ] + }, + Process_Sheet: { + title: '工艺单', + codeField: 'sheetNo', + statusField: 'status', + groups: [ + { title: '工艺单信息', fields: ['sheetNo', 'name', 'processZhuangkouId', 'version', 'sourceSheetId', 'status'] }, + { title: '发布信息', fields: ['publisher', 'publishTime'] } + ] + }, + + // ================= ProPlan 生产计划 ================= + ProPlan_WorkOrder: { + title: '生产工单', + codeField: 'orderNo', + statusField: 'status', + groups: [ + { title: '工单信息', fields: ['orderNo', 'processZhuangkouId', 'sheetId', 'targetProcess', 'priority', 'status'] }, + { title: '计划与执行', fields: ['planInput', 'planOutput', 'actualInput', 'actualOutput', 'planStartDate', 'planEndDate'] } + ] + }, + ProPlan_Change: { + title: '计划变更单', + codeField: 'changeNo', + groups: [ + { title: '变更信息', fields: ['changeNo', 'processZhuangkouId', 'changeType', 'fromOrder', 'toOrder', 'operatorId', 'changeTime', { name: 'reason', span: 16 }] } + ] + }, + + // ================= ProXuan 选茧 ================= + ProXuan_Daily: { + title: '选茧日报', + groups: [ + { title: '日报信息', fields: ['dailyDate', 'teamId', 'zhuangkouId', 'workerCount', 'workHours'] }, + { title: '产量数据', fields: ['inputWeight', 'goodWeight', 'wasteWeight'] }, + { title: '备注', fields: [{ name: 'remark', span: 16 }] } + ] + }, + ProXuan_Quality: { + title: '选茧质量记录', + groups: [ + { title: '质量信息', fields: ['qualityDate', 'zhuangkouId', 'goodRate', 'wasteRate', 'qualityLevel'] }, + { title: '问题描述', fields: [{ name: 'issueDesc', span: 16 }] } + ] + }, + + // ================= ProQian 前纺 ================= + ProQian_Schedule: { + title: '前纺排班', + groups: [ + { title: '排班信息', fields: ['scheduleDate', 'shift', 'workshopId', 'machineId', 'employeeId', 'postType', 'xuCount'] } + ] + }, + ProQian_ShiftTime: { + title: '看台时长记录', + groups: [ + { title: '时长记录', fields: ['employeeId', 'processZhuangkouId', 'workDate', 'shift', 'machineId', 'hours', 'opType', 'sourceId'] } + ] + }, + ProQian_ThreadRecord: { + title: '落丝记录', + groups: [ + { title: '落丝信息', fields: ['recordDate', 'processZhuangkouId', 'machineId', 'shift', 'specId', 'employeeId', 'threadCount', 'weight'] } + ] + }, + + // ================= ProHou 后纺 ================= + ProHou_Daily: { + title: '后纺日报', + groups: [ + { title: '日报信息', fields: ['dailyDate', 'processZhuangkouId', 'teamId', 'workerCount'] }, + { title: '产量数据', fields: ['inputWeight', 'outputWeight', 'outputCount'] }, + { title: '备注', fields: [{ name: 'remark', span: 16 }] } + ] + }, + ProHou_SpoolLedger: { + title: '丝片台账', + codeField: 'code', + statusField: 'status', + groups: [ + { title: '丝片信息', fields: ['code', 'processZhuangkouId', 'specId', 'grade', 'pieceNo'] }, + { title: '生产信息', fields: ['produceDate', 'teamId', 'weight', 'status'] } + ] + }, + ProHou_SpoolCheck: { + title: '丝片检查', + groups: [ + { title: '检查信息', fields: ['spoolId', 'checkDate', 'checker', 'checkResult'] }, + { title: '检查明细', fields: [{ name: 'checkItemsJson', span: 16 }] }, + { title: '备注', fields: [{ name: 'remark', span: 16 }] } + ] + }, + ProHou_WeighBig: { + title: '秤大丝记录', + codeField: 'billNo', + groups: [ + { title: '称重信息', fields: ['billNo', 'processZhuangkouId', 'specId', 'grade', 'weighDate', 'operator'] }, + { title: '称重数据', fields: ['totalWeight', 'packageCount'] } + ] + }, + + // ================= Lims 检验 ================= + Lims_SpoolCheck: { + title: '丝片检验单', + codeField: 'checkNo', + statusField: 'status', + groups: [ + { title: '检验单信息', fields: ['checkNo', 'checkDate', 'processZhuangkouId', 'teamId', 'shift', 'checker'] }, + { title: '检验数据', fields: ['checkCount', 'checkWeight', 'passCount', 'passRate', 'status'] } + ] + }, + Lims_BlackBoard: { + title: '黑板检验单', + codeField: 'checkNo', + groups: [ + { title: '检验单信息', fields: ['checkNo', 'checkDate', 'processZhuangkouId', 'specId', 'checker'] }, + { title: '检验指标', fields: ['uniformity', 'cleanliness', 'neatness', 'grade'] }, + { title: '原始数据', fields: [{ name: 'dataJson', span: 16 }] } + ] + }, + Lims_Denier: { + title: '纤度检验单', + codeField: 'checkNo', + groups: [ + { title: '检验单信息', fields: ['checkNo', 'checkDate', 'processZhuangkouId', 'specId', 'checker'] }, + { title: '纤度指标', fields: ['centerDenier', 'avgDenier', 'deviation', 'deviationRate', 'wildCount', 'breakingStrength', 'breakingElongation'] } + ] + }, + Lims_Moisture: { + title: '公量检验单', + codeField: 'checkNo', + groups: [ + { title: '检验单信息', fields: ['checkNo', 'checkDate', 'processZhuangkouId', 'specId', 'checker'] }, + { title: '公量指标', fields: ['moistureRegain', 'standardRegain', 'grossWeight', 'netWeight', 'conditionWeight'] } + ] + }, + Lims_Abnormal: { + title: '检验异常记录', + codeField: 'abnormalNo', + statusField: 'status', + groups: [ + { title: '异常信息', fields: ['abnormalNo', 'checkType', 'processZhuangkouId', 'abnormalType', 'foundDate'] }, + { title: '异常描述', fields: [{ name: 'description', span: 16 }] }, + { title: '处理信息', fields: ['status', 'handler', { name: 'handleResult', span: 16 }] } + ] + }, + Lims_GradeResult: { + title: '厂检定级单', + codeField: 'gradeNo', + statusField: 'status', + groups: [ + { title: '判定单信息', fields: ['gradeNo', 'processZhuangkouId', 'specId', 'standardId', 'grade', 'grader', 'gradeDate', 'status'] }, + { title: '判定依据', fields: [{ name: 'basisDesc', span: 16 }] }, + { title: '原始数据', fields: [{ name: 'dataJson', span: 16 }] } + ] + }, + + // ================= Qums 质量 ================= + Qums_GrainCount: { + title: '生丝粒数测定', + codeField: 'billNo', + groups: [ + { title: '测定信息', fields: ['billNo', 'zhuangkouId', 'testDate', 'tester'] }, + { title: '测定数据', fields: ['sampleWeight', 'grainCount', 'countPerKg'] } + ] + }, + Qums_CheckTask: { + title: '质检任务', + codeField: 'taskNo', + statusField: 'status', + groups: [ + { title: '任务信息', fields: ['taskNo', 'name', 'checkType', 'scope', 'planDate', 'manager', 'status'] } + ] + }, + + // ================= Fims 成品 ================= + Fims_Batch: { + title: '成品批次', + codeField: 'batchNo', + statusField: 'status', + groups: [ + { title: '批次信息', fields: ['batchNo', 'processZhuangkouId', 'specId', 'grade', 'createDate', 'status'] }, + { title: '重量数据', fields: ['totalWeight', 'packageCount'] }, + { title: '来源信息', fields: ['inStockId'] } + ] + }, + Fims_Package: { + title: '成品包件', + codeField: 'packageNo', + statusField: 'status', + groups: [ + { title: '包件信息', fields: ['packageNo', 'batchId', 'processZhuangkouId', 'specId', 'grade', 'status'] }, + { title: '重量数据', fields: ['grossWeight', 'tareWeight', 'netWeight'] }, + { title: '出入库信息', fields: ['inDate', 'outDate'] } + ] + }, + Fims_InStock: { + title: '成品入库单', + codeField: 'billNo', + statusField: 'status', + groups: [ + { title: '入库单信息', fields: ['billNo', 'processZhuangkouId', 'specId', 'grade', 'gradeResultId', 'inDate', 'location', 'status'] }, + { title: '入库数据', fields: ['netWeight', 'packageCount'] } + ] + }, + Fims_OutStock: { + title: '成品出库单', + codeField: 'billNo', + statusField: 'status', + groups: [ + { title: '出库单信息', fields: ['billNo', 'customerId', 'contractId', 'outType', 'outDate', 'carrier', 'status'] }, + { title: '出库数据', fields: ['netWeight', 'packageCount'] } + ] + }, + + // ================= Sams 销售 ================= + Sams_Customer: { + title: '客户档案', + codeField: 'code', + groups: [ + { title: '基本信息', fields: ['code', 'name', 'shortName', 'contact', 'phone', { name: 'address', span: 16 }] }, + { title: '财务信息', fields: ['creditLevel', 'bankName', 'bankAccount', 'taxNo'] }, + { title: '状态', fields: ['isEnable'] } + ] + }, + Sams_Contract: { + title: '销售合同', + codeField: 'contractNo', + statusField: 'status', + groups: [ + { title: '合同信息', fields: ['contractNo', 'name', 'customerId', 'signDate', 'status'] }, + { title: '金额信息', fields: ['totalAmount', 'executedAmount', 'paidAmount'] }, + { title: '备注', fields: [{ name: 'memo', span: 16 }] } + ] + }, + Sams_DispatchPlan: { + title: '发货计划', + codeField: 'planNo', + statusField: 'status', + groups: [ + { title: '计划信息', fields: ['planNo', 'contractItemId', 'planDate', 'planQuantity', 'shippedQuantity', 'carrier', 'status'] } + ] + }, + Sams_Payment: { + title: '回款登记', + codeField: 'paymentNo', + groups: [ + { title: '回款信息', fields: ['paymentNo', 'contractId', 'customerId', 'payDate', 'amount', 'payType', 'outBillNo', 'operator'] } + ] + }, + Sams_Invoice: { + title: '开票登记', + codeField: 'invoiceNo', + statusField: 'status', + groups: [ + { title: '发票信息', fields: ['invoiceNo', 'contractId', 'customerId', 'invoiceDate', 'amount', 'taxRate', 'invoiceType', 'status'] } + ] + }, + + // ================= HRS 人事 ================= + HRS_Employee: { + title: '职工档案', + codeField: 'code', + statusField: 'status', + groups: [ + { title: '基本信息', fields: ['code', 'name', 'gender', 'idCard', 'phone', 'orgId', 'workTypeId', 'position', 'status'] }, + { title: '任职信息', fields: ['hireDate', 'leaveDate'] }, + { title: '银行信息', fields: ['bankCardNo', 'bankName'] } + ] + }, + HRS_EmployeeChange: { + title: '职工异动记录', + groups: [ + { title: '异动信息', fields: ['employeeId', 'changeType', 'changeDate', 'fromOrgId', 'toOrgId', { name: 'reason', span: 16 }] } + ] + }, + HRS_Attendance: { + title: '出勤管理', + groups: [ + { title: '出勤信息', fields: ['employeeId', 'attDate', 'attType', 'hours', 'inputType', 'periodId'] } + ] + }, + HRS_CanteenRecord: { + title: '食堂消费记录', + groups: [ + { title: '消费信息', fields: ['employeeId', 'eatDate', 'mealType', 'amount', 'subsidy', 'orgId'] } + ] + }, + + // ================= FlexPay 计件薪资 ================= + FlexPay_Payroll: { + title: '薪资发放批次', + codeField: 'batchNo', + statusField: 'status', + groups: [ + { title: '发放信息', fields: ['batchNo', 'periodId', 'totalAmount', 'empCount', 'status', 'payTime'] }, + { title: '银行文件', fields: [{ name: 'bankFilePath', span: 16 }] } + ] + }, + + // ================= Enms 能源 ================= + Enms_Reading: { + title: '抄表记录', + groups: [ + { title: '抄表信息', fields: ['meterId', 'readDate', 'reading', 'lastReading', 'usage', 'reader', 'readType'] } + ] + }, + + // ================= WmsWu 五金仓储 ================= + WmsWu_InStock: { + title: '五金入库单', + codeField: 'billNo', + statusField: 'status', + groups: [ + { title: '入库单信息', fields: ['billNo', 'supplierId', 'purchaseId', 'inDate', 'totalAmount', 'status'] } + ] + }, + WmsWu_OutStock: { + title: '五金出库单', + codeField: 'billNo', + statusField: 'status', + groups: [ + { title: '出库单信息', fields: ['billNo', 'outOrgId', 'outDate', 'receiver', 'status'] } + ] + }, + WmsWu_PurchasePlan: { + title: '五金采购计划', + codeField: 'planNo', + statusField: 'status', + groups: [ + { title: '计划信息', fields: ['planNo', 'name', 'planAmount', 'status'] } + ] + }, + WmsWu_Apply: { + title: '领料申请', + codeField: 'applyNo', + statusField: 'status', + groups: [ + { title: '申请信息', fields: ['applyNo', 'applyOrgId', 'applicant', 'applyDate', 'status'] } + ] + }, + + // ================= WmsFu 副产品仓储 ================= + WmsFu_InStock: { + title: '副产品入库单', + codeField: 'billNo', + statusField: 'status', + groups: [ + { title: '入库单信息', fields: ['billNo', 'materialId', 'processZhuangkouId', 'inDate', 'netWeight', 'sourceProcess', 'status'] } + ] + }, + WmsFu_OutStock: { + title: '副产品出库单', + codeField: 'billNo', + statusField: 'status', + groups: [ + { title: '出库单信息', fields: ['billNo', 'materialId', 'customerId', 'outDate', 'netWeight', 'outType', 'status'] } + ] + } +} diff --git a/web/src/config/table-map.js b/web/src/config/table-map.js index 4694246..4727554 100644 --- a/web/src/config/table-map.js +++ b/web/src/config/table-map.js @@ -14,6 +14,9 @@ const TABLE_MAP = { 'RawMaterial/zhuangkou': 'RawMaterial_Zhuangkou', 'RawMaterial/instock': 'RawMaterial_InStock', + 'RawMaterial/inspect': 'RawMaterial_Inspect', + 'RawMaterial/warehouse': 'RawMaterial_Warehouse', + 'RawMaterial/envrecord': 'RawMaterial_EnvRecord', 'RawMaterial/repack': 'RawMaterial_Repack', 'RawMaterial/outstock': 'RawMaterial_OutStock', 'RawMaterial/stock': 'RawMaterial_Stock', @@ -140,6 +143,95 @@ export function resolveDetail(component) { return component ? DETAIL_MAP[component] || null : null } +/** + * 单据页映射:component → 单据页路由(:id 占位替换) + * 配置后,通用 CRUD 列表的"新增/编辑"会打开 ERP 风格单据界面 + * (单据页需自行注册静态路由并处理无 id 为新增、带 id 为编辑) + */ +export const BILL_MAP = { + // 原料管理(专用单据页) + 'RawMaterial/outstock': '/rawmaterial/outstock-bill', + 'RawMaterial/zhuangkou': '/rawmaterial/zhuangkou-bill', + 'RawMaterial/instock': '/rawmaterial/instock-bill', + 'RawMaterial/inspect': '/rawmaterial/inspect-bill', + + // 工艺管理 + 'Process/trial': '/bill/Process_Trial', + 'Process/zhuangkou': '/bill/Process_ProcessZhuangkou', + 'Process/sheet': '/bill/Process_Sheet', + + // 生产计划 + 'ProPlan/workorder': '/bill/ProPlan_WorkOrder', + 'ProPlan/change': '/bill/ProPlan_Change', + + // 选茧 + 'ProXuan/daily': '/bill/ProXuan_Daily', + 'ProXuan/quality': '/bill/ProXuan_Quality', + + // 前纺 + 'ProQian/schedule': '/bill/ProQian_Schedule', + 'ProQian/shifttime': '/bill/ProQian_ShiftTime', + 'ProQian/thread': '/bill/ProQian_ThreadRecord', + + // 后纺 + 'ProHou/daily': '/bill/ProHou_Daily', + 'ProHou/spool': '/bill/ProHou_SpoolLedger', + 'ProHou/check': '/bill/ProHou_SpoolCheck', + 'ProHou/weigh': '/bill/ProHou_WeighBig', + + // 检验 + 'Lims/spoolcheck': '/bill/Lims_SpoolCheck', + 'Lims/blackboard': '/bill/Lims_BlackBoard', + 'Lims/denier': '/bill/Lims_Denier', + 'Lims/moisture': '/bill/Lims_Moisture', + 'Lims/abnormal': '/bill/Lims_Abnormal', + 'Lims/grade': '/bill/Lims_GradeResult', + + // 质量 + 'Qums/grain': '/bill/Qums_GrainCount', + 'Qums/task': '/bill/Qums_CheckTask', + + // 成品 + 'Fims/batch': '/bill/Fims_Batch', + 'Fims/package': '/bill/Fims_Package', + 'Fims/instock': '/bill/Fims_InStock', + 'Fims/outstock': '/bill/Fims_OutStock', + + // 销售 + 'Sams/customer': '/bill/Sams_Customer', + 'Sams/contract': '/bill/Sams_Contract', + 'Sams/dispatch': '/bill/Sams_DispatchPlan', + 'Sams/payment': '/bill/Sams_Payment', + 'Sams/invoice': '/bill/Sams_Invoice', + + // 人事 + 'HRS/employee': '/bill/HRS_Employee', + 'HRS/change': '/bill/HRS_EmployeeChange', + 'HRS/attendance': '/bill/HRS_Attendance', + 'HRS/canteen': '/bill/HRS_CanteenRecord', + + // 计件薪资 + 'FlexPay/payroll': '/bill/FlexPay_Payroll', + + // 能源 + 'Enms/reading': '/bill/Enms_Reading', + + // 五金仓储 + 'WmsWu/instock': '/bill/WmsWu_InStock', + 'WmsWu/outstock': '/bill/WmsWu_OutStock', + 'WmsWu/plan': '/bill/WmsWu_PurchasePlan', + 'WmsWu/apply': '/bill/WmsWu_Apply', + + // 副产品仓储 + 'WmsFu/instock': '/bill/WmsFu_InStock', + 'WmsFu/outstock': '/bill/WmsFu_OutStock' +} + +/** 根据 component 解析单据页路由(无则返回 null) */ +export function resolveBill(component) { + return component ? BILL_MAP[component] || null : null +} + /** * 打印模板映射:component → 模板名称(对应 RepCenter_ReportDesign.Name) * 配置后,通用 CRUD 列表操作列会显示"打印"按钮 diff --git a/web/src/router/index.js b/web/src/router/index.js index db5f602..0020027 100644 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -1,7 +1,7 @@ import { createRouter, createWebHistory } from 'vue-router' import { useUserStore } from '@/stores/user' import { useTabsStore } from '@/stores/tabs' -import { resolveTable, resolveDetail, resolvePrint, resolveWorkflow, SPECIAL_PAGES } from '@/config/table-map' +import { resolveTable, resolveDetail, resolvePrint, resolveWorkflow, resolveBill, SPECIAL_PAGES } from '@/config/table-map' // 静态路由(与权限无关) export const constantRoutes = [ @@ -35,6 +35,36 @@ export const constantRoutes = [ component: () => import('@/views/process/zhuangkou-detail.vue'), meta: { title: '工艺庄口详情', hidden: true } }, + { + path: '/rawmaterial/outstock-bill/:id?', + name: 'RawMaterialOutstockBill', + component: () => import('@/views/rawmaterial/outstock-bill.vue'), + meta: { title: '原料出库单', hidden: true } + }, + { + path: '/rawmaterial/zhuangkou-bill/:id?', + name: 'RawMaterialZhuangkouBill', + component: () => import('@/views/rawmaterial/zhuangkou-bill.vue'), + meta: { title: '原料庄口', hidden: true } + }, + { + path: '/rawmaterial/instock-bill/:id?', + name: 'RawMaterialInStockBill', + component: () => import('@/views/rawmaterial/instock-bill.vue'), + meta: { title: '原料磅码单', hidden: true } + }, + { + path: '/rawmaterial/inspect-bill/:id?', + name: 'RawMaterialInspectBill', + component: () => import('@/views/rawmaterial/inspect-bill.vue'), + meta: { title: '原料检验单', hidden: true } + }, + { + path: '/bill/:table/:id?', + name: 'BillPage', + component: () => import('@/components/BillPage.vue'), + meta: { title: '业务单据', hidden: true } + }, { path: '/workflow/todo', name: 'WorkFlowTodo', @@ -112,6 +142,7 @@ export function registerDynamicRoutes(menus) { tableName: table, component: p.component, detailRoute: resolveDetail(p.component), + billRoute: resolveBill(p.component), printTemplate: resolvePrint(p.component), workflow: resolveWorkflow(p.component) } diff --git a/web/src/views/process/zhuangkou-detail.vue b/web/src/views/process/zhuangkou-detail.vue index 48bc257..c8453dd 100644 --- a/web/src/views/process/zhuangkou-detail.vue +++ b/web/src/views/process/zhuangkou-detail.vue @@ -74,8 +74,9 @@ const refCache = reactive({}) // ================= 庄口档案 ================= const zkLabel = computed(() => { + if (!info.value.zhuangkouId) return '—' const cache = refCache['RawMaterial_Zhuangkou'] - return (cache?.map?.[info.value.zhuangkouId] ?? info.value.zhuangkouId) || '—' + return cache?.map?.[info.value.zhuangkouId] || '—' }) // ================= 枚举解析 ================= @@ -228,7 +229,7 @@ function fmtCell(sec, col, row) { } if (col.refTable) { const cache = refCache[col.refTable] - return cache?.map?.[v] ?? v + return cache?.map?.[v] || '—' } if (/Date/i.test(col.prop)) return formatTime(v) if (typeof v === 'number') return Number.isInteger(v) ? v : v.toFixed(2) diff --git a/web/src/views/rawmaterial/inspect-bill.vue b/web/src/views/rawmaterial/inspect-bill.vue new file mode 100644 index 0000000..d17f012 --- /dev/null +++ b/web/src/views/rawmaterial/inspect-bill.vue @@ -0,0 +1,503 @@ + + + + + diff --git a/web/src/views/rawmaterial/instock-bill.vue b/web/src/views/rawmaterial/instock-bill.vue new file mode 100644 index 0000000..6b7b34f --- /dev/null +++ b/web/src/views/rawmaterial/instock-bill.vue @@ -0,0 +1,410 @@ + + + + + diff --git a/web/src/views/rawmaterial/outstock-bill.vue b/web/src/views/rawmaterial/outstock-bill.vue new file mode 100644 index 0000000..d997a26 --- /dev/null +++ b/web/src/views/rawmaterial/outstock-bill.vue @@ -0,0 +1,491 @@ + + + + + diff --git a/web/src/views/rawmaterial/zhuangkou-bill.vue b/web/src/views/rawmaterial/zhuangkou-bill.vue new file mode 100644 index 0000000..f47883c --- /dev/null +++ b/web/src/views/rawmaterial/zhuangkou-bill.vue @@ -0,0 +1,461 @@ + + + + + diff --git a/项目说明 b/项目说明 index 3f1cc57..1d0b178 100644 --- a/项目说明 +++ b/项目说明 @@ -11,7 +11,7 @@ F9智慧缫丝设计文档,这是一份系统开发设计文档: 9.开发步骤:先完成PC端,再进行电视大屏端、小程序端,再进行联调内测 # 二、缫丝厂的业务流程说明 缫丝厂基本流程:干茧(也有可能是鲜茧)原料出库后,依次经过选茧工序筛选出上车茧,送入前缫车间开展煮茧作业,经煮茧膨润丝胶后进入自动缫丝机完成索绪、理绪、缫丝落丝,产出小䈅丝片;小䈅丝片流转至后缫工段,完成给湿、复摇返丝、编丝整理,同步开展黑板、纤度、公量等理化检验,按批次完成生丝组批定级,检验合格后入库形成成品生丝,各工序同步产出各类下脚副产品单独归集入库。 -庄口指原料蚕茧的批次化管理,原料庄口指的是A地某年某季的原料蚕茧,工艺庄口指根据生产工艺组合的生产用庄口,开始生产后,全局采用该工艺庄口ID作为贯穿系统的线索,即产生的成本、产量、排工等均已该线索为依据。 +庄口指原料蚕茧的批次化管理,原料庄口指的是A地某年某季的原料蚕茧,工艺庄口指根据生产工艺组合的生产用庄口,开始生产后,全局采用该工艺庄口作为贯穿系统的线索,即产生的成本、产量、排工等均已该线索为依据。 缫丝生产主要的原料就是蚕茧,辅料有一些,能源主要有电力、燃气、煤、水等,产成品主要是生丝,生丝要称重,检验后打柄入库。 产成品的副产品有条吐、下足茧、茧衣等,这些要进行入库,销售,并暂估或者卖出作为收入。 缫丝不是按订单排产,核心围绕**工艺庄口**组织生产,不是通用离散制造的工单模式。