feat: 全系统窗体升级 - 配置驱动通用单据页(BillPage)、原料专用单据页、宽屏弹窗及日期转换优化
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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<void> {
|
||||
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
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -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);
|
||||
});
|
||||
@@ -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')
|
||||
@@ -282,14 +282,19 @@ public class DataController : ControllerBase
|
||||
return matches.Count == 1 ? matches[0] : null;
|
||||
}
|
||||
|
||||
/// <summary>请求体 JSON 序列化选项:大小写不敏感 + 宽松日期解析(兼容 "yyyy-MM-dd HH:mm:ss")</summary>
|
||||
private static readonly System.Text.Json.JsonSerializerOptions BodyJsonOptions = new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
Converters = { new Converters.FlexibleDateTimeConverter() }
|
||||
};
|
||||
|
||||
/// <summary>将请求体 JSON 转换为目标实体对象</summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>提取请求体中出现的字段名(用于部分更新,属性名大小写不敏感)</summary>
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -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, "", "-"),
|
||||
|
||||
@@ -78,8 +78,20 @@ public class WorkBenchController : ControllerBase
|
||||
trend.Add(new { date = day.ToString("MM-dd"), weight = sum });
|
||||
}
|
||||
|
||||
// ===== 待办列表(工单 + 工作流审批)=====
|
||||
var pendingOrders = await _db.Select<ProPlan_WorkOrder>()
|
||||
.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<Process_ProcessZhuangkou>();
|
||||
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<ProPlan_WorkOrder>()
|
||||
.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<object>();
|
||||
foreach (var o in pendingOrders)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace F9MES.Api.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// 宽松 DateTime 转换器:
|
||||
/// 兼容前端 el-date-picker 输出的 "yyyy-MM-dd HH:mm:ss"(空格分隔)以及标准 ISO 8601(T 分隔)格式。
|
||||
/// 序列化保持与默认一致的 ISO 8601 输出,仅放宽反序列化。
|
||||
/// </summary>
|
||||
public class FlexibleDateTimeConverter : JsonConverter<DateTime>
|
||||
{
|
||||
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);
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
@@ -17,8 +17,8 @@ public class BizBoss_CostAllocate : BaseEntity
|
||||
[Column]
|
||||
public long PeriodId { get; set; }
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[Description("工艺庄口")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
@@ -47,8 +47,8 @@ public class BizBoss_Profit : BaseEntity
|
||||
[Column]
|
||||
public long PeriodId { get; set; }
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[Description("工艺庄口")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ public class Fims_Batch : BaseEntity
|
||||
[Column(DbType = "varchar(50)")]
|
||||
public string BatchNo { get; set; } = "";
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[Description("工艺庄口")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
@@ -72,8 +72,8 @@ public class Fims_Package : BaseEntity
|
||||
[Column]
|
||||
public long BatchId { get; set; }
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[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; } = "";
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[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
|
||||
{
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[Description("工艺庄口")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
|
||||
@@ -127,8 +127,8 @@ public class FlexPay_PieceResult : BaseEntity
|
||||
[Column]
|
||||
public long EmployeeId { get; set; }
|
||||
|
||||
/// <summary>关联工艺庄口ID</summary>
|
||||
[Description("关联工艺庄口ID")]
|
||||
/// <summary>关联工艺庄口</summary>
|
||||
[Description("关联工艺庄口")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ public class Lims_SpoolCheck : BaseEntity
|
||||
[Column]
|
||||
public DateTime CheckDate { get; set; }
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[Description("工艺庄口")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
@@ -83,8 +83,8 @@ public class Lims_BlackBoard : BaseEntity
|
||||
[Column]
|
||||
public DateTime CheckDate { get; set; }
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[Description("工艺庄口")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
@@ -138,8 +138,8 @@ public class Lims_Denier : BaseEntity
|
||||
[Column]
|
||||
public DateTime CheckDate { get; set; }
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[Description("工艺庄口")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
@@ -203,8 +203,8 @@ public class Lims_Moisture : BaseEntity
|
||||
[Column]
|
||||
public DateTime CheckDate { get; set; }
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[Description("工艺庄口")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
@@ -258,8 +258,8 @@ public class Lims_Abnormal : BaseEntity
|
||||
[Column]
|
||||
public int CheckType { get; set; } = 0;
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[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; } = "";
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[Description("工艺庄口")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ public class ProHou_Daily : BaseEntity
|
||||
[Column]
|
||||
public DateTime DailyDate { get; set; }
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[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; } = "";
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[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; } = "";
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[Description("工艺庄口")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ public class ProPlan_WorkOrder : BaseEntity
|
||||
[Column(DbType = "varchar(50)")]
|
||||
public string OrderNo { get; set; } = "";
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[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; } = "";
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[Description("工艺庄口")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
|
||||
@@ -122,8 +122,8 @@ public class ProQian_ShiftTime : BaseEntity
|
||||
[Column]
|
||||
public long EmployeeId { get; set; }
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[Description("工艺庄口")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
@@ -167,8 +167,8 @@ public class ProQian_CocoonBoiling : BaseEntity
|
||||
[Column]
|
||||
public DateTime BoilDate { get; set; }
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[Description("工艺庄口")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
@@ -197,8 +197,8 @@ public class ProQian_ThreadRecord : BaseEntity
|
||||
[Column]
|
||||
public DateTime RecordDate { get; set; }
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[Description("工艺庄口")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
|
||||
@@ -52,8 +52,8 @@ public class ProXuan_Daily : BaseEntity
|
||||
[Column]
|
||||
public long TeamId { get; set; }
|
||||
|
||||
/// <summary>原料庄口ID</summary>
|
||||
[Description("原料庄口ID")]
|
||||
/// <summary>原料庄口</summary>
|
||||
[Description("原料庄口")]
|
||||
[Column]
|
||||
public long ZhuangkouId { get; set; }
|
||||
|
||||
@@ -97,8 +97,8 @@ public class ProXuan_Quality : BaseEntity
|
||||
[Column]
|
||||
public DateTime QualityDate { get; set; }
|
||||
|
||||
/// <summary>原料庄口ID</summary>
|
||||
[Description("原料庄口ID")]
|
||||
/// <summary>原料庄口</summary>
|
||||
[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
|
||||
{
|
||||
/// <summary>原料庄口ID</summary>
|
||||
[Description("原料庄口ID")]
|
||||
/// <summary>原料庄口</summary>
|
||||
[Description("原料庄口")]
|
||||
[Column]
|
||||
public long ZhuangkouId { get; set; }
|
||||
|
||||
|
||||
@@ -23,13 +23,13 @@ public class Process_Trial : BaseEntity
|
||||
[Column]
|
||||
public int TrialType { get; set; } = 0;
|
||||
|
||||
/// <summary>原料庄口ID</summary>
|
||||
[Description("原料庄口ID")]
|
||||
/// <summary>原料庄口</summary>
|
||||
[Description("原料庄口")]
|
||||
[Column]
|
||||
public long ZhuangkouId { get; set; }
|
||||
|
||||
/// <summary>工艺庄口ID(可选)</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口(可选)</summary>
|
||||
[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; }
|
||||
|
||||
/// <summary>关联原料庄口ID(可选)</summary>
|
||||
[Description("关联原料庄口ID")]
|
||||
/// <summary>关联原料庄口(可选)</summary>
|
||||
[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; } = "";
|
||||
|
||||
/// <summary>工艺庄口ID</summary>
|
||||
[Description("工艺庄口ID")]
|
||||
/// <summary>工艺庄口</summary>
|
||||
[Description("工艺庄口")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ public class Qums_GrainCount : BaseEntity
|
||||
[Column(DbType = "varchar(50)")]
|
||||
public string BillNo { get; set; } = "";
|
||||
|
||||
/// <summary>原料庄口ID</summary>
|
||||
[Description("原料庄口ID")]
|
||||
/// <summary>原料庄口</summary>
|
||||
[Description("原料庄口")]
|
||||
[Column]
|
||||
public long ZhuangkouId { get; set; }
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace F9MES.Domain.RawMaterial;
|
||||
|
||||
/// <summary>原料庄口(编号规则:YLZ+yyyyMMdd+3位流水,如 YLZ20260815-001)</summary>
|
||||
[Table(Name = "RawMaterial_Zhuangkou")]
|
||||
[Description("原料庄口")]
|
||||
public class RawMaterial_Zhuangkou : BaseEntity
|
||||
{
|
||||
/// <summary>庄口编号(自动生成 YLZ20260815-001)</summary>
|
||||
@@ -72,6 +73,51 @@ public class RawMaterial_Zhuangkou : BaseEntity
|
||||
[Description("状态:0=收茧中 1=已入库 2=翻包中 3=已领完")]
|
||||
[Column]
|
||||
public int Status { get; set; } = 0;
|
||||
|
||||
/// <summary>综合等级(如 3A/2A/A,参照生丝等级)</summary>
|
||||
[Description("综合等级")]
|
||||
[Column(DbType = "varchar(20)")]
|
||||
public string? Grade { get; set; }
|
||||
|
||||
/// <summary>上车茧率(%,缫丝适制性)</summary>
|
||||
[Description("上车茧率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? LoadingCocoonRate { get; set; }
|
||||
|
||||
/// <summary>上茧率(%,好茧占比)</summary>
|
||||
[Description("上茧率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? GoodCocoonRate { get; set; }
|
||||
|
||||
/// <summary>茧层率(%,茧层占全茧重比例)</summary>
|
||||
[Description("茧层率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? ShellRate { get; set; }
|
||||
|
||||
/// <summary>出丝率(%,缫丝得丝率)</summary>
|
||||
[Description("出丝率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? ReelableRate { get; set; }
|
||||
|
||||
/// <summary>解舒率(%,缫丝解舒性能)</summary>
|
||||
[Description("解舒率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? Reelability { get; set; }
|
||||
|
||||
/// <summary>含水量/回潮率(%,干茧需防潮)</summary>
|
||||
[Description("含水量/回潮率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? MoistureRate { get; set; }
|
||||
|
||||
/// <summary>建议储存仓库ID(RawMaterial_Warehouse)</summary>
|
||||
[Description("建议储存仓库ID")]
|
||||
[Column]
|
||||
public long WarehouseId { get; set; }
|
||||
|
||||
/// <summary>建议储存期(月)</summary>
|
||||
[Description("建议储存期(月)")]
|
||||
[Column]
|
||||
public int? ShelfLifeMonths { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>原料入库单(磅码单过磅)</summary>
|
||||
@@ -83,8 +129,8 @@ public class RawMaterial_InStock : BaseEntity
|
||||
[Column(DbType = "varchar(50)")]
|
||||
public string BillNo { get; set; } = "";
|
||||
|
||||
/// <summary>原料庄口ID</summary>
|
||||
[Description("原料庄口ID")]
|
||||
/// <summary>原料庄口</summary>
|
||||
[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; } = "";
|
||||
|
||||
/// <summary>原料庄口ID</summary>
|
||||
[Description("原料庄口ID")]
|
||||
/// <summary>原料庄口</summary>
|
||||
[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; } = "";
|
||||
|
||||
/// <summary>原料庄口ID</summary>
|
||||
[Description("原料庄口ID")]
|
||||
/// <summary>原料庄口</summary>
|
||||
[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
|
||||
{
|
||||
/// <summary>原料庄口ID</summary>
|
||||
[Description("原料庄口ID")]
|
||||
/// <summary>原料庄口</summary>
|
||||
[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; }
|
||||
}
|
||||
|
||||
/// <summary>原料仓库(干茧仓/冷冻鲜茧仓(冷库)/下脚料仓)</summary>
|
||||
[Table(Name = "RawMaterial_Warehouse")]
|
||||
[Description("原料仓库")]
|
||||
public class RawMaterial_Warehouse : BaseEntity
|
||||
{
|
||||
/// <summary>仓库编号</summary>
|
||||
[Description("仓库编号")]
|
||||
[Column(DbType = "varchar(50)")]
|
||||
public string Code { get; set; } = "";
|
||||
|
||||
/// <summary>仓库名称</summary>
|
||||
[Description("仓库名称")]
|
||||
[Column(DbType = "varchar(100)")]
|
||||
public string Name { get; set; } = "";
|
||||
|
||||
/// <summary>仓库类型:0=干茧仓 1=冷冻鲜茧仓(冷库) 2=下脚料仓</summary>
|
||||
[Description("仓库类型:0=干茧仓 1=冷冻鲜茧仓 2=下脚料仓")]
|
||||
[Column]
|
||||
public int WarehouseType { get; set; } = 0;
|
||||
|
||||
/// <summary>库区/仓位说明</summary>
|
||||
[Description("库区/仓位")]
|
||||
[Column(DbType = "varchar(200)")]
|
||||
public string? Area { get; set; }
|
||||
|
||||
/// <summary>温度下限(℃,0=不限)</summary>
|
||||
[Description("温度下限(℃)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? TempMin { get; set; }
|
||||
|
||||
/// <summary>温度上限(℃,0=不限)</summary>
|
||||
[Description("温度上限(℃)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? TempMax { get; set; }
|
||||
|
||||
/// <summary>湿度下限(%RH)</summary>
|
||||
[Description("湿度下限(%RH)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? HumiMin { get; set; }
|
||||
|
||||
/// <summary>湿度上限(%RH,干茧防霉需控制在 60-70)</summary>
|
||||
[Description("湿度上限(%RH)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? HumiMax { get; set; }
|
||||
|
||||
/// <summary>库容量(吨)</summary>
|
||||
[Description("库容量(吨)")]
|
||||
[Column(DbType = "decimal(12,2)")]
|
||||
public decimal? Capacity { get; set; }
|
||||
|
||||
/// <summary>当前存量(吨)</summary>
|
||||
[Description("当前存量(吨)")]
|
||||
[Column(DbType = "decimal(12,2)")]
|
||||
public decimal? CurrentLoad { get; set; }
|
||||
|
||||
/// <summary>负责人</summary>
|
||||
[Description("负责人")]
|
||||
[Column(DbType = "varchar(50)")]
|
||||
public string? Manager { get; set; }
|
||||
|
||||
/// <summary>状态:0=停用 1=启用</summary>
|
||||
[Description("状态:0=停用 1=启用")]
|
||||
[Column]
|
||||
public int Status { get; set; } = 1;
|
||||
}
|
||||
|
||||
/// <summary>仓储环境记录(干茧仓防霉湿度、冷库鲜茧温度巡检)</summary>
|
||||
[Table(Name = "RawMaterial_EnvRecord")]
|
||||
[Description("仓储环境记录")]
|
||||
public class RawMaterial_EnvRecord : BaseEntity
|
||||
{
|
||||
/// <summary>仓库ID(RawMaterial_Warehouse)</summary>
|
||||
[Description("仓库ID")]
|
||||
[Column]
|
||||
public long WarehouseId { get; set; }
|
||||
|
||||
/// <summary>记录时间</summary>
|
||||
[Description("记录时间")]
|
||||
[Column]
|
||||
public DateTime RecordTime { get; set; }
|
||||
|
||||
/// <summary>实测温度(℃)</summary>
|
||||
[Description("实测温度(℃)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? Temperature { get; set; }
|
||||
|
||||
/// <summary>实测湿度(%RH)</summary>
|
||||
[Description("实测湿度(%RH)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? Humidity { get; set; }
|
||||
|
||||
/// <summary>达标状态:0=达标 1=温度超标 2=湿度超标 3=温湿度均超标</summary>
|
||||
[Description("达标状态:0=达标 1=温度超标 2=湿度超标 3=均超标")]
|
||||
[Column]
|
||||
public int AlarmStatus { get; set; } = 0;
|
||||
|
||||
/// <summary>处理措施</summary>
|
||||
[Description("处理措施")]
|
||||
[Column(DbType = "varchar(200)")]
|
||||
public string? Measure { get; set; }
|
||||
|
||||
/// <summary>记录人</summary>
|
||||
[Description("记录人")]
|
||||
[Column(DbType = "varchar(50)")]
|
||||
public string? Recorder { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>原料检验单(蚕茧入库质检与定级)</summary>
|
||||
[Table(Name = "RawMaterial_Inspect")]
|
||||
[Description("原料检验单")]
|
||||
public class RawMaterial_Inspect : BaseEntity
|
||||
{
|
||||
/// <summary>检验单号(YJ+日期+流水)</summary>
|
||||
[Description("检验单号(YJ+日期+流水)")]
|
||||
[Column(DbType = "varchar(50)")]
|
||||
public string Code { get; set; } = "";
|
||||
|
||||
/// <summary>原料庄口ID(RawMaterial_Zhuangkou)</summary>
|
||||
[Description("原料庄口ID")]
|
||||
[Column]
|
||||
public long ZhuangkouId { get; set; }
|
||||
|
||||
/// <summary>关联入库单ID(RawMaterial_InStock,可空)</summary>
|
||||
[Description("关联入库单ID")]
|
||||
[Column]
|
||||
public long InStockId { get; set; }
|
||||
|
||||
/// <summary>检验日期</summary>
|
||||
[Description("检验日期")]
|
||||
[Column]
|
||||
public DateTime CheckDate { get; set; }
|
||||
|
||||
/// <summary>蚕茧类型:0=干茧 1=冷冻鲜茧</summary>
|
||||
[Description("蚕茧类型:0=干茧 1=冷冻鲜茧")]
|
||||
[Column]
|
||||
public int CocoonType { get; set; } = 0;
|
||||
|
||||
/// <summary>取样重量(kg)</summary>
|
||||
[Description("取样重量(kg)")]
|
||||
[Column(DbType = "decimal(14,2)")]
|
||||
public decimal? SampleWeight { get; set; }
|
||||
|
||||
/// <summary>上车茧率(%)</summary>
|
||||
[Description("上车茧率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? LoadingCocoonRate { get; set; }
|
||||
|
||||
/// <summary>上茧率(%)</summary>
|
||||
[Description("上茧率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? GoodCocoonRate { get; set; }
|
||||
|
||||
/// <summary>下脚茧率(%)</summary>
|
||||
[Description("下脚茧率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? WasteCocoonRate { get; set; }
|
||||
|
||||
/// <summary>茧层率(%)</summary>
|
||||
[Description("茧层率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? ShellRate { get; set; }
|
||||
|
||||
/// <summary>出丝率(%)</summary>
|
||||
[Description("出丝率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? ReelableRate { get; set; }
|
||||
|
||||
/// <summary>解舒率(%)</summary>
|
||||
[Description("解舒率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? Reelability { get; set; }
|
||||
|
||||
/// <summary>含水量/回潮率(%)</summary>
|
||||
[Description("含水量/回潮率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? MoistureRate { get; set; }
|
||||
|
||||
/// <summary>霉茧率(%)</summary>
|
||||
[Description("霉茧率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? MoldyRate { get; set; }
|
||||
|
||||
/// <summary>内印茧率(%)</summary>
|
||||
[Description("内印茧率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? InnerPrintRate { get; set; }
|
||||
|
||||
/// <summary>黄斑茧率(%)</summary>
|
||||
[Description("黄斑茧率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? YellowSpotRate { get; set; }
|
||||
|
||||
/// <summary>柴印茧率(%)</summary>
|
||||
[Description("柴印茧率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? ChaiPrintRate { get; set; }
|
||||
|
||||
/// <summary>双宫茧率(%)</summary>
|
||||
[Description("双宫茧率(%)")]
|
||||
[Column(DbType = "decimal(8,2)")]
|
||||
public decimal? DoubleCocoonRate { get; set; }
|
||||
|
||||
/// <summary>综合等级(参照生丝等级 6A-2A/A)</summary>
|
||||
[Description("综合等级")]
|
||||
[Column(DbType = "varchar(20)")]
|
||||
public string? Grade { get; set; }
|
||||
|
||||
/// <summary>检验结论:0=合格 1=降级 2=拒收</summary>
|
||||
[Description("检验结论:0=合格 1=降级 2=拒收")]
|
||||
[Column]
|
||||
public int Conclusion { get; set; } = 0;
|
||||
|
||||
/// <summary>检验员</summary>
|
||||
[Description("检验员")]
|
||||
[Column(DbType = "varchar(50)")]
|
||||
public string? Inspector { get; set; }
|
||||
|
||||
/// <summary>备注</summary>
|
||||
[Description("备注")]
|
||||
[Column(DbType = "varchar(500)")]
|
||||
public string? Remark { get; set; }
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ public class WmsFu_Stock : BaseEntity
|
||||
[Column]
|
||||
public long MaterialId { get; set; }
|
||||
|
||||
/// <summary>来源庄口ID(工艺庄口,可为0汇总)</summary>
|
||||
[Description("来源庄口ID")]
|
||||
/// <summary>来源庄口(工艺庄口,可为0汇总)</summary>
|
||||
[Description("来源庄口")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
@@ -53,8 +53,8 @@ public class WmsFu_InStock : BaseEntity
|
||||
[Column]
|
||||
public long MaterialId { get; set; }
|
||||
|
||||
/// <summary>工艺庄口ID(来源)</summary>
|
||||
[Description("工艺庄口ID(来源)")]
|
||||
/// <summary>工艺庄口(来源)</summary>
|
||||
[Description("工艺庄口(来源)")]
|
||||
[Column]
|
||||
public long ProcessZhuangkouId { get; set; }
|
||||
|
||||
|
||||
@@ -0,0 +1,392 @@
|
||||
<template>
|
||||
<div class="bill-page" v-loading="loading">
|
||||
<!-- 顶部工具条 -->
|
||||
<div class="bill-toolbar">
|
||||
<div class="toolbar-left">
|
||||
<el-button class="back-btn" round @click="goBack">← 返回</el-button>
|
||||
<span class="toolbar-title">{{ cfg.title || table }}</span>
|
||||
<el-tag v-if="codeField && form[codeField.name]" effect="dark" type="warning" class="toolbar-code">
|
||||
{{ codeLabel }}:{{ form[codeField.name] }}
|
||||
</el-tag>
|
||||
<el-tag v-if="statusField" effect="dark" :type="statusTagType" class="toolbar-status">
|
||||
{{ statusLabel }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<span class="toolbar-mode">{{ isEdit ? '编辑' : '新增' }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 分组卡片 -->
|
||||
<div class="bill-body">
|
||||
<div v-for="g in groups" :key="g.title" class="bill-card">
|
||||
<div class="card-title">{{ g.title }}</div>
|
||||
<el-form ref="formRef" :model="form" label-width="130px">
|
||||
<el-row :gutter="36">
|
||||
<el-col v-for="f in g.fields" :key="f.name" :span="f.span || 8">
|
||||
<el-form-item :label="f.label">
|
||||
<!-- 业务编号:自动生成,只读 -->
|
||||
<el-input v-if="f.isCode" v-model="form[f.name]" disabled placeholder="自动生成" />
|
||||
<!-- 主键:编辑态只读展示 -->
|
||||
<el-input v-else-if="f.isIdentity && isEdit" v-model="form[f.name]" disabled />
|
||||
<!-- 枚举下拉 -->
|
||||
<el-select
|
||||
v-else-if="f.options?.length"
|
||||
v-model="form[f.name]"
|
||||
:placeholder="'请选择' + f.label"
|
||||
style="width: 100%"
|
||||
clearable
|
||||
filterable
|
||||
>
|
||||
<el-option v-for="opt in f.options" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||||
</el-select>
|
||||
<!-- 关联下拉 -->
|
||||
<el-select
|
||||
v-else-if="f.refTable"
|
||||
v-model="form[f.name]"
|
||||
:placeholder="'请选择' + f.label"
|
||||
style="width: 100%"
|
||||
clearable
|
||||
filterable
|
||||
:loading="refLoading(f)"
|
||||
>
|
||||
<el-option v-for="opt in refCache[refKey(f)]?.items || []" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||||
</el-select>
|
||||
<!-- 文本 / 多行文本 -->
|
||||
<el-input
|
||||
v-else-if="isTextType(f)"
|
||||
v-model="form[f.name]"
|
||||
:placeholder="'请输入' + f.label"
|
||||
:rows="controlType(f) === 'textarea' ? 4 : undefined"
|
||||
:type="controlType(f) === 'textarea' ? 'textarea' : 'text'"
|
||||
clearable
|
||||
/>
|
||||
<!-- 数字 -->
|
||||
<el-input-number
|
||||
v-else-if="isNumberType(f)"
|
||||
v-model="form[f.name]"
|
||||
:controls-position="'right'"
|
||||
:precision="isDecimalType(f) ? 2 : 0"
|
||||
style="width: 100%"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
<!-- 日期时间 -->
|
||||
<el-date-picker
|
||||
v-else-if="isDateType(f)"
|
||||
v-model="form[f.name]"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择时间"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<!-- 开关 -->
|
||||
<el-switch v-else-if="f.propType === 'Boolean'" v-model="form[f.name]" />
|
||||
<!-- 兜底 -->
|
||||
<el-input v-else v-model="form[f.name]" placeholder="请输入" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<div class="bill-footer">
|
||||
<el-button size="large" @click="goBack">取消</el-button>
|
||||
<el-button size="large" type="primary" :loading="saving" @click="handleSave">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, watch, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { getTableMeta, crudGet, crudAdd, crudUpdate, getRefs, genCode } from '@/api'
|
||||
import { BILL_CONFIGS } from '@/config/bill-configs'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
/** 表名:路由参数,如 Process_Trial */
|
||||
const table = computed(() => route.params.table || '')
|
||||
/** 记录 id:存在即编辑态 */
|
||||
const id = computed(() => route.params.id || '')
|
||||
|
||||
const cfg = computed(() => BILL_CONFIGS[table.value] || {})
|
||||
const isEdit = computed(() => !!id.value)
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
const form = ref({})
|
||||
const fields = ref([])
|
||||
const refCache = reactive({})
|
||||
|
||||
const SYSTEM_FIELDS = ['Flag', 'AddTime', 'Adder', 'UpdateTime', 'Updater']
|
||||
|
||||
// ---------- 类型判断(与 CrudPage 保持一致) ----------
|
||||
const isTextType = (f) => f.propType === 'String'
|
||||
const isNumberType = (f) =>
|
||||
['Int16', 'Int32', 'Int64', 'Decimal', 'Double', 'Single', 'Byte', 'Long'].includes(f.propType)
|
||||
const isDecimalType = (f) => ['Decimal', 'Double', 'Single'].includes(f.propType)
|
||||
const isDateType = (f) => f.propType === 'DateTime' || f.dbType?.includes('date')
|
||||
const controlType = (f) => {
|
||||
if (f.isCode) return 'code'
|
||||
if (f.options?.length) return 'select'
|
||||
if (f.refTable) return 'ref'
|
||||
if (f.propType === 'Boolean') return 'switch'
|
||||
if (isDateType(f)) return 'date'
|
||||
if (isNumberType(f)) return 'number'
|
||||
if (f.dbType && f.dbType.includes('text')) return 'textarea'
|
||||
if (f.propType === 'String' && /(备注|说明|描述|内容|意见|原因|地址|依据|明细|JSON)/.test(f.label)) return 'textarea'
|
||||
return 'text'
|
||||
}
|
||||
|
||||
// ---------- 关联下拉 ----------
|
||||
const refKey = (f) => (f.refFilterField ? `${f.refTable}|${f.refFilterField}=${f.refFilterValue}` : f.refTable)
|
||||
const refLoading = (f) => !!refCache[refKey(f)]?.loading
|
||||
|
||||
async function loadRefs(key) {
|
||||
if (!key || refCache[key]) return
|
||||
const [t, filter] = key.split('|')
|
||||
let filterField, filterValue
|
||||
if (filter) {
|
||||
const eq = filter.indexOf('=')
|
||||
filterField = filter.slice(0, eq)
|
||||
filterValue = filter.slice(eq + 1)
|
||||
}
|
||||
refCache[key] = { items: [], map: {}, loading: true }
|
||||
try {
|
||||
const res = await getRefs(t, { size: 500, filterField, filterValue })
|
||||
if (res.code === 0 && Array.isArray(res.data)) {
|
||||
const map = {}
|
||||
res.data.forEach((i) => (map[i.value] = i.label))
|
||||
refCache[key] = { items: res.data, map, loading: false }
|
||||
} else {
|
||||
refCache[key] = { items: [], map: {}, loading: false }
|
||||
}
|
||||
} catch (e) {
|
||||
refCache[key] = { items: [], map: {}, loading: false }
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- 分组字段 ----------
|
||||
const groups = computed(() => {
|
||||
const fieldMap = {}
|
||||
fields.value.forEach((f) => (fieldMap[f.name] = f))
|
||||
return (cfg.value.groups || [])
|
||||
.map((g) => ({
|
||||
title: g.title,
|
||||
fields: (g.fields || [])
|
||||
.map((x) => (typeof x === 'string' ? { name: x } : x))
|
||||
.map((o) => ({ ...o, meta: fieldMap[o.name] }))
|
||||
.filter((o) => o.meta && !o.meta.isSystem && !o.meta.isIdentity)
|
||||
.map((o) => ({
|
||||
...o.meta,
|
||||
span: o.span || o.meta.span || 8,
|
||||
label: o.label || o.meta.label
|
||||
}))
|
||||
}))
|
||||
.filter((g) => g.fields.length)
|
||||
})
|
||||
|
||||
/** 自动编号字段 */
|
||||
const codeField = computed(() => fields.value.find((f) => f.isCode) || null)
|
||||
const codeLabel = computed(() => codeField.value?.label || '编号')
|
||||
|
||||
/** 状态字段(顶部标签) */
|
||||
const statusField = computed(() => {
|
||||
if (!cfg.value.statusField) return null
|
||||
return fields.value.find((f) => f.name === cfg.value.statusField) || null
|
||||
})
|
||||
const statusLabel = computed(() => {
|
||||
if (!statusField.value) return ''
|
||||
const v = form.value[statusField.value.name]
|
||||
if (statusField.value.options?.length) {
|
||||
return statusField.value.options.find((o) => o.value === v)?.label ?? (v ?? '')
|
||||
}
|
||||
return v ?? ''
|
||||
})
|
||||
const ENUM_TAG_TYPES = { 0: 'info', 1: 'success', 2: 'warning', 3: 'danger' }
|
||||
const statusTagType = computed(() => {
|
||||
const v = form.value[statusField.value?.name]
|
||||
return ENUM_TAG_TYPES[v] || 'info'
|
||||
})
|
||||
|
||||
// ---------- 数据加载 ----------
|
||||
async function loadMeta() {
|
||||
const res = await getTableMeta(table.value)
|
||||
fields.value = (res.data.fields || []).map((f) => ({
|
||||
...f,
|
||||
name: f.name.charAt(0).toLowerCase() + f.name.slice(1)
|
||||
}))
|
||||
const keys = [...new Set(fields.value.filter((f) => f.refTable).map(refKey))]
|
||||
await Promise.all(keys.map(loadRefs))
|
||||
document.title = `${cfg.value.title || table.value} - F9智慧缫丝系统`
|
||||
}
|
||||
|
||||
async function loadForm() {
|
||||
if (isEdit.value) {
|
||||
const res = await crudGet(table.value, id.value)
|
||||
const d = res.data || {}
|
||||
Object.keys(d).forEach((k) => (form.value[k] = d[k]))
|
||||
} else if (codeField.value) {
|
||||
try {
|
||||
const res = await genCode(table.value, codeField.value.name)
|
||||
if (res.code === 0) form.value[codeField.value.name] = res.data.value
|
||||
} catch (e) {
|
||||
/* 无编号规则则手动填写 */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 首次加载
|
||||
onMounted(load)
|
||||
|
||||
// 同一组件内路由参数变化(如新增保存后跳转编辑态)时重新加载
|
||||
watch(
|
||||
() => [route.params.table, route.params.id],
|
||||
(nv, ov) => {
|
||||
if (nv[0] !== ov[0] || nv[1] !== ov[1]) load()
|
||||
}
|
||||
)
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
await loadMeta()
|
||||
form.value = {}
|
||||
await loadForm()
|
||||
} catch (e) {
|
||||
ElMessage.error('加载数据失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- 保存 ----------
|
||||
async function handleSave() {
|
||||
saving.value = true
|
||||
try {
|
||||
const payload = { ...form.value }
|
||||
SYSTEM_FIELDS.forEach((k) => delete payload[k])
|
||||
if (!isEdit.value) delete payload.Id
|
||||
fields.value.forEach((f) => {
|
||||
const v = payload[f.name]
|
||||
if (v === '' || v === null || v === undefined) delete payload[f.name]
|
||||
})
|
||||
|
||||
if (isEdit.value) {
|
||||
await crudUpdate(table.value, { ...payload, id: id.value })
|
||||
ElMessage.success('保存成功')
|
||||
goBack()
|
||||
} else {
|
||||
const res = await crudAdd(table.value, payload)
|
||||
ElMessage.success('新增成功')
|
||||
const newId = res.data?.id || res.data?.Id
|
||||
if (newId) {
|
||||
await router.replace({ path: `/bill/${table.value}/${newId}` })
|
||||
} else {
|
||||
goBack()
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// 错误由全局拦截器统一提示
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
if (window.history.length > 1) {
|
||||
router.back()
|
||||
} else {
|
||||
router.push('/workbench')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.bill-page {
|
||||
max-width: 1360px;
|
||||
margin: 0 auto;
|
||||
padding: 18px 24px 60px;
|
||||
}
|
||||
.bill-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: linear-gradient(135deg, #1f2d3d 0%, #2d4055 100%);
|
||||
border-radius: 10px;
|
||||
padding: 14px 24px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 10px rgba(31, 45, 61, 0.15);
|
||||
}
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
}
|
||||
.back-btn {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
border: none;
|
||||
color: #fff;
|
||||
padding: 8px 18px;
|
||||
}
|
||||
.back-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.22);
|
||||
color: #fff;
|
||||
}
|
||||
.toolbar-title {
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
}
|
||||
.toolbar-code {
|
||||
font-size: 13px;
|
||||
}
|
||||
.toolbar-mode {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
font-size: 13px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 20px;
|
||||
padding: 4px 14px;
|
||||
}
|
||||
.bill-card {
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
|
||||
padding: 30px 36px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.card-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #1f2d3d;
|
||||
margin-bottom: 26px;
|
||||
padding-left: 12px;
|
||||
border-left: 4px solid #409eff;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.bill-card .el-form-item {
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
.bill-card .el-form-item__label {
|
||||
font-weight: 500;
|
||||
}
|
||||
.bill-card .el-input__wrapper,
|
||||
.bill-card .el-textarea__inner {
|
||||
padding: 6px 14px;
|
||||
}
|
||||
.bill-card .el-input-number .el-input__wrapper {
|
||||
padding-left: 14px;
|
||||
}
|
||||
.bill-footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
padding: 8px 0 24px;
|
||||
}
|
||||
.bill-footer .el-button {
|
||||
min-width: 120px;
|
||||
padding: 12px 28px;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
@@ -47,7 +47,7 @@
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</div>
|
||||
<div class="flex" style="gap: 10px">
|
||||
<el-button type="success" @click="openDialog()">
|
||||
<el-button type="success" @click="handleAdd">
|
||||
<el-icon style="margin-right: 4px"><Plus /></el-icon>新增
|
||||
</el-button>
|
||||
<el-button type="danger" :disabled="!selection.length" @click="handleBatchDelete">
|
||||
@@ -124,7 +124,7 @@
|
||||
size="small"
|
||||
@click="handleDetail(row)"
|
||||
>详情</el-button>
|
||||
<el-button link type="primary" size="small" @click="openDialog(row)">编辑</el-button>
|
||||
<el-button link type="primary" size="small" @click="handleEdit(row)">编辑</el-button>
|
||||
<el-button link type="danger" size="small" @click="handleDelete(row)">删除</el-button>
|
||||
<el-button
|
||||
v-if="printTemplate"
|
||||
@@ -163,13 +163,14 @@
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="dialogTitle"
|
||||
width="640px"
|
||||
:width="dialogWidth"
|
||||
class="crud-dialog"
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="130px">
|
||||
<el-row :gutter="16">
|
||||
<el-col v-for="f in formFields" :key="f.name" :span="f.span || 12">
|
||||
<el-row :gutter="32">
|
||||
<el-col v-for="f in formFields" :key="f.name" :span="f.span || formSpan">
|
||||
<el-form-item :label="f.label" :prop="f.name">
|
||||
<!-- 业务编号:只读展示(新增时自动生成) -->
|
||||
<el-input v-if="f.isCode" v-model="form[f.name]" disabled placeholder="自动生成" />
|
||||
@@ -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;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 弹窗布局样式:el-dialog 挂载在 body 上,需全局样式生效 -->
|
||||
<style>
|
||||
.crud-dialog .el-dialog__header {
|
||||
padding: 18px 36px 14px;
|
||||
}
|
||||
.crud-dialog .el-dialog__body {
|
||||
padding: 20px 36px 8px;
|
||||
}
|
||||
.crud-dialog .el-form-item {
|
||||
margin-bottom: 26px;
|
||||
}
|
||||
.crud-dialog .el-form-item__label {
|
||||
font-weight: 500;
|
||||
}
|
||||
.crud-dialog .el-input__wrapper,
|
||||
.crud-dialog .el-textarea__inner {
|
||||
padding: 6px 14px;
|
||||
}
|
||||
.crud-dialog .el-input-number .el-input__wrapper {
|
||||
padding-left: 14px;
|
||||
padding-right: 42px;
|
||||
}
|
||||
.crud-dialog .el-dialog__footer {
|
||||
padding: 6px 36px 20px;
|
||||
}
|
||||
.crud-dialog .el-dialog__footer .el-button {
|
||||
padding: 10px 28px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -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'] }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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 列表操作列会显示"打印"按钮
|
||||
|
||||
+32
-1
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,503 @@
|
||||
<template>
|
||||
<div class="bill-page">
|
||||
<!-- 顶部工具条 -->
|
||||
<div class="bill-toolbar">
|
||||
<div class="toolbar-left">
|
||||
<el-button text @click="goBack">
|
||||
<el-icon><ArrowLeft /></el-icon>返回
|
||||
</el-button>
|
||||
<span class="bill-title">原料检验单</span>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<span class="bill-no">检验单号:{{ form.code || '(自动生成)' }}</span>
|
||||
<el-tag :type="conclusionTag.type" size="small">{{ conclusionTag.label }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 检验单信息 -->
|
||||
<el-card v-loading="loading" shadow="never" class="bill-card">
|
||||
<template #header>
|
||||
<div class="card-title"><span class="title-mark"></span>检验单信息</div>
|
||||
</template>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-position="right" label-width="110px" class="bill-form">
|
||||
<el-row :gutter="36">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="检验单号">
|
||||
<el-input v-model="form.code" :disabled="isEdit" placeholder="新增自动生成,可手动修改" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="原料庄口" prop="zhuangkouId">
|
||||
<el-select
|
||||
v-model="form.zhuangkouId"
|
||||
placeholder="请选择原料庄口"
|
||||
filterable
|
||||
clearable
|
||||
:loading="refLoading('RawMaterial_Zhuangkou')"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in refCache['RawMaterial_Zhuangkou']?.items || []"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="关联入库单">
|
||||
<el-select
|
||||
v-model="form.inStockId"
|
||||
placeholder="关联磅码单(可选)"
|
||||
filterable
|
||||
clearable
|
||||
:loading="inStockLoading"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in inStockOptions"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="检验日期" prop="checkDate">
|
||||
<el-date-picker
|
||||
v-model="form.checkDate"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择检验时间"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="蚕茧类型" prop="cocoonType">
|
||||
<el-select v-model="form.cocoonType" placeholder="请选择蚕茧类型" style="width: 100%">
|
||||
<el-option label="干茧" :value="0" />
|
||||
<el-option label="冷冻鲜茧" :value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="检验员">
|
||||
<el-input v-model="form.inspector" placeholder="检验员姓名" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 主要质量指标 -->
|
||||
<el-card v-loading="loading" shadow="never" class="bill-card">
|
||||
<template #header>
|
||||
<div class="card-title"><span class="title-mark title-mark--green"></span>主要质量指标(%)</div>
|
||||
</template>
|
||||
<el-form label-position="right" label-width="130px" class="bill-form">
|
||||
<el-row :gutter="36">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="取样重量(kg)">
|
||||
<el-input-number v-model="form.sampleWeight" :precision="2" :min="0" :step="0.5" controls-position="right" placeholder="kg" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="上车茧率">
|
||||
<el-input-number v-model="form.loadingCocoonRate" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="上茧率">
|
||||
<el-input-number v-model="form.goodCocoonRate" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="下脚茧率">
|
||||
<el-input-number v-model="form.wasteCocoonRate" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="茧层率">
|
||||
<el-input-number v-model="form.shellRate" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="出丝率">
|
||||
<el-input-number v-model="form.reelableRate" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="解舒率">
|
||||
<el-input-number v-model="form.reelability" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="含水量/回潮率">
|
||||
<el-input-number v-model="form.moistureRate" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label=" ">
|
||||
<el-alert type="info" :closable="false" show-icon title="百分比指标建议范围 0-100" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 缺陷茧指标 -->
|
||||
<el-card v-loading="loading" shadow="never" class="bill-card">
|
||||
<template #header>
|
||||
<div class="card-title"><span class="title-mark title-mark--orange"></span>缺陷茧指标(%)</div>
|
||||
</template>
|
||||
<el-form label-position="right" label-width="130px" class="bill-form">
|
||||
<el-row :gutter="36">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="霉茧率">
|
||||
<el-input-number v-model="form.moldyRate" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="内印茧率">
|
||||
<el-input-number v-model="form.innerPrintRate" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="黄斑茧率">
|
||||
<el-input-number v-model="form.yellowSpotRate" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="柴印茧率">
|
||||
<el-input-number v-model="form.chaiPrintRate" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="双宫茧率">
|
||||
<el-input-number v-model="form.doubleCocoonRate" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="综合等级">
|
||||
<el-select v-model="form.grade" placeholder="参照生丝等级" filterable allow-create clearable style="width: 100%">
|
||||
<el-option label="6A" value="6A" />
|
||||
<el-option label="5A" value="5A" />
|
||||
<el-option label="4A" value="4A" />
|
||||
<el-option label="3A" value="3A" />
|
||||
<el-option label="2A" value="2A" />
|
||||
<el-option label="A" value="A" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 检验结论 -->
|
||||
<el-card v-loading="loading" shadow="never" class="bill-card">
|
||||
<template #header>
|
||||
<div class="card-title"><span class="title-mark"></span>检验结论</div>
|
||||
</template>
|
||||
<el-form label-position="right" label-width="110px" class="bill-form">
|
||||
<el-row :gutter="36">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="检验结论" prop="conclusion">
|
||||
<el-select v-model="form.conclusion" placeholder="请选择检验结论" style="width: 100%">
|
||||
<el-option label="合格" :value="0" />
|
||||
<el-option label="降级" :value="1" />
|
||||
<el-option label="拒收" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="检验补充说明" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<div class="bill-footer">
|
||||
<el-button @click="goBack">取消</el-button>
|
||||
<el-button type="primary" :loading="saving" @click="handleSave">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { crudGet, crudAdd, crudUpdate, crudPage, getRefs, genCode } from '@/api'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const TABLE = 'RawMaterial_Inspect'
|
||||
|
||||
const billId = computed(() => Number(route.params.id) || 0)
|
||||
const isEdit = computed(() => billId.value > 0)
|
||||
|
||||
// ================= 状态 =================
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
const formRef = ref()
|
||||
const form = reactive({
|
||||
code: '',
|
||||
zhuangkouId: null,
|
||||
inStockId: null,
|
||||
checkDate: '',
|
||||
cocoonType: 0,
|
||||
sampleWeight: null,
|
||||
loadingCocoonRate: null,
|
||||
goodCocoonRate: null,
|
||||
wasteCocoonRate: null,
|
||||
shellRate: null,
|
||||
reelableRate: null,
|
||||
reelability: null,
|
||||
moistureRate: null,
|
||||
moldyRate: null,
|
||||
innerPrintRate: null,
|
||||
yellowSpotRate: null,
|
||||
chaiPrintRate: null,
|
||||
doubleCocoonRate: null,
|
||||
grade: '',
|
||||
conclusion: 0,
|
||||
inspector: '',
|
||||
remark: ''
|
||||
})
|
||||
const rules = {
|
||||
zhuangkouId: [{ required: true, message: '请选择原料庄口', trigger: 'change' }],
|
||||
checkDate: [{ required: true, message: '请选择检验日期', trigger: 'change' }],
|
||||
cocoonType: [{ required: true, message: '请选择蚕茧类型', trigger: 'change' }],
|
||||
conclusion: [{ required: true, message: '请选择检验结论', trigger: 'change' }]
|
||||
}
|
||||
|
||||
/** 关联表下拉缓存 */
|
||||
const refCache = reactive({})
|
||||
|
||||
// ================= 计算属性 =================
|
||||
const conclusionMap = {
|
||||
0: { label: '合格', type: 'success' },
|
||||
1: { label: '降级', type: 'warning' },
|
||||
2: { label: '拒收', type: 'danger' }
|
||||
}
|
||||
const conclusionTag = computed(() => conclusionMap[form.conclusion] || conclusionMap[0])
|
||||
|
||||
/** 关联入库单(无 RefMap,走 crudPage 拉取磅码单) */
|
||||
const inStockOptions = ref([])
|
||||
const inStockLoading = ref(false)
|
||||
|
||||
// ================= 关联下拉 =================
|
||||
async function loadRefs(table, params = {}) {
|
||||
if (refCache[table]) return
|
||||
refCache[table] = { items: [], loading: true }
|
||||
try {
|
||||
const res = await getRefs(table, { size: 500, ...params })
|
||||
refCache[table] = {
|
||||
items: res.code === 0 && Array.isArray(res.data) ? res.data : [],
|
||||
loading: false
|
||||
}
|
||||
} catch (e) {
|
||||
refCache[table] = { items: [], loading: false }
|
||||
}
|
||||
}
|
||||
const refLoading = (t) => !!refCache[t]?.loading
|
||||
|
||||
async function loadInStockOptions() {
|
||||
inStockLoading.value = true
|
||||
try {
|
||||
const res = await crudPage('RawMaterial_InStock', { page: 1, size: 500 })
|
||||
const items = res.data?.items || []
|
||||
inStockOptions.value = items.map((i) => ({ value: i.id, label: i.billNo }))
|
||||
} catch (e) {
|
||||
inStockOptions.value = []
|
||||
} finally {
|
||||
inStockLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ================= 工具 =================
|
||||
const fmtDateTime = (d) => {
|
||||
const dt = d instanceof Date ? d : new Date(d)
|
||||
const p = (n) => String(n).padStart(2, '0')
|
||||
return `${dt.getFullYear()}-${p(dt.getMonth() + 1)}-${p(dt.getDate())} ${p(dt.getHours())}:${p(dt.getMinutes())}:${p(dt.getSeconds())}`
|
||||
}
|
||||
|
||||
// ================= 加载单据 =================
|
||||
async function loadBill() {
|
||||
if (!isEdit.value) {
|
||||
form.checkDate = fmtDateTime(new Date())
|
||||
try {
|
||||
const res = await genCode(TABLE, 'code')
|
||||
if (res.code === 0) form.code = res.data.value
|
||||
} catch (e) {
|
||||
/* 生成失败可手动填写 */
|
||||
}
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await crudGet(TABLE, billId.value)
|
||||
const d = res.data || {}
|
||||
Object.keys(form).forEach((k) => {
|
||||
if (d[k] !== undefined && d[k] !== null) form[k] = d[k]
|
||||
})
|
||||
} catch (e) {
|
||||
/* 拦截器已提示 */
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ================= 保存 =================
|
||||
async function handleSave() {
|
||||
await formRef.value.validate()
|
||||
saving.value = true
|
||||
try {
|
||||
const payload = { ...form }
|
||||
;['id', 'flag', 'addTime', 'adder', 'updateTime', 'updater'].forEach((k) => delete payload[k])
|
||||
Object.keys(payload).forEach((k) => {
|
||||
if (payload[k] === '' || payload[k] === null || payload[k] === undefined) delete payload[k]
|
||||
})
|
||||
if (isEdit.value) payload.id = billId.value
|
||||
|
||||
let savedId = billId.value
|
||||
if (isEdit.value) {
|
||||
await crudUpdate(TABLE, payload)
|
||||
ElMessage.success('修改成功')
|
||||
} else {
|
||||
const res = await crudAdd(TABLE, payload)
|
||||
savedId = res.data?.id || res.data?.Id
|
||||
if (!savedId) savedId = billId.value
|
||||
ElMessage.success('新增成功')
|
||||
}
|
||||
if (savedId && !isEdit.value) {
|
||||
await router.replace({ path: `/rawmaterial/inspect-bill/${savedId}` })
|
||||
return
|
||||
}
|
||||
await loadBill()
|
||||
} catch (e) {
|
||||
/* 校验失败或拦截器已提示 */
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ================= 初始化 =================
|
||||
function goBack() {
|
||||
router.push('/rawmaterial/inspect')
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all([loadRefs('RawMaterial_Zhuangkou'), loadInStockOptions()])
|
||||
await loadBill()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.bill-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding: 18px 24px 28px;
|
||||
max-width: 1360px;
|
||||
}
|
||||
.bill-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #1f2d3d;
|
||||
border-radius: 6px;
|
||||
padding: 12px 22px;
|
||||
color: #fff;
|
||||
}
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
}
|
||||
.toolbar-left :deep(.el-button) {
|
||||
color: #cfd8e3;
|
||||
}
|
||||
.toolbar-left :deep(.el-button:hover) {
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
.bill-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.bill-no {
|
||||
font-size: 13px;
|
||||
color: #e6ecf5;
|
||||
}
|
||||
.bill-card {
|
||||
border: none;
|
||||
}
|
||||
.bill-card :deep(.el-card__header) {
|
||||
padding: 14px 24px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
background: #f7f8fa;
|
||||
}
|
||||
.bill-card :deep(.el-card__body) {
|
||||
padding: 30px 36px 8px;
|
||||
}
|
||||
.card-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
.title-mark {
|
||||
width: 4px;
|
||||
height: 16px;
|
||||
border-radius: 2px;
|
||||
background: #409eff;
|
||||
}
|
||||
.title-mark--green {
|
||||
background: #67c23a;
|
||||
}
|
||||
.title-mark--orange {
|
||||
background: #e6a23c;
|
||||
}
|
||||
.bill-form :deep(.el-form-item) {
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
.bill-form :deep(.el-form-item__label) {
|
||||
font-weight: 500;
|
||||
color: #606266;
|
||||
}
|
||||
.bill-form :deep(.el-input__wrapper),
|
||||
.bill-form :deep(.el-textarea__inner) {
|
||||
padding: 6px 14px;
|
||||
}
|
||||
.bill-form :deep(.el-select__wrapper) {
|
||||
padding: 6px 14px;
|
||||
}
|
||||
.bill-form :deep(.el-input-number .el-input__wrapper) {
|
||||
padding-left: 14px;
|
||||
padding-right: 40px;
|
||||
}
|
||||
.bill-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
.bill-footer :deep(.el-button) {
|
||||
padding: 10px 28px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,410 @@
|
||||
<template>
|
||||
<div class="bill-page">
|
||||
<!-- 顶部工具条 -->
|
||||
<div class="bill-toolbar">
|
||||
<div class="toolbar-left">
|
||||
<el-button text @click="goBack">
|
||||
<el-icon><ArrowLeft /></el-icon>返回
|
||||
</el-button>
|
||||
<span class="bill-title">原料磅码单</span>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<span class="bill-no">磅码单号:{{ form.billNo || '(自动生成)' }}</span>
|
||||
<el-tag :type="statusTag.type" size="small">{{ statusTag.label }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 磅码单信息 -->
|
||||
<el-card v-loading="loading" shadow="never" class="bill-card">
|
||||
<template #header>
|
||||
<div class="card-title"><span class="title-mark"></span>磅码单信息</div>
|
||||
</template>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-position="right" label-width="110px" class="bill-form">
|
||||
<el-row :gutter="36">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="磅码单号">
|
||||
<el-input v-model="form.billNo" :disabled="isEdit" placeholder="新增自动生成,可手动修改" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="原料庄口" prop="zhuangkouId">
|
||||
<el-select
|
||||
v-model="form.zhuangkouId"
|
||||
placeholder="请选择原料庄口"
|
||||
filterable
|
||||
clearable
|
||||
:loading="refLoading('RawMaterial_Zhuangkou')"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in refCache['RawMaterial_Zhuangkou']?.items || []"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="供应商" prop="supplierId">
|
||||
<el-select
|
||||
v-model="form.supplierId"
|
||||
placeholder="请选择供应商"
|
||||
filterable
|
||||
clearable
|
||||
:loading="refLoading('BaseCommon_Partner')"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in refCache['BaseCommon_Partner']?.items || []"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="过磅日期" prop="weighDate">
|
||||
<el-date-picker
|
||||
v-model="form.weighDate"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择过磅时间"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="过磅员">
|
||||
<el-input v-model="form.weighMan" placeholder="过磅员姓名" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="单据状态" prop="status">
|
||||
<el-select v-model="form.status" placeholder="请选择单据状态" style="width: 100%">
|
||||
<el-option label="草稿" :value="0" />
|
||||
<el-option label="已过磅" :value="1" />
|
||||
<el-option label="已入库" :value="2" />
|
||||
<el-option label="作废" :value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 过磅数据 -->
|
||||
<el-card v-loading="loading" shadow="never" class="bill-card">
|
||||
<template #header>
|
||||
<div class="card-title"><span class="title-mark title-mark--green"></span>过磅数据</div>
|
||||
</template>
|
||||
<el-form label-position="right" label-width="130px" class="bill-form">
|
||||
<el-row :gutter="36">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="毛重(kg)" prop="grossWeight">
|
||||
<el-input-number v-model="form.grossWeight" :precision="2" :min="0" :step="10" controls-position="right" placeholder="地磅毛重" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="皮重(kg)" prop="tareWeight">
|
||||
<el-input-number v-model="form.tareWeight" :precision="2" :min="0" :step="10" controls-position="right" placeholder="车皮重量" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="净重(kg)">
|
||||
<el-input :model-value="computedNet" disabled class="calc-input" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="单价(元/kg)" prop="unitPrice">
|
||||
<el-input-number v-model="form.unitPrice" :precision="4" :min="0" :step="0.1" controls-position="right" placeholder="收购单价" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="金额(元)">
|
||||
<el-input :model-value="computedAmount" disabled class="calc-input" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label=" ">
|
||||
<el-alert type="info" :closable="false" show-icon title="净重 = 毛重 − 皮重,金额 = 净重 × 单价,自动计算" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<div class="bill-footer">
|
||||
<el-button @click="goBack">取消</el-button>
|
||||
<el-button type="primary" :loading="saving" @click="handleSave">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { crudGet, crudAdd, crudUpdate, getRefs, genCode } from '@/api'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const TABLE = 'RawMaterial_InStock'
|
||||
|
||||
const billId = computed(() => Number(route.params.id) || 0)
|
||||
const isEdit = computed(() => billId.value > 0)
|
||||
|
||||
// ================= 状态 =================
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
const formRef = ref()
|
||||
const form = reactive({
|
||||
billNo: '',
|
||||
zhuangkouId: null,
|
||||
supplierId: null,
|
||||
grossWeight: null,
|
||||
tareWeight: null,
|
||||
netWeight: 0,
|
||||
weighDate: '',
|
||||
unitPrice: null,
|
||||
amount: 0,
|
||||
weighMan: '',
|
||||
status: 0
|
||||
})
|
||||
const rules = {
|
||||
zhuangkouId: [{ required: true, message: '请选择原料庄口', trigger: 'change' }],
|
||||
supplierId: [{ required: true, message: '请选择供应商', trigger: 'change' }],
|
||||
weighDate: [{ required: true, message: '请选择过磅日期', trigger: 'change' }],
|
||||
grossWeight: [{ required: true, message: '请输入毛重', trigger: 'blur' }],
|
||||
tareWeight: [{ required: true, message: '请输入皮重', trigger: 'blur' }],
|
||||
unitPrice: [{ required: true, message: '请输入单价', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '请选择单据状态', trigger: 'change' }]
|
||||
}
|
||||
|
||||
/** 关联表下拉缓存 */
|
||||
const refCache = reactive({})
|
||||
|
||||
// ================= 计算属性 =================
|
||||
const statusMap = {
|
||||
0: { label: '草稿', type: 'info' },
|
||||
1: { label: '已过磅', type: 'warning' },
|
||||
2: { label: '已入库', type: 'success' },
|
||||
3: { label: '作废', type: 'danger' }
|
||||
}
|
||||
const statusTag = computed(() => statusMap[form.status] || statusMap[0])
|
||||
|
||||
const computedNet = computed(() =>
|
||||
((Number(form.grossWeight) || 0) - (Number(form.tareWeight) || 0)).toFixed(2)
|
||||
)
|
||||
const computedAmount = computed(() =>
|
||||
((Number(computedNet.value) || 0) * (Number(form.unitPrice) || 0)).toFixed(2)
|
||||
)
|
||||
|
||||
// ================= 关联下拉 =================
|
||||
async function loadRefs(table, params = {}) {
|
||||
if (refCache[table]) return
|
||||
refCache[table] = { items: [], loading: true }
|
||||
try {
|
||||
const res = await getRefs(table, { size: 500, ...params })
|
||||
refCache[table] = {
|
||||
items: res.code === 0 && Array.isArray(res.data) ? res.data : [],
|
||||
loading: false
|
||||
}
|
||||
} catch (e) {
|
||||
refCache[table] = { items: [], loading: false }
|
||||
}
|
||||
}
|
||||
const refLoading = (t) => !!refCache[t]?.loading
|
||||
|
||||
// ================= 工具 =================
|
||||
const fmtDateTime = (d) => {
|
||||
const dt = d instanceof Date ? d : new Date(d)
|
||||
const p = (n) => String(n).padStart(2, '0')
|
||||
return `${dt.getFullYear()}-${p(dt.getMonth() + 1)}-${p(dt.getDate())} ${p(dt.getHours())}:${p(dt.getMinutes())}:${p(dt.getSeconds())}`
|
||||
}
|
||||
|
||||
// ================= 加载单据 =================
|
||||
async function loadBill() {
|
||||
if (!isEdit.value) {
|
||||
form.weighDate = fmtDateTime(new Date())
|
||||
try {
|
||||
const res = await genCode(TABLE, 'billNo')
|
||||
if (res.code === 0) form.billNo = res.data.value
|
||||
} catch (e) {
|
||||
/* 生成失败可手动填写 */
|
||||
}
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await crudGet(TABLE, billId.value)
|
||||
const d = res.data || {}
|
||||
Object.keys(form).forEach((k) => {
|
||||
if (d[k] !== undefined && d[k] !== null) form[k] = d[k]
|
||||
})
|
||||
} catch (e) {
|
||||
/* 拦截器已提示 */
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ================= 保存 =================
|
||||
async function handleSave() {
|
||||
await formRef.value.validate()
|
||||
saving.value = true
|
||||
try {
|
||||
const payload = { ...form }
|
||||
;['id', 'flag', 'addTime', 'adder', 'updateTime', 'updater'].forEach((k) => delete payload[k])
|
||||
Object.keys(payload).forEach((k) => {
|
||||
if (payload[k] === '' || payload[k] === null || payload[k] === undefined) delete payload[k]
|
||||
})
|
||||
// 净重与金额为计算字段,保存时写入后端
|
||||
payload.netWeight = Number(computedNet.value)
|
||||
payload.amount = Number(computedAmount.value)
|
||||
if (isEdit.value) payload.id = billId.value
|
||||
|
||||
let savedId = billId.value
|
||||
if (isEdit.value) {
|
||||
await crudUpdate(TABLE, payload)
|
||||
ElMessage.success('修改成功')
|
||||
} else {
|
||||
const res = await crudAdd(TABLE, payload)
|
||||
savedId = res.data?.id || res.data?.Id
|
||||
if (!savedId) savedId = billId.value
|
||||
ElMessage.success('新增成功')
|
||||
}
|
||||
if (savedId && !isEdit.value) {
|
||||
await router.replace({ path: `/rawmaterial/instock-bill/${savedId}` })
|
||||
return
|
||||
}
|
||||
await loadBill()
|
||||
} catch (e) {
|
||||
/* 校验失败或拦截器已提示 */
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ================= 初始化 =================
|
||||
function goBack() {
|
||||
router.push('/rawmaterial/instock')
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all([
|
||||
loadRefs('RawMaterial_Zhuangkou'),
|
||||
loadRefs('BaseCommon_Partner', { filterField: 'PartnerType', filterValue: 1 })
|
||||
])
|
||||
await loadBill()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.bill-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding: 18px 24px 28px;
|
||||
max-width: 1360px;
|
||||
}
|
||||
.bill-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #1f2d3d;
|
||||
border-radius: 6px;
|
||||
padding: 12px 22px;
|
||||
color: #fff;
|
||||
}
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
}
|
||||
.toolbar-left :deep(.el-button) {
|
||||
color: #cfd8e3;
|
||||
}
|
||||
.toolbar-left :deep(.el-button:hover) {
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
.bill-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.bill-no {
|
||||
font-size: 13px;
|
||||
color: #e6ecf5;
|
||||
}
|
||||
.bill-card {
|
||||
border: none;
|
||||
}
|
||||
.bill-card :deep(.el-card__header) {
|
||||
padding: 14px 24px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
background: #f7f8fa;
|
||||
}
|
||||
.bill-card :deep(.el-card__body) {
|
||||
padding: 30px 36px 8px;
|
||||
}
|
||||
.card-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
.title-mark {
|
||||
width: 4px;
|
||||
height: 16px;
|
||||
border-radius: 2px;
|
||||
background: #409eff;
|
||||
}
|
||||
.title-mark--green {
|
||||
background: #67c23a;
|
||||
}
|
||||
.bill-form :deep(.el-form-item) {
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
.bill-form :deep(.el-form-item__label) {
|
||||
font-weight: 500;
|
||||
color: #606266;
|
||||
}
|
||||
.bill-form :deep(.el-input__wrapper),
|
||||
.bill-form :deep(.el-textarea__inner) {
|
||||
padding: 6px 14px;
|
||||
}
|
||||
.bill-form :deep(.el-select__wrapper) {
|
||||
padding: 6px 14px;
|
||||
}
|
||||
.bill-form :deep(.el-input-number .el-input__wrapper) {
|
||||
padding-left: 14px;
|
||||
padding-right: 40px;
|
||||
}
|
||||
.calc-input :deep(.el-input__wrapper) {
|
||||
background: #f5f7fa;
|
||||
}
|
||||
.calc-input :deep(.el-input__inner) {
|
||||
font-weight: 600;
|
||||
color: #409eff;
|
||||
}
|
||||
.bill-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
.bill-footer :deep(.el-button) {
|
||||
padding: 10px 28px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,491 @@
|
||||
<template>
|
||||
<div class="bill-page">
|
||||
<!-- 顶部工具条:返回 + 单据标题 + 单据号 + 状态 + 打印/流程按钮 -->
|
||||
<div class="bill-toolbar">
|
||||
<div class="toolbar-left">
|
||||
<el-button text @click="goBack">
|
||||
<el-icon><ArrowLeft /></el-icon>返回
|
||||
</el-button>
|
||||
<span class="bill-title">原料出库单</span>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<span class="bill-no">单据号:{{ form.billNo || '(未生成)' }}</span>
|
||||
<el-tag :type="statusTag.type" size="small">{{ statusTag.label }}</el-tag>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button size="small" :disabled="!billId" @click="onPrint">
|
||||
<el-icon style="margin-right: 4px"><View /></el-icon>打印预览
|
||||
</el-button>
|
||||
<el-button size="small" :disabled="!billId" @click="onPrint">
|
||||
<el-icon style="margin-right: 4px"><Printer /></el-icon>打印
|
||||
</el-button>
|
||||
<el-button size="small" type="warning" :disabled="!billId" @click="openWorkflow">
|
||||
<el-icon style="margin-right: 4px"><Checked /></el-icon>流程
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 单据主体 -->
|
||||
<el-card v-loading="loading" shadow="never" class="bill-card">
|
||||
<template #header>
|
||||
<div class="card-title">
|
||||
<span class="title-mark"></span>单据信息
|
||||
<span v-if="saving" class="saving-tip">保存中…</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-position="right" class="bill-form">
|
||||
<el-row :gutter="28">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="出库日期" prop="outDate">
|
||||
<el-date-picker
|
||||
v-model="form.outDate"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择出库日期"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="出库类型" prop="outType">
|
||||
<el-select v-model="form.outType" placeholder="请选择出库类型" style="width: 100%">
|
||||
<el-option label="领料" :value="0" />
|
||||
<el-option label="退货" :value="1" />
|
||||
<el-option label="报损" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="原料庄口" prop="zhuangkouId">
|
||||
<el-select
|
||||
v-model="form.zhuangkouId"
|
||||
placeholder="请选择原料庄口"
|
||||
filterable
|
||||
clearable
|
||||
:loading="refLoading('RawMaterial_Zhuangkou')"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in refCache['RawMaterial_Zhuangkou']?.items || []"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="领料车间/组织" prop="outOrgId">
|
||||
<el-select
|
||||
v-model="form.outOrgId"
|
||||
placeholder="请选择领料车间/组织"
|
||||
filterable
|
||||
clearable
|
||||
:loading="refLoading('BaseSys_Org')"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in refCache['BaseSys_Org']?.items || []"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="出库重量(kg)" prop="outWeight">
|
||||
<el-input-number
|
||||
v-model="form.outWeight"
|
||||
:precision="2"
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
placeholder="请输入出库重量"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="领用人" prop="receiver">
|
||||
<el-input v-model="form.receiver" placeholder="请输入领用人" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="memo">
|
||||
<el-input v-model="form.memo" type="textarea" :rows="3" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<div class="bill-footer">
|
||||
<el-button @click="goBack">取消</el-button>
|
||||
<el-button
|
||||
v-if="billId && form.status === 0"
|
||||
type="danger"
|
||||
:loading="voiding"
|
||||
@click="handleVoid"
|
||||
>作废</el-button>
|
||||
<el-button type="primary" :loading="saving" @click="handleSave">保存</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 发起审批流程对话框 -->
|
||||
<el-dialog v-model="wfDialogVisible" title="发起审批流程" width="420px" destroy-on-close>
|
||||
<el-form label-width="90px">
|
||||
<el-form-item label="审批流程">
|
||||
<el-select v-model="wfFlowId" style="width: 100%" placeholder="请选择流程">
|
||||
<el-option v-for="f in wfFlows" :key="f.id" :label="f.name" :value="f.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="业务单号">
|
||||
<el-input :model-value="form.billNo" disabled />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="wfDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="wfSaving" @click="handleStartWorkflow">发起</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { crudGet, crudAdd, crudUpdate, getRefs, genCode } from '@/api'
|
||||
import request from '@/api/request'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { buildPrintUrl } from '@/config/print'
|
||||
import { workflowDefinitions, workflowStart } from '@/api/workflow'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const TABLE = 'RawMaterial_OutStock'
|
||||
const PRINT_TEMPLATE = '三等分原料出库单'
|
||||
const BIZ_TYPE = 'RawMaterial_OutStock'
|
||||
|
||||
/** 单据 id(无则为新增) */
|
||||
const billId = computed(() => Number(route.params.id) || 0)
|
||||
|
||||
// ================= 状态 =================
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
const voiding = ref(false)
|
||||
const form = reactive({
|
||||
billNo: '',
|
||||
zhuangkouId: null,
|
||||
outOrgId: null,
|
||||
outWeight: null,
|
||||
receiver: '',
|
||||
outDate: '',
|
||||
outType: 0,
|
||||
status: 0,
|
||||
memo: ''
|
||||
})
|
||||
const rules = {
|
||||
outDate: [{ required: true, message: '请选择出库日期', trigger: 'change' }],
|
||||
outType: [{ required: true, message: '请选择出库类型', trigger: 'change' }],
|
||||
zhuangkouId: [{ required: true, message: '请选择原料庄口', trigger: 'change' }],
|
||||
outOrgId: [{ required: true, message: '请选择领料车间/组织', trigger: 'change' }],
|
||||
outWeight: [{ required: true, message: '请输入出库重量', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
/** 关联表下拉缓存 */
|
||||
const refCache = reactive({})
|
||||
|
||||
// ================= 计算属性 =================
|
||||
const statusMap = { 0: { label: '草稿', type: 'info' }, 1: { label: '已出库', type: 'success' }, 2: { label: '作废', type: 'danger' } }
|
||||
const statusTag = computed(() => statusMap[form.status] || statusMap[0])
|
||||
|
||||
// ================= 关联下拉 =================
|
||||
async function loadRefs(table) {
|
||||
if (refCache[table]) return
|
||||
refCache[table] = { items: [], loading: true }
|
||||
try {
|
||||
const res = await getRefs(table, { size: 500 })
|
||||
refCache[table] = {
|
||||
items: (res.code === 0 && Array.isArray(res.data)) ? res.data : [],
|
||||
loading: false
|
||||
}
|
||||
} catch (e) {
|
||||
refCache[table] = { items: [], loading: false }
|
||||
}
|
||||
}
|
||||
const refLoading = (t) => !!refCache[t]?.loading
|
||||
|
||||
// ================= 加载单据 =================
|
||||
async function loadBill() {
|
||||
if (!billId.value) {
|
||||
// 新增:自动生成单据号 + 默认出库日期
|
||||
form.outDate = new Date().toISOString().slice(0, 19).replace('T', ' ')
|
||||
try {
|
||||
const res = await genCode(TABLE, 'billNo')
|
||||
if (res.code === 0) form.billNo = res.data.value
|
||||
} catch (e) {
|
||||
/* 生成失败可手动填写 */
|
||||
}
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await crudGet(TABLE, billId.value)
|
||||
const d = res.data || {}
|
||||
Object.keys(form).forEach((k) => {
|
||||
if (d[k] !== undefined && d[k] !== null) form[k] = d[k]
|
||||
})
|
||||
if (form.outDate) form.outDate = String(form.outDate).replace('T', ' ').slice(0, 19)
|
||||
} catch (e) {
|
||||
/* 拦截器已提示 */
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ================= 保存 =================
|
||||
async function handleSave() {
|
||||
saving.value = true
|
||||
try {
|
||||
const payload = { ...form }
|
||||
;['id', 'flag', 'addTime', 'adder', 'updateTime', 'updater'].forEach((k) => delete payload[k])
|
||||
Object.keys(payload).forEach((k) => {
|
||||
if (payload[k] === '' || payload[k] === null || payload[k] === undefined) delete payload[k]
|
||||
})
|
||||
// 新增默认草稿,编辑保持原状态
|
||||
if (!billId.value) payload.status = 0
|
||||
if (billId.value) payload.id = billId.value
|
||||
|
||||
let savedId = billId.value
|
||||
if (billId.value) {
|
||||
await crudUpdate(TABLE, payload)
|
||||
ElMessage.success('修改成功')
|
||||
} else {
|
||||
const res = await crudAdd(TABLE, payload)
|
||||
savedId = res.data?.id || res.data?.Id
|
||||
if (!savedId) savedId = billId.value
|
||||
ElMessage.success('新增成功')
|
||||
}
|
||||
// 保存成功后刷新为编辑态(启用打印/流程)
|
||||
if (savedId && !billId.value) {
|
||||
await router.replace({ path: `/rawmaterial/outstock-bill/${savedId}` })
|
||||
return
|
||||
}
|
||||
await loadBill()
|
||||
} catch (e) {
|
||||
/* 拦截器已提示 */
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ================= 作废 =================
|
||||
async function handleVoid() {
|
||||
await ElMessageBox.confirm(`确定作废单据「${form.billNo}」吗?作废后不可恢复。`, '作废确认', {
|
||||
type: 'warning',
|
||||
confirmButtonText: '作废',
|
||||
cancelButtonText: '取消'
|
||||
})
|
||||
voiding.value = true
|
||||
try {
|
||||
await crudUpdate(TABLE, { id: billId.value, status: 2 })
|
||||
form.status = 2
|
||||
ElMessage.success('单据已作废')
|
||||
} catch (e) {
|
||||
/* 拦截器已提示 */
|
||||
} finally {
|
||||
voiding.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ================= 打印 =================
|
||||
/** 按模板名查找模板并打开 openprint 外部打印页(预览与打印共用,打开打印页) */
|
||||
async function onPrint() {
|
||||
try {
|
||||
const res = await request.get('/print/templates')
|
||||
const items = res.items || []
|
||||
const tpl = items.find((t) => t.name === PRINT_TEMPLATE)
|
||||
if (!tpl) {
|
||||
ElMessage.warning(`未找到打印模板「${PRINT_TEMPLATE}」,请先在打印设计器创建`)
|
||||
return
|
||||
}
|
||||
const url = buildPrintUrl({
|
||||
template: tpl.id,
|
||||
table: TABLE,
|
||||
row: billId.value,
|
||||
token: useUserStore().token
|
||||
})
|
||||
window.open(url, '_blank')
|
||||
} catch (e) {
|
||||
ElMessage.error('获取打印模板失败')
|
||||
}
|
||||
}
|
||||
|
||||
// ================= 审批(流程) =================
|
||||
const wfDialogVisible = ref(false)
|
||||
const wfSaving = ref(false)
|
||||
const wfFlows = ref([])
|
||||
const wfFlowId = ref(null)
|
||||
|
||||
async function openWorkflow() {
|
||||
wfFlows.value = []
|
||||
try {
|
||||
const res = await workflowDefinitions(BIZ_TYPE)
|
||||
wfFlows.value = res.data || []
|
||||
} catch (e) {
|
||||
/* 拦截器已提示 */
|
||||
}
|
||||
if (!wfFlows.value.length) {
|
||||
ElMessage.warning('该业务未配置可用的审批流程')
|
||||
return
|
||||
}
|
||||
wfFlowId.value = wfFlows.value[0].id
|
||||
wfDialogVisible.value = true
|
||||
}
|
||||
|
||||
async function handleStartWorkflow() {
|
||||
if (!wfFlowId.value) return
|
||||
wfSaving.value = true
|
||||
try {
|
||||
await workflowStart({
|
||||
workflowId: wfFlowId.value,
|
||||
bizType: BIZ_TYPE,
|
||||
bizId: billId.value,
|
||||
billNo: form.billNo
|
||||
})
|
||||
ElMessage.success('审批流程已发起')
|
||||
wfDialogVisible.value = false
|
||||
} catch (e) {
|
||||
/* 拦截器已提示 */
|
||||
} finally {
|
||||
wfSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ================= 初始化 =================
|
||||
function goBack() {
|
||||
router.push('/rawmaterial/outstock')
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all([loadRefs('RawMaterial_Zhuangkou'), loadRefs('BaseSys_Org')])
|
||||
await loadBill()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.bill-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
padding: 4px 8px 24px;
|
||||
}
|
||||
|
||||
/* 顶部工具条 */
|
||||
.bill-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #1f2d3d;
|
||||
border-radius: 6px;
|
||||
padding: 10px 18px;
|
||||
color: #fff;
|
||||
}
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.toolbar-left :deep(.el-button) {
|
||||
color: #cfd8e3;
|
||||
}
|
||||
.toolbar-left :deep(.el-button:hover) {
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
.bill-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.bill-no {
|
||||
font-size: 13px;
|
||||
color: #e6ecf5;
|
||||
}
|
||||
.toolbar-right :deep(.el-divider--vertical) {
|
||||
border-color: rgba(255, 255, 255, 0.25);
|
||||
margin: 0 6px;
|
||||
}
|
||||
.toolbar-right :deep(.el-button) {
|
||||
color: #e6ecf5;
|
||||
border-color: rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
.toolbar-right :deep(.el-button:hover) {
|
||||
color: #fff;
|
||||
border-color: #fff;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
/* 单据卡片 */
|
||||
.bill-card {
|
||||
border: none;
|
||||
}
|
||||
.bill-card :deep(.el-card__header) {
|
||||
padding: 14px 20px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
background: #f7f8fa;
|
||||
}
|
||||
.bill-card :deep(.el-card__body) {
|
||||
padding: 26px 28px 18px;
|
||||
}
|
||||
.card-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
.title-mark {
|
||||
width: 4px;
|
||||
height: 16px;
|
||||
border-radius: 2px;
|
||||
background: #409eff;
|
||||
}
|
||||
.saving-tip {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #909399;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* 表单:宽松间距 */
|
||||
.bill-form :deep(.el-form-item) {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.bill-form :deep(.el-form-item__label) {
|
||||
font-weight: 500;
|
||||
color: #606266;
|
||||
}
|
||||
.bill-form :deep(.el-input__wrapper),
|
||||
.bill-form :deep(.el-textarea__inner) {
|
||||
padding: 4px 12px;
|
||||
}
|
||||
.bill-form :deep(.el-select__wrapper) {
|
||||
padding: 4px 12px;
|
||||
}
|
||||
|
||||
/* 底部操作栏 */
|
||||
.bill-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,461 @@
|
||||
<template>
|
||||
<div class="bill-page">
|
||||
<!-- 顶部工具条:返回 + 单据标题 + 庄口编号 + 状态 -->
|
||||
<div class="bill-toolbar">
|
||||
<div class="toolbar-left">
|
||||
<el-button text @click="goBack">
|
||||
<el-icon><ArrowLeft /></el-icon>返回
|
||||
</el-button>
|
||||
<span class="bill-title">原料庄口</span>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<span class="bill-no">庄口编号:{{ form.code || '(自动生成)' }}</span>
|
||||
<el-tag :type="statusTag.type" size="small">{{ statusTag.label }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<el-card v-loading="loading" shadow="never" class="bill-card">
|
||||
<template #header>
|
||||
<div class="card-title"><span class="title-mark"></span>基本信息</div>
|
||||
</template>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-position="right" label-width="104px" class="bill-form">
|
||||
<el-row :gutter="36">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="庄口编号">
|
||||
<el-input v-model="form.code" :disabled="isEdit" placeholder="新增自动生成,可手动修改" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="庄口名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="如:四川凉山-2026春茧" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="庄口简称">
|
||||
<el-input v-model="form.shortName" placeholder="内部简称" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="产地">
|
||||
<el-input v-model="form.origin" placeholder="如:四川凉山" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="站点">
|
||||
<el-input v-model="form.station" placeholder="收购站点" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="季别">
|
||||
<el-input v-model="form.season" placeholder="春茧 / 夏茧 / 秋茧" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="品种">
|
||||
<el-input v-model="form.variety" placeholder="如:青松×皓月" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="原料类型" prop="cocoonType">
|
||||
<el-select v-model="form.cocoonType" placeholder="请选择原料类型" style="width: 100%">
|
||||
<el-option label="干茧" :value="0" />
|
||||
<el-option label="冷冻鲜茧" :value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="form.status" placeholder="请选择状态" style="width: 100%">
|
||||
<el-option label="收茧中" :value="0" />
|
||||
<el-option label="已入库" :value="1" />
|
||||
<el-option label="翻包中" :value="2" />
|
||||
<el-option label="已领完" :value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 质量指标 -->
|
||||
<el-card v-loading="loading" shadow="never" class="bill-card">
|
||||
<template #header>
|
||||
<div class="card-title"><span class="title-mark title-mark--green"></span>质量指标(检验/抽检数据)</div>
|
||||
</template>
|
||||
<el-form ref="qualityRef" :model="form" label-position="right" label-width="150px" class="bill-form">
|
||||
<el-row :gutter="36">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="综合等级">
|
||||
<el-select v-model="form.grade" placeholder="参照生丝等级" filterable allow-create clearable style="width: 100%">
|
||||
<el-option label="6A" value="6A" />
|
||||
<el-option label="5A" value="5A" />
|
||||
<el-option label="4A" value="4A" />
|
||||
<el-option label="3A" value="3A" />
|
||||
<el-option label="2A" value="2A" />
|
||||
<el-option label="A" value="A" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="上车茧率">
|
||||
<el-input-number v-model="form.loadingCocoonRate" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="上茧率">
|
||||
<el-input-number v-model="form.goodCocoonRate" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="茧层率">
|
||||
<el-input-number v-model="form.shellRate" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="出丝率">
|
||||
<el-input-number v-model="form.reelableRate" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="解舒率">
|
||||
<el-input-number v-model="form.reelability" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="含水量/回潮率">
|
||||
<el-input-number v-model="form.moistureRate" :precision="2" :min="0" :max="100" :step="0.5" controls-position="right" placeholder="%" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 仓储与库存 -->
|
||||
<el-card v-loading="loading" shadow="never" class="bill-card">
|
||||
<template #header>
|
||||
<div class="card-title"><span class="title-mark title-mark--orange"></span>仓储与库存</div>
|
||||
</template>
|
||||
<el-form label-position="right" label-width="104px" class="bill-form">
|
||||
<el-row :gutter="36">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="建议储存仓库">
|
||||
<el-select
|
||||
v-model="form.warehouseId"
|
||||
placeholder="请选择储存仓库"
|
||||
filterable
|
||||
clearable
|
||||
:loading="refLoading('RawMaterial_Warehouse')"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in refCache['RawMaterial_Warehouse']?.items || []"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="建议储存期(月)">
|
||||
<el-input-number v-model="form.shelfLifeMonths" :min="0" :max="36" controls-position="right" placeholder="月" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label=" ">
|
||||
<el-alert type="info" :closable="false" show-icon title="以上指标由入库/出库流水自动累加,无需手工维护" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="入库总净重">
|
||||
<el-input :model-value="fmtWeight(form.totalInWeight)" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="出库总净重">
|
||||
<el-input :model-value="fmtWeight(form.totalOutWeight)" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="当前库存">
|
||||
<el-input :model-value="fmtWeight(form.stockWeight)" disabled class="stock-input" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<div class="bill-footer">
|
||||
<el-button @click="goBack">取消</el-button>
|
||||
<el-button type="primary" :loading="saving" @click="handleSave">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { crudGet, crudAdd, crudUpdate, getRefs, genCode } from '@/api'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const TABLE = 'RawMaterial_Zhuangkou'
|
||||
|
||||
/** 单据 id(无则为新增) */
|
||||
const billId = computed(() => Number(route.params.id) || 0)
|
||||
const isEdit = computed(() => billId.value > 0)
|
||||
|
||||
// ================= 状态 =================
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
const formRef = ref()
|
||||
const form = reactive({
|
||||
code: '',
|
||||
name: '',
|
||||
shortName: '',
|
||||
origin: '',
|
||||
station: '',
|
||||
season: '',
|
||||
variety: '',
|
||||
cocoonType: 0,
|
||||
status: 0,
|
||||
grade: '',
|
||||
loadingCocoonRate: null,
|
||||
goodCocoonRate: null,
|
||||
shellRate: null,
|
||||
reelableRate: null,
|
||||
reelability: null,
|
||||
moistureRate: null,
|
||||
warehouseId: null,
|
||||
shelfLifeMonths: null,
|
||||
totalInWeight: 0,
|
||||
totalOutWeight: 0,
|
||||
stockWeight: 0
|
||||
})
|
||||
const rules = {
|
||||
name: [{ required: true, message: '请输入庄口名称', trigger: 'blur' }],
|
||||
cocoonType: [{ required: true, message: '请选择原料类型', trigger: 'change' }],
|
||||
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
|
||||
}
|
||||
|
||||
/** 关联表下拉缓存 */
|
||||
const refCache = reactive({})
|
||||
|
||||
// ================= 计算属性 =================
|
||||
const statusMap = {
|
||||
0: { label: '收茧中', type: 'info' },
|
||||
1: { label: '已入库', type: 'success' },
|
||||
2: { label: '翻包中', type: 'warning' },
|
||||
3: { label: '已领完', type: 'danger' }
|
||||
}
|
||||
const statusTag = computed(() => statusMap[form.status] || statusMap[0])
|
||||
|
||||
// ================= 关联下拉 =================
|
||||
async function loadRefs(table) {
|
||||
if (refCache[table]) return
|
||||
refCache[table] = { items: [], loading: true }
|
||||
try {
|
||||
const res = await getRefs(table, { size: 500 })
|
||||
refCache[table] = {
|
||||
items: res.code === 0 && Array.isArray(res.data) ? res.data : [],
|
||||
loading: false
|
||||
}
|
||||
} catch (e) {
|
||||
refCache[table] = { items: [], loading: false }
|
||||
}
|
||||
}
|
||||
const refLoading = (t) => !!refCache[t]?.loading
|
||||
|
||||
// ================= 格式化 =================
|
||||
const fmtWeight = (v) => (v === null || v === undefined || v === '' ? '0.00' : `${Number(v).toFixed(2)} kg`)
|
||||
|
||||
// ================= 加载单据 =================
|
||||
async function loadBill() {
|
||||
if (!isEdit.value) {
|
||||
try {
|
||||
const res = await genCode(TABLE, 'code')
|
||||
if (res.code === 0) form.code = res.data.value
|
||||
} catch (e) {
|
||||
/* 生成失败可手动填写 */
|
||||
}
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await crudGet(TABLE, billId.value)
|
||||
const d = res.data || {}
|
||||
Object.keys(form).forEach((k) => {
|
||||
if (d[k] !== undefined && d[k] !== null) form[k] = d[k]
|
||||
})
|
||||
} catch (e) {
|
||||
/* 拦截器已提示 */
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ================= 保存 =================
|
||||
async function handleSave() {
|
||||
await formRef.value.validate()
|
||||
saving.value = true
|
||||
try {
|
||||
const payload = { ...form }
|
||||
;['id', 'flag', 'addTime', 'adder', 'updateTime', 'updater'].forEach((k) => delete payload[k])
|
||||
Object.keys(payload).forEach((k) => {
|
||||
if (payload[k] === '' || payload[k] === null || payload[k] === undefined) delete payload[k]
|
||||
})
|
||||
if (isEdit.value) payload.id = billId.value
|
||||
|
||||
let savedId = billId.value
|
||||
if (isEdit.value) {
|
||||
await crudUpdate(TABLE, payload)
|
||||
ElMessage.success('修改成功')
|
||||
} else {
|
||||
const res = await crudAdd(TABLE, payload)
|
||||
savedId = res.data?.id || res.data?.Id
|
||||
if (!savedId) savedId = billId.value
|
||||
ElMessage.success('新增成功')
|
||||
}
|
||||
if (savedId && !isEdit.value) {
|
||||
await router.replace({ path: `/rawmaterial/zhuangkou-bill/${savedId}` })
|
||||
return
|
||||
}
|
||||
await loadBill()
|
||||
} catch (e) {
|
||||
/* 校验失败或拦截器已提示 */
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ================= 初始化 =================
|
||||
function goBack() {
|
||||
router.push('/rawmaterial/zhuangkou')
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all([loadRefs('RawMaterial_Warehouse')])
|
||||
await loadBill()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.bill-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding: 18px 24px 28px;
|
||||
max-width: 1360px;
|
||||
}
|
||||
|
||||
/* 顶部工具条 */
|
||||
.bill-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #1f2d3d;
|
||||
border-radius: 6px;
|
||||
padding: 12px 22px;
|
||||
color: #fff;
|
||||
}
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
}
|
||||
.toolbar-left :deep(.el-button) {
|
||||
color: #cfd8e3;
|
||||
}
|
||||
.toolbar-left :deep(.el-button:hover) {
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
.bill-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.bill-no {
|
||||
font-size: 13px;
|
||||
color: #e6ecf5;
|
||||
}
|
||||
|
||||
/* 单据卡片:宽松间距 */
|
||||
.bill-card {
|
||||
border: none;
|
||||
}
|
||||
.bill-card :deep(.el-card__header) {
|
||||
padding: 14px 24px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
background: #f7f8fa;
|
||||
}
|
||||
.bill-card :deep(.el-card__body) {
|
||||
padding: 30px 36px 8px;
|
||||
}
|
||||
.card-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
.title-mark {
|
||||
width: 4px;
|
||||
height: 16px;
|
||||
border-radius: 2px;
|
||||
background: #409eff;
|
||||
}
|
||||
.title-mark--green {
|
||||
background: #67c23a;
|
||||
}
|
||||
.title-mark--orange {
|
||||
background: #e6a23c;
|
||||
}
|
||||
|
||||
/* 表单:大行距、大控件内边距 */
|
||||
.bill-form :deep(.el-form-item) {
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
.bill-form :deep(.el-form-item__label) {
|
||||
font-weight: 500;
|
||||
color: #606266;
|
||||
}
|
||||
.bill-form :deep(.el-input__wrapper),
|
||||
.bill-form :deep(.el-textarea__inner) {
|
||||
padding: 6px 14px;
|
||||
}
|
||||
.bill-form :deep(.el-select__wrapper) {
|
||||
padding: 6px 14px;
|
||||
}
|
||||
.bill-form :deep(.el-input-number .el-input__wrapper) {
|
||||
padding-left: 14px;
|
||||
padding-right: 40px;
|
||||
}
|
||||
.stock-input :deep(.el-input__wrapper) {
|
||||
background: #f5f7fa;
|
||||
}
|
||||
.stock-input :deep(.el-input__inner) {
|
||||
font-weight: 600;
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
/* 底部操作栏 */
|
||||
.bill-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
.bill-footer :deep(.el-button) {
|
||||
padding: 10px 28px;
|
||||
}
|
||||
</style>
|
||||
@@ -11,7 +11,7 @@ F9智慧缫丝设计文档,这是一份系统开发设计文档:
|
||||
9.开发步骤:先完成PC端,再进行电视大屏端、小程序端,再进行联调内测
|
||||
# 二、缫丝厂的业务流程说明
|
||||
缫丝厂基本流程:干茧(也有可能是鲜茧)原料出库后,依次经过选茧工序筛选出上车茧,送入前缫车间开展煮茧作业,经煮茧膨润丝胶后进入自动缫丝机完成索绪、理绪、缫丝落丝,产出小䈅丝片;小䈅丝片流转至后缫工段,完成给湿、复摇返丝、编丝整理,同步开展黑板、纤度、公量等理化检验,按批次完成生丝组批定级,检验合格后入库形成成品生丝,各工序同步产出各类下脚副产品单独归集入库。
|
||||
庄口指原料蚕茧的批次化管理,原料庄口指的是A地某年某季的原料蚕茧,工艺庄口指根据生产工艺组合的生产用庄口,开始生产后,全局采用该工艺庄口ID作为贯穿系统的线索,即产生的成本、产量、排工等均已该线索为依据。
|
||||
庄口指原料蚕茧的批次化管理,原料庄口指的是A地某年某季的原料蚕茧,工艺庄口指根据生产工艺组合的生产用庄口,开始生产后,全局采用该工艺庄口作为贯穿系统的线索,即产生的成本、产量、排工等均已该线索为依据。
|
||||
缫丝生产主要的原料就是蚕茧,辅料有一些,能源主要有电力、燃气、煤、水等,产成品主要是生丝,生丝要称重,检验后打柄入库。
|
||||
产成品的副产品有条吐、下足茧、茧衣等,这些要进行入库,销售,并暂估或者卖出作为收入。
|
||||
缫丝不是按订单排产,核心围绕**工艺庄口**组织生产,不是通用离散制造的工单模式。
|
||||
|
||||
Reference in New Issue
Block a user