From 9f416561d66065b43d0bee5dcaf9588700171aae Mon Sep 17 00:00:00 2001 From: fanhongcai Date: Sun, 16 Aug 2026 03:58:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=85=A8=E7=B3=BB=E7=BB=9F=E7=AA=97?= =?UTF-8?q?=E4=BD=93=E5=8D=87=E7=BA=A7=20-=20=E9=85=8D=E7=BD=AE=E9=A9=B1?= =?UTF-8?q?=E5=8A=A8=E9=80=9A=E7=94=A8=E5=8D=95=E6=8D=AE=E9=A1=B5(BillPage?= =?UTF-8?q?)=E3=80=81=E5=8E=9F=E6=96=99=E4=B8=93=E7=94=A8=E5=8D=95?= =?UTF-8?q?=E6=8D=AE=E9=A1=B5=E3=80=81=E5=AE=BD=E5=B1=8F=E5=BC=B9=E7=AA=97?= =?UTF-8?q?=E5=8F=8A=E6=97=A5=E6=9C=9F=E8=BD=AC=E6=8D=A2=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 + openprint/src/core/headless/createHeadless.ts | 19 +- print-page.png | Bin 0 -> 14809 bytes server/scripts/debug-outstock-save.mjs | 68 +++ server/scripts/verify-iframe-size.mjs | 46 ++ .../F9MES.Api/Controllers/DataController.cs | 13 +- .../F9MES.Api/Controllers/MetaController.cs | 5 +- .../Controllers/WorkBenchController.cs | 26 +- .../Converters/FlexibleDateTimeConverter.cs | 30 ++ .../F9MES.Application/Init/InitDataService.cs | 3 + .../F9MES.Domain/BizBoss/BizBoss_Entities.cs | 8 +- server/src/F9MES.Domain/Fims/Fims_Entities.cs | 16 +- .../F9MES.Domain/FlexPay/FlexPay_Entities.cs | 4 +- server/src/F9MES.Domain/Lims/Lims_Entities.cs | 24 +- .../F9MES.Domain/ProHou/ProHou_Entities.cs | 12 +- .../F9MES.Domain/ProPlan/ProPlan_Entities.cs | 8 +- .../F9MES.Domain/ProQian/ProQian_Entities.cs | 12 +- .../F9MES.Domain/ProXuan/ProXuan_Entities.cs | 12 +- .../F9MES.Domain/Process/Process_Entities.cs | 16 +- server/src/F9MES.Domain/Qums/Qums_Entities.cs | 4 +- .../RawMaterial/RawMaterial_Entities.cs | 285 +++++++++- .../src/F9MES.Domain/WmsFu/WmsFu_Entities.cs | 8 +- web/src/components/BillPage.vue | 392 ++++++++++++++ web/src/components/CrudPage.vue | 77 ++- web/src/config/bill-configs.js | 384 +++++++++++++ web/src/config/table-map.js | 92 ++++ web/src/router/index.js | 33 +- web/src/views/process/zhuangkou-detail.vue | 5 +- web/src/views/rawmaterial/inspect-bill.vue | 503 ++++++++++++++++++ web/src/views/rawmaterial/instock-bill.vue | 410 ++++++++++++++ web/src/views/rawmaterial/outstock-bill.vue | 491 +++++++++++++++++ web/src/views/rawmaterial/zhuangkou-bill.vue | 461 ++++++++++++++++ 项目说明 | 2 +- 33 files changed, 3379 insertions(+), 96 deletions(-) create mode 100644 print-page.png create mode 100644 server/scripts/debug-outstock-save.mjs create mode 100644 server/scripts/verify-iframe-size.mjs create mode 100644 server/src/F9MES.Api/Converters/FlexibleDateTimeConverter.cs create mode 100644 web/src/components/BillPage.vue create mode 100644 web/src/config/bill-configs.js create mode 100644 web/src/views/rawmaterial/inspect-bill.vue create mode 100644 web/src/views/rawmaterial/instock-bill.vue create mode 100644 web/src/views/rawmaterial/outstock-bill.vue create mode 100644 web/src/views/rawmaterial/zhuangkou-bill.vue 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 0000000000000000000000000000000000000000..a4f6c71b2bd63fd031b2ed545bbab37ed2c24713 GIT binary patch literal 14809 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 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地某年某季的原料蚕茧,工艺庄口指根据生产工艺组合的生产用庄口,开始生产后,全局采用该工艺庄口作为贯穿系统的线索,即产生的成本、产量、排工等均已该线索为依据。 缫丝生产主要的原料就是蚕茧,辅料有一些,能源主要有电力、燃气、煤、水等,产成品主要是生丝,生丝要称重,检验后打柄入库。 产成品的副产品有条吐、下足茧、茧衣等,这些要进行入库,销售,并暂估或者卖出作为收入。 缫丝不是按订单排产,核心围绕**工艺庄口**组织生产,不是通用离散制造的工单模式。