소스 검색

fix(s6): make IPQC list/detail readable for business users

D-02 业务状态中文化:新增单一字典 BUSINESS_STATUS_TEXT,覆盖 businessStatus
(INSPECTING/PENDING_REVIEW/APPROVED/RETURNED/PENDING_DISPOSITION/DISPOSED)
与兜底 flowStatus(NotStarted/Running/Approved)。未知枚举原样返回而非显示"未知",
避免后端新增状态时静默误导;空值 → '-'。颜色与文案分离。

D-03 判定中文化:judgementText 复用录入下拉的权威语义 0=合格 / 1=不合格,
string 与 number 两型都认。0 是合法值,全程禁用 truthy 判断。

D-05 列表新增「流程状态」列:列表接口原先不返回流程状态,故 IpqcInspectionService.GetList
左联 ado_s6_ipqc_inspection_flow_state(按 tenant_id 相等 + IsDelete=0 收口)
只读透出 business_status,DTO 增 BusinessStatus。不使用源字段 inspection_status 顶替。

D-08 空值统一:displayValue 统一 '-' 占位,保留 0 / false;清掉会吞 0 的 || '-'。
D-10 展示当前节点:使用后端已有中文 currentNodeName,终态不展示以免残留旧节点误导。
D-12 日期统一复用 /@/utils/formatTime 的 formatDate,移除本页自写 slice 实现,
     Invalid Date → '-'。
D-13 数值 0 正确显示。

未改任何业务契约:提交 payload、v-model、select option value 仍为原始枚举/数值;
状态机 / can* / assignee / 审批角色 / MDP / 数据库均未触碰。
D-06 源状态 vs 流程状态口径、D-07 审批中心 BizType、D-11 源数据乱码、D-14 类型声明
仍为 OPEN,不在本批范围。

契约测试扩展至 43 passed(Batch 1 的 26 + 本批 17)。

chore: bump version Web 2.4.370 / server 1.0.470
YY968XX 1 일 전
부모
커밋
2c1904e8bb

+ 1 - 1
Web/package.json

@@ -1,7 +1,7 @@
 {
 {
 	"name": "admin.net",
 	"name": "admin.net",
 	"type": "module",
 	"type": "module",
-	"version": "2.4.369",
+	"version": "2.4.370",
 	"packageManager": "pnpm@10.32.1",
 	"packageManager": "pnpm@10.32.1",
 	"lastBuildTime": "2026.03.15",
 	"lastBuildTime": "2026.03.15",
 	"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
 	"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",

+ 3 - 0
Web/src/views/aidop/s6/api/ipqcInspection.ts

@@ -50,6 +50,9 @@ export interface IpqcInspectionRow {
 	inspecStandardCode?: string;
 	inspecStandardCode?: string;
 	/** 源检验单 id(qms_gcjyd.id;流程 BizId 用此) */
 	/** 源检验单 id(qms_gcjyd.id;流程 BizId 用此) */
 	sourceRowId?: number;
 	sourceRowId?: number;
+	/** 流程业务状态(ado_s6_ipqc_inspection_flow_state.business_status);未发起流程时为 null。
+	 *  与源字段 inspectionStatus 语义不同(后者是源单据状态、流程不回写),不可互相替代。 */
+	businessStatus?: string | null;
 }
 }
 
 
 export function fetchIpqcInspectionList(params: any) {
 export function fetchIpqcInspectionList(params: any) {

+ 196 - 67
Web/src/views/aidop/s6/process-quality/processInspectionList.vue

@@ -18,23 +18,59 @@
 			<el-table-column prop="productModel" label="产品型号" min-width="140" show-overflow-tooltip resizable />
 			<el-table-column prop="productModel" label="产品型号" min-width="140" show-overflow-tooltip resizable />
 			<el-table-column prop="productionBatchNo" label="生产批号" min-width="140" show-overflow-tooltip resizable />
 			<el-table-column prop="productionBatchNo" label="生产批号" min-width="140" show-overflow-tooltip resizable />
 			<el-table-column prop="productionWorkOrder" label="生产工单" min-width="150" show-overflow-tooltip resizable />
 			<el-table-column prop="productionWorkOrder" label="生产工单" min-width="150" show-overflow-tooltip resizable />
-			<el-table-column prop="resultJudgement" label="结果判定" min-width="110" resizable />
-			<el-table-column prop="attachment" label="附件" min-width="100" resizable />
-			<el-table-column prop="remark" label="备注" min-width="160" show-overflow-tooltip resizable />
-			<el-table-column prop="inspector" label="检验人" min-width="120" show-overflow-tooltip resizable />
-			<el-table-column prop="processCode" label="工序编码" min-width="130" show-overflow-tooltip resizable />
-			<el-table-column prop="processName" label="工序名称" min-width="140" show-overflow-tooltip resizable />
-			<el-table-column prop="productionPerson" label="生产人员" min-width="120" show-overflow-tooltip resizable />
-			<el-table-column prop="sampleQty" label="样本量" min-width="100" resizable />
-			<el-table-column prop="formNo" label="表单编号" min-width="140" show-overflow-tooltip resizable />
-			<el-table-column prop="versionNo" label="版本号" min-width="100" resizable />
+			<el-table-column prop="businessStatus" label="流程状态" min-width="130" resizable>
+				<template #default="{ row }">
+					<el-tag v-if="row.businessStatus" size="small" :type="businessStatusTagType(row.businessStatus)">
+						{{ businessStatusText(row.businessStatus) }}
+					</el-tag>
+					<span v-else>未发起</span>
+				</template>
+			</el-table-column>
+			<el-table-column prop="resultJudgement" label="结果判定" min-width="110" resizable>
+				<template #default="{ row }">{{ judgementText(row.resultJudgement) }}</template>
+			</el-table-column>
+			<el-table-column prop="attachment" label="附件" min-width="100" resizable>
+				<template #default="{ row }">{{ displayValue(row.attachment) }}</template>
+			</el-table-column>
+			<el-table-column prop="remark" label="备注" min-width="160" show-overflow-tooltip resizable>
+				<template #default="{ row }">{{ displayValue(row.remark) }}</template>
+			</el-table-column>
+			<el-table-column prop="inspector" label="检验人" min-width="120" show-overflow-tooltip resizable>
+				<template #default="{ row }">{{ displayValue(row.inspector) }}</template>
+			</el-table-column>
+			<el-table-column prop="processCode" label="工序编码" min-width="130" show-overflow-tooltip resizable>
+				<template #default="{ row }">{{ displayValue(row.processCode) }}</template>
+			</el-table-column>
+			<el-table-column prop="processName" label="工序名称" min-width="140" show-overflow-tooltip resizable>
+				<template #default="{ row }">{{ displayValue(row.processName) }}</template>
+			</el-table-column>
+			<el-table-column prop="productionPerson" label="生产人员" min-width="120" show-overflow-tooltip resizable>
+				<template #default="{ row }">{{ displayValue(row.productionPerson) }}</template>
+			</el-table-column>
+			<el-table-column prop="sampleQty" label="样本量" min-width="100" resizable>
+				<template #default="{ row }">{{ displayValue(row.sampleQty) }}</template>
+			</el-table-column>
+			<el-table-column prop="formNo" label="表单编号" min-width="140" show-overflow-tooltip resizable>
+				<template #default="{ row }">{{ displayValue(row.formNo) }}</template>
+			</el-table-column>
+			<el-table-column prop="versionNo" label="版本号" min-width="100" resizable>
+				<template #default="{ row }">{{ displayValue(row.versionNo) }}</template>
+			</el-table-column>
 			<el-table-column prop="effectiveDate" label="生效日期" min-width="150" resizable>
 			<el-table-column prop="effectiveDate" label="生效日期" min-width="150" resizable>
 					<template #default="{ row }">{{ fmtDate(row.effectiveDate) }}</template>
 					<template #default="{ row }">{{ fmtDate(row.effectiveDate) }}</template>
 				</el-table-column>
 				</el-table-column>
-			<el-table-column prop="materialCode" label="物料编码" min-width="140" show-overflow-tooltip resizable />
-			<el-table-column prop="materialName" label="物料名称" min-width="160" show-overflow-tooltip resizable />
-			<el-table-column prop="inspecStandardVersion" label="检规版本" min-width="110" resizable />
-			<el-table-column prop="inspecStandardCode" label="检规编号" min-width="130" show-overflow-tooltip resizable />
+			<el-table-column prop="materialCode" label="物料编码" min-width="140" show-overflow-tooltip resizable>
+				<template #default="{ row }">{{ displayValue(row.materialCode) }}</template>
+			</el-table-column>
+			<el-table-column prop="materialName" label="物料名称" min-width="160" show-overflow-tooltip resizable>
+				<template #default="{ row }">{{ displayValue(row.materialName) }}</template>
+			</el-table-column>
+			<el-table-column prop="inspecStandardVersion" label="检规版本" min-width="110" resizable>
+				<template #default="{ row }">{{ displayValue(row.inspecStandardVersion) }}</template>
+			</el-table-column>
+			<el-table-column prop="inspecStandardCode" label="检规编号" min-width="130" show-overflow-tooltip resizable>
+				<template #default="{ row }">{{ displayValue(row.inspecStandardCode) }}</template>
+			</el-table-column>
 			<el-table-column label="操作" width="90" fixed="right">
 			<el-table-column label="操作" width="90" fixed="right">
 				<template #default="{ row }">
 				<template #default="{ row }">
 					<el-button link type="primary" @click="openDetail(row)">查看</el-button>
 					<el-button link type="primary" @click="openDetail(row)">查看</el-button>
@@ -63,6 +99,11 @@
 					<div class="flow-status-line">
 					<div class="flow-status-line">
 						<el-tag size="small" :type="flowTagType">{{ flowStatusText }}</el-tag>
 						<el-tag size="small" :type="flowTagType">{{ flowStatusText }}</el-tag>
 						<span class="flow-msg">{{ flowState.message }}</span>
 						<span class="flow-msg">{{ flowState.message }}</span>
+						<!-- 当前节点:仅在流程进行中且后端给出中文节点名时展示。
+						     终态时后端返回 null,此处即不显示,避免残留旧节点造成误导。 -->
+						<span v-if="!flowState.isCompleted && flowState.currentNodeName" class="flow-meta">
+							当前节点:{{ flowState.currentNodeName }}
+						</span>
 						<span v-if="flowState.inspectorName" class="flow-meta">检验员:{{ flowState.inspectorName }}</span>
 						<span v-if="flowState.inspectorName" class="flow-meta">检验员:{{ flowState.inspectorName }}</span>
 						<span v-if="flowState.supervisorName" class="flow-meta">主管:{{ flowState.supervisorName }}</span>
 						<span v-if="flowState.supervisorName" class="flow-meta">主管:{{ flowState.supervisorName }}</span>
 					</div>
 					</div>
@@ -117,57 +158,87 @@
 							<el-descriptions-item label="处置类型">{{ dispTypeText(disposition.dispositionType) }}</el-descriptions-item>
 							<el-descriptions-item label="处置类型">{{ dispTypeText(disposition.dispositionType) }}</el-descriptions-item>
 							<el-descriptions-item label="处置数量">{{ disposition.dispositionQty ?? '-' }}</el-descriptions-item>
 							<el-descriptions-item label="处置数量">{{ disposition.dispositionQty ?? '-' }}</el-descriptions-item>
 							<el-descriptions-item label="返工/让步/报废">{{ disposition.reworkQty ?? 0 }} / {{ disposition.concessionQty ?? 0 }} / {{ disposition.scrapQty ?? 0 }}</el-descriptions-item>
 							<el-descriptions-item label="返工/让步/报废">{{ disposition.reworkQty ?? 0 }} / {{ disposition.concessionQty ?? 0 }} / {{ disposition.scrapQty ?? 0 }}</el-descriptions-item>
-							<el-descriptions-item label="责任部门">{{ disposition.responsibilityDept || '-' }}</el-descriptions-item>
-							<el-descriptions-item label="责任人">{{ disposition.responsibleUser || '-' }}</el-descriptions-item>
-							<el-descriptions-item label="处置人">{{ disposition.submittedByName || '-' }}</el-descriptions-item>
-							<el-descriptions-item label="处置意见" :span="3">{{ disposition.dispositionOpinion || '-' }}</el-descriptions-item>
+							<el-descriptions-item label="责任部门">{{ displayValue(disposition.responsibilityDept) }}</el-descriptions-item>
+							<el-descriptions-item label="责任人">{{ displayValue(disposition.responsibleUser) }}</el-descriptions-item>
+							<el-descriptions-item label="处置人">{{ displayValue(disposition.submittedByName) }}</el-descriptions-item>
+							<el-descriptions-item label="处置意见" :span="3">{{ displayValue(disposition.dispositionOpinion) }}</el-descriptions-item>
 						</el-descriptions>
 						</el-descriptions>
 					</div>
 					</div>
 				</div>
 				</div>
 
 
 				<el-descriptions :column="3" border size="small">
 				<el-descriptions :column="3" border size="small">
-					<el-descriptions-item label="单据编号">{{ detail.billNo || '-' }}</el-descriptions-item>
-					<el-descriptions-item label="产品型号">{{ detail.productModel || '-' }}</el-descriptions-item>
-					<el-descriptions-item label="生产批号">{{ detail.productionBatchNo || '-' }}</el-descriptions-item>
-					<el-descriptions-item label="生产工单">{{ detail.productionWorkOrder || '-' }}</el-descriptions-item>
-					<el-descriptions-item label="结果判定">{{ detail.resultJudgement || '-' }}</el-descriptions-item>
-					<el-descriptions-item label="检验状态">{{ detail.inspectionStatus || '-' }}</el-descriptions-item>
-					<el-descriptions-item label="检验人">{{ detail.inspector || '-' }}</el-descriptions-item>
-					<el-descriptions-item label="工序编码">{{ detail.processCode || '-' }}</el-descriptions-item>
-					<el-descriptions-item label="工序名称">{{ detail.processName || '-' }}</el-descriptions-item>
-					<el-descriptions-item label="生产人员">{{ detail.productionPerson || '-' }}</el-descriptions-item>
-					<el-descriptions-item label="样本量">{{ detail.sampleQty ?? '-' }}</el-descriptions-item>
-					<el-descriptions-item label="生效日期">{{ fmtDate(detail.effectiveDate) || '-' }}</el-descriptions-item>
-					<el-descriptions-item label="物料编码">{{ detail.materialCode || '-' }}</el-descriptions-item>
-					<el-descriptions-item label="物料名称">{{ detail.materialName || '-' }}</el-descriptions-item>
-					<el-descriptions-item label="表单编号">{{ detail.formNo || '-' }}</el-descriptions-item>
-					<el-descriptions-item label="版本号">{{ detail.versionNo || '-' }}</el-descriptions-item>
-					<el-descriptions-item label="检规版本">{{ detail.inspecStandardVersion || '-' }}</el-descriptions-item>
-					<el-descriptions-item label="检规编号">{{ detail.inspecStandardCode || '-' }}</el-descriptions-item>
-					<el-descriptions-item label="备注" :span="3">{{ detail.remark || '-' }}</el-descriptions-item>
+					<el-descriptions-item label="单据编号">{{ displayValue(detail.billNo) }}</el-descriptions-item>
+					<el-descriptions-item label="产品型号">{{ displayValue(detail.productModel) }}</el-descriptions-item>
+					<el-descriptions-item label="生产批号">{{ displayValue(detail.productionBatchNo) }}</el-descriptions-item>
+					<el-descriptions-item label="生产工单">{{ displayValue(detail.productionWorkOrder) }}</el-descriptions-item>
+					<el-descriptions-item label="结果判定">{{ judgementText(detail.resultJudgement) }}</el-descriptions-item>
+					<el-descriptions-item label="检验状态">{{ displayValue(detail.inspectionStatus) }}</el-descriptions-item>
+					<el-descriptions-item label="检验人">{{ displayValue(detail.inspector) }}</el-descriptions-item>
+					<el-descriptions-item label="工序编码">{{ displayValue(detail.processCode) }}</el-descriptions-item>
+					<el-descriptions-item label="工序名称">{{ displayValue(detail.processName) }}</el-descriptions-item>
+					<el-descriptions-item label="生产人员">{{ displayValue(detail.productionPerson) }}</el-descriptions-item>
+					<el-descriptions-item label="样本量">{{ displayValue(detail.sampleQty) }}</el-descriptions-item>
+					<el-descriptions-item label="生效日期">{{ fmtDate(detail.effectiveDate) }}</el-descriptions-item>
+					<el-descriptions-item label="物料编码">{{ displayValue(detail.materialCode) }}</el-descriptions-item>
+					<el-descriptions-item label="物料名称">{{ displayValue(detail.materialName) }}</el-descriptions-item>
+					<el-descriptions-item label="表单编号">{{ displayValue(detail.formNo) }}</el-descriptions-item>
+					<el-descriptions-item label="版本号">{{ displayValue(detail.versionNo) }}</el-descriptions-item>
+					<el-descriptions-item label="检规版本">{{ displayValue(detail.inspecStandardVersion) }}</el-descriptions-item>
+					<el-descriptions-item label="检规编号">{{ displayValue(detail.inspecStandardCode) }}</el-descriptions-item>
+					<el-descriptions-item label="备注" :span="3">{{ displayValue(detail.remark) }}</el-descriptions-item>
 				</el-descriptions>
 				</el-descriptions>
 
 
 				<div class="detail-lines-title">检验明细</div>
 				<div class="detail-lines-title">检验明细</div>
 				<el-table :data="detailLines" border stripe size="small">
 				<el-table :data="detailLines" border stripe size="small">
 					<el-table-column type="index" label="#" width="50" />
 					<el-table-column type="index" label="#" width="50" />
-					<el-table-column prop="inspectionItem" label="检验项目" min-width="140" show-overflow-tooltip />
-					<el-table-column prop="inspectionBasis" label="检验依据" min-width="140" show-overflow-tooltip />
-					<el-table-column prop="processCode" label="工序编码" min-width="110" show-overflow-tooltip />
-					<el-table-column prop="processName" label="工序名称" min-width="120" show-overflow-tooltip />
-					<el-table-column prop="inspectionMethod" label="检验方法" min-width="120" show-overflow-tooltip />
-					<el-table-column prop="technicalStandard" label="技术标准" min-width="120" show-overflow-tooltip />
-					<el-table-column prop="upperLimit" label="上限" min-width="90" />
-					<el-table-column prop="lowerLimit" label="下限" min-width="90" />
-					<el-table-column prop="measurementInstrument" label="测量仪器" min-width="120" show-overflow-tooltip />
-					<el-table-column prop="sampleQty" label="样本量" min-width="80" />
-					<el-table-column prop="inspectionQty" label="检验数量" min-width="90" />
-					<el-table-column prop="unqualifiedQty" label="不合格数" min-width="90" />
-					<el-table-column prop="resultJudgement" label="判定" min-width="80" />
-					<el-table-column prop="inspector" label="检验人" min-width="100" show-overflow-tooltip />
+					<el-table-column prop="inspectionItem" label="检验项目" min-width="140" show-overflow-tooltip>
+						<template #default="{ row }">{{ displayValue(row.inspectionItem) }}</template>
+					</el-table-column>
+					<el-table-column prop="inspectionBasis" label="检验依据" min-width="140" show-overflow-tooltip>
+						<template #default="{ row }">{{ displayValue(row.inspectionBasis) }}</template>
+					</el-table-column>
+					<el-table-column prop="processCode" label="工序编码" min-width="110" show-overflow-tooltip>
+						<template #default="{ row }">{{ displayValue(row.processCode) }}</template>
+					</el-table-column>
+					<el-table-column prop="processName" label="工序名称" min-width="120" show-overflow-tooltip>
+						<template #default="{ row }">{{ displayValue(row.processName) }}</template>
+					</el-table-column>
+					<el-table-column prop="inspectionMethod" label="检验方法" min-width="120" show-overflow-tooltip>
+						<template #default="{ row }">{{ displayValue(row.inspectionMethod) }}</template>
+					</el-table-column>
+					<el-table-column prop="technicalStandard" label="技术标准" min-width="120" show-overflow-tooltip>
+						<template #default="{ row }">{{ displayValue(row.technicalStandard) }}</template>
+					</el-table-column>
+					<el-table-column prop="upperLimit" label="上限" min-width="90">
+						<template #default="{ row }">{{ displayValue(row.upperLimit) }}</template>
+					</el-table-column>
+					<el-table-column prop="lowerLimit" label="下限" min-width="90">
+						<template #default="{ row }">{{ displayValue(row.lowerLimit) }}</template>
+					</el-table-column>
+					<el-table-column prop="measurementInstrument" label="测量仪器" min-width="120" show-overflow-tooltip>
+						<template #default="{ row }">{{ displayValue(row.measurementInstrument) }}</template>
+					</el-table-column>
+					<el-table-column prop="sampleQty" label="样本量" min-width="80">
+						<template #default="{ row }">{{ displayValue(row.sampleQty) }}</template>
+					</el-table-column>
+					<el-table-column prop="inspectionQty" label="检验数量" min-width="90">
+						<template #default="{ row }">{{ displayValue(row.inspectionQty) }}</template>
+					</el-table-column>
+					<el-table-column prop="unqualifiedQty" label="不合格数" min-width="90">
+						<template #default="{ row }">{{ displayValue(row.unqualifiedQty) }}</template>
+					</el-table-column>
+					<el-table-column prop="resultJudgement" label="判定" min-width="80">
+						<template #default="{ row }">{{ judgementText(row.resultJudgement) }}</template>
+					</el-table-column>
+					<el-table-column prop="inspector" label="检验人" min-width="100" show-overflow-tooltip>
+						<template #default="{ row }">{{ displayValue(row.inspector) }}</template>
+					</el-table-column>
 					<el-table-column prop="inspectionTime" label="检验时间" min-width="150">
 					<el-table-column prop="inspectionTime" label="检验时间" min-width="150">
 						<template #default="{ row }">{{ fmtDateTime(row.inspectionTime) }}</template>
 						<template #default="{ row }">{{ fmtDateTime(row.inspectionTime) }}</template>
 					</el-table-column>
 					</el-table-column>
-					<el-table-column prop="remark" label="备注" min-width="140" show-overflow-tooltip />
+					<el-table-column prop="remark" label="备注" min-width="140" show-overflow-tooltip>
+						<template #default="{ row }">{{ displayValue(row.remark) }}</template>
+					</el-table-column>
 					<template #empty>
 					<template #empty>
 						<el-empty description="暂无明细" />
 						<el-empty description="暂无明细" />
 					</template>
 					</template>
@@ -182,6 +253,7 @@ import { computed, onActivated, onMounted, reactive, ref } from 'vue';
 import { useRoute } from 'vue-router';
 import { useRoute } from 'vue-router';
 import { ElMessage, ElMessageBox } from 'element-plus';
 import { ElMessage, ElMessageBox } from 'element-plus';
 import AidopDemoShell from '/@/views/aidop/components/AidopDemoShell.vue';
 import AidopDemoShell from '/@/views/aidop/components/AidopDemoShell.vue';
+import { formatDate } from '/@/utils/formatTime';
 import {
 import {
 	fetchIpqcInspectionList,
 	fetchIpqcInspectionList,
 	fetchIpqcInspectionDetail,
 	fetchIpqcInspectionDetail,
@@ -251,14 +323,76 @@ function onSizeChange() {
 	loadList();
 	loadList();
 }
 }
 
 
-function fmtDate(v?: string) {
-	if (!v) return '';
-	return v.length >= 10 ? v.slice(0, 10) : v;
+// ───────────────────────── 展示层 formatter ─────────────────────────
+// 只影响渲染。提交 payload、v-model、select option value 一律保持后端原始枚举/数值,
+// 严禁把中文写回业务契约。
+
+/** 空值统一占位。0 / false 是合法业务值必须原样显示,故不可用 `||`。 */
+function displayValue(v: unknown) {
+	if (v === null || v === undefined) return '-';
+	if (typeof v === 'string' && v.trim() === '') return '-';
+	return v as string | number | boolean;
+}
+
+/** 解析后端日期串(支持 'YYYY-MM-DD HH:mm:ss' 与 ISO-T),不可解析返回 null。 */
+function toDate(v?: string | null) {
+	if (!v) return null;
+	const d = new Date(String(v).replace(' ', 'T'));
+	return isNaN(d.getTime()) ? null : d;
 }
 }
 
 
-function fmtDateTime(v?: string) {
-	if (!v) return '';
-	return v.length > 19 ? v.slice(0, 19).replace('T', ' ') : v.replace('T', ' ');
+function fmtDate(v?: string | null) {
+	const d = toDate(v);
+	return d ? formatDate(d, 'YYYY-mm-dd') : '-';
+}
+
+function fmtDateTime(v?: string | null) {
+	const d = toDate(v);
+	return d ? formatDate(d, 'YYYY-mm-dd HH:MM:SS') : '-';
+}
+
+/**
+ * 流程业务状态字典。key 同时覆盖 businessStatus(扩展表)与 flowStatus(ApprovalFlow 实例)两套原始值,
+ * 二者大小写不同、互不冲突。**未知枚举原样返回**——后端将来新增状态时宁可露出原值,也不静默显示"未知"。
+ */
+const BUSINESS_STATUS_TEXT: Record<string, string> = {
+	// businessStatus(ado_s6_ipqc_inspection_flow_state.business_status)
+	INSPECTING: '检验中',
+	PENDING_REVIEW: '待主管审核',
+	APPROVED: '已通过',
+	RETURNED: '已退回',
+	PENDING_DISPOSITION: '待质量处置',
+	DISPOSED: '已处置',
+	// flowStatus(尚无 businessStatus 时的兜底来源)
+	NotStarted: '待检验',
+	Running: '流程进行中',
+	Approved: '已通过',
+};
+
+function businessStatusText(v?: string | null) {
+	if (v === null || v === undefined || v === '') return '-';
+	return BUSINESS_STATUS_TEXT[v] ?? v;
+}
+
+/** 状态标签颜色(与文案分离,沿用页面既有色彩语义)。 */
+function businessStatusTagType(v?: string | null) {
+	if (v === 'APPROVED' || v === 'DISPOSED' || v === 'Approved') return 'success';
+	if (v === 'RETURNED') return 'warning';
+	if (v === 'PENDING_DISPOSITION') return 'danger';
+	if (v === 'PENDING_REVIEW') return 'primary';
+	return 'info';
+}
+
+/**
+ * 结果判定字典,与录入下拉「合格(0) / 不合格(1)」同源。
+ * 后端 result_judgement 为字符串字典码,前端表单为 number,故两型都要认。
+ * **0 是合法值**,禁止用 truthy 判断。
+ */
+function judgementText(v?: string | number | null) {
+	if (v === 0 || v === '0') return '合格';
+	if (v === 1 || v === '1') return '不合格';
+	if (v === null || v === undefined || (typeof v === 'string' && v.trim() === '')) return '-';
+	return String(v);
 }
 }
 
 
 // 详情 drawer:头 + 明细(确定子集列)+ 检验员/主管流程区。
 // 详情 drawer:头 + 明细(确定子集列)+ 检验员/主管流程区。
@@ -311,15 +445,10 @@ const submitForm = reactive<{ jgpd: number | null; jyhgsl: number; jybhgsl: numb
 	bz: '',
 	bz: '',
 });
 });
 
 
-const flowStatusText = computed(() => flowState.value.businessStatus || flowState.value.flowStatus || '未发起');
-const flowTagType = computed(() => {
-	const s = flowState.value.businessStatus;
-	if (s === 'APPROVED' || s === 'DISPOSED') return 'success';
-	if (s === 'RETURNED') return 'warning';
-	if (s === 'PENDING_DISPOSITION') return 'danger';
-	if (s === 'PENDING_REVIEW') return 'primary';
-	return 'info';
-});
+/** 原始状态值(businessStatus 优先,回落 flowStatus)——只用于取文案/颜色,不直接上屏。 */
+const flowStatusRaw = computed(() => flowState.value.businessStatus || flowState.value.flowStatus || '');
+const flowStatusText = computed(() => (flowStatusRaw.value ? businessStatusText(flowStatusRaw.value) : '未发起'));
+const flowTagType = computed(() => businessStatusTagType(flowStatusRaw.value));
 
 
 // 主管整批判定表单 + N3 质量处置表单 + 已有处置
 // 主管整批判定表单 + N3 质量处置表单 + 已有处置
 const supervisorForm = reactive<{ jgpd: number | null }>({ jgpd: null });
 const supervisorForm = reactive<{ jgpd: number | null }>({ jgpd: null });

+ 3 - 3
server/Admin.NET.Web.Entry/Admin.NET.Web.Entry.csproj

@@ -11,9 +11,9 @@
     <GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
     <GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
     <Copyright>Admin.NET</Copyright>
     <Copyright>Admin.NET</Copyright>
     <Description>Admin.NET 通用权限开发平台</Description>
     <Description>Admin.NET 通用权限开发平台</Description>
-    <AssemblyVersion>1.0.469</AssemblyVersion>
-    <FileVersion>1.0.469</FileVersion>
-    <Version>1.0.469</Version>
+    <AssemblyVersion>1.0.470</AssemblyVersion>
+    <FileVersion>1.0.470</FileVersion>
+    <Version>1.0.470</Version>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>

+ 120 - 0
server/Plugins/Admin.NET.Plugin.AiDOP.Tests/S6/Manufacturing/S6IpqcDisplayContractTests.cs

@@ -22,6 +22,12 @@ public class S6IpqcDisplayContractTests
     private static string JsonSql() => File.ReadAllText(FindFile(
     private static string JsonSql() => File.ReadAllText(FindFile(
         "server", "Plugins", "Admin.NET.Plugin.AiDOP", "DataPlatform", "MdpJsonSql.cs"));
         "server", "Plugins", "Admin.NET.Plugin.AiDOP", "DataPlatform", "MdpJsonSql.cs"));
 
 
+    private static string Vue() => File.ReadAllText(FindFile(
+        "Web", "src", "views", "aidop", "s6", "process-quality", "processInspectionList.vue"));
+
+    private static string ListService() => File.ReadAllText(FindFile(
+        "server", "Plugins", "Admin.NET.Plugin.AiDOP", "Manufacturing", "IpqcInspectionService.cs"));
+
     /// <summary>head transform 的每个字符串列都必须走 MdpJsonSql.Str。</summary>
     /// <summary>head transform 的每个字符串列都必须走 MdpJsonSql.Str。</summary>
     [Theory]
     [Theory]
     [InlineData("djbh")]
     [InlineData("djbh")]
@@ -135,6 +141,120 @@ public class S6IpqcDisplayContractTests
         Assert.Contains("var dispositionRequired = input.Qualified ? 0 : 1;", s);
         Assert.Contains("var dispositionRequired = input.Qualified ? 0 : 1;", s);
     }
     }
 
 
+    // ───────────────────────── Batch 2 · UI Readability ─────────────────────────
+
+    /// <summary>六个业务状态 + 兜底 flowStatus 都必须有中文映射。</summary>
+    [Theory]
+    [InlineData("INSPECTING", "检验中")]
+    [InlineData("PENDING_REVIEW", "待主管审核")]
+    [InlineData("APPROVED", "已通过")]
+    [InlineData("RETURNED", "已退回")]
+    [InlineData("PENDING_DISPOSITION", "待质量处置")]
+    [InlineData("DISPOSED", "已处置")]
+    [InlineData("NotStarted", "待检验")]
+    [InlineData("Running", "流程进行中")]
+    public void BusinessStatus_HasChineseMapping(string raw, string zh)
+    {
+        Assert.Contains($"{raw}: '{zh}'", Vue());
+    }
+
+    /// <summary>未知状态原样返回,不静默显示"未知";空值 → '-'。</summary>
+    [Fact]
+    public void BusinessStatusText_UnknownFallsBackToRawValue()
+    {
+        var v = Vue();
+        Assert.Contains("return BUSINESS_STATUS_TEXT[v] ?? v;", v);
+        Assert.Contains("if (v === null || v === undefined || v === '') return '-';", v);
+    }
+
+    /// <summary>判定 0/1 中文化,且 0 不得被 truthy 判断吞掉(string 与 number 两型都认)。</summary>
+    [Fact]
+    public void JudgementText_MapsZeroAndOne_WithoutTruthyCheck()
+    {
+        var v = Vue();
+        Assert.Contains("if (v === 0 || v === '0') return '合格';", v);
+        Assert.Contains("if (v === 1 || v === '1') return '不合格';", v);
+        Assert.DoesNotContain("resultJudgement || '-'", v);
+    }
+
+    /// <summary>空值占位统一走 displayValue;0 / false 是合法值必须原样显示。</summary>
+    [Fact]
+    public void DisplayValue_PreservesZeroAndFalse()
+    {
+        var v = Vue();
+        Assert.Contains("function displayValue(v: unknown)", v);
+        Assert.Contains("if (v === null || v === undefined) return '-';", v);
+        // 不得再用 || '-' 处理可能为 0 的字段;仅允许 dispTypeText 的字符串枚举兜底
+        Assert.Equal(1, v.Split("|| '-'").Length - 1);
+        Assert.Contains("REWORK: '返工'", v);
+    }
+
+    /// <summary>列表必须有「流程状态」列,且绑定 businessStatus 而非源字段 inspectionStatus。</summary>
+    [Fact]
+    public void List_HasWorkflowStatusColumn_BoundToBusinessStatus()
+    {
+        var v = Vue();
+        Assert.Contains("label=\"流程状态\"", v);
+        Assert.Contains("businessStatusText(row.businessStatus)", v);
+        Assert.DoesNotContain("businessStatusText(row.inspectionStatus)", v);
+    }
+
+    /// <summary>模板不得直接插值原始业务状态枚举。</summary>
+    [Fact]
+    public void Template_DoesNotRenderRawBusinessStatus()
+    {
+        var v = Vue();
+        Assert.DoesNotContain("{{ row.businessStatus }}", v);
+        Assert.DoesNotContain("{{ flowState.businessStatus }}", v);
+        Assert.DoesNotContain("{{ detail.resultJudgement }}", v);
+    }
+
+    /// <summary>currentNodeName 被使用,且终态不展示(避免残留旧节点误导)。</summary>
+    [Fact]
+    public void CurrentNodeName_IsRendered_AndHiddenWhenCompleted()
+    {
+        var v = Vue();
+        Assert.Contains("flowState.currentNodeName", v);
+        Assert.Contains("!flowState.isCompleted && flowState.currentNodeName", v);
+    }
+
+    /// <summary>日期复用项目公共 formatter,不再自写 slice 逻辑;空值 → '-'。</summary>
+    [Fact]
+    public void Dates_ReuseSharedFormatter_AndPlaceholderOnEmpty()
+    {
+        var v = Vue();
+        Assert.Contains("import { formatDate } from '/@/utils/formatTime';", v);
+        Assert.Contains("formatDate(d, 'YYYY-mm-dd')", v);
+        Assert.Contains("formatDate(d, 'YYYY-mm-dd HH:MM:SS')", v);
+        Assert.DoesNotContain("v.slice(0, 10)", v);          // 旧的自写实现已移除
+        Assert.Contains("return isNaN(d.getTime()) ? null : d;", v);  // Invalid Date → '-'
+    }
+
+    /// <summary>提交契约不得被中文化污染:payload / v-model / option value 仍为原始值。</summary>
+    [Fact]
+    public void SubmitContract_KeepsRawValues()
+    {
+        var v = Vue();
+        Assert.Contains("label=\"合格(0)\" :value=\"0\"", v);
+        Assert.Contains("label=\"不合格(1)\" :value=\"1\"", v);
+        Assert.Contains("value=\"REWORK\"", v);
+        Assert.DoesNotContain(":value=\"'合格'\"", v);
+    }
+
+    /// <summary>列表流程状态来自 flow_state 左联,且租户与软删都收口。</summary>
+    [Fact]
+    public void ListSql_JoinsFlowState_TenantScoped()
+    {
+        var s = ListService();
+        Assert.Contains("f.business_status          AS BusinessStatus", s);
+        Assert.Contains("LEFT JOIN ado_s6_ipqc_inspection_flow_state f", s);
+        Assert.Contains("ON f.inspection_id = CAST(m.source_row_id AS SIGNED)", s);
+        Assert.Contains("AND f.tenant_id = m.tenant_id", s);
+        Assert.Contains("AND f.IsDelete = 0", s);
+        // 列表仍强制按登录租户过滤
+        Assert.Contains("m.tenant_id = @TenantId", s);
+    }
+
     private static string FindFile(params string[] parts)
     private static string FindFile(params string[] parts)
     {
     {
         var dir = new DirectoryInfo(AppContext.BaseDirectory);
         var dir = new DirectoryInfo(AppContext.BaseDirectory);

+ 7 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Manufacturing/Dto/IpqcInspectionDto.cs

@@ -93,6 +93,13 @@ public class IpqcInspectionListRow
 
 
     /// <summary>源检验单 id(qms_gcjyd.id = mdp_std.source_row_id;流程 BizId 用此)</summary>
     /// <summary>源检验单 id(qms_gcjyd.id = mdp_std.source_row_id;流程 BizId 用此)</summary>
     public long SourceRowId { get; set; }
     public long SourceRowId { get; set; }
+
+    /// <summary>
+    /// 流程业务状态(ado_s6_ipqc_inspection_flow_state.business_status,只读透出)。
+    /// 尚未发起流程时为 null。<b>与源字段 inspection_status 语义不同</b>:后者是源单据自带状态、
+    /// 流程推进刻意不回写,两者不可互相替代。
+    /// </summary>
+    public string? BusinessStatus { get; set; }
 }
 }
 
 
 /// <summary>
 /// <summary>

+ 6 - 1
server/Plugins/Admin.NET.Plugin.AiDOP/Manufacturing/IpqcInspectionService.cs

@@ -87,8 +87,13 @@ public class IpqcInspectionService : IDynamicApiController, ITransient
                 m.material_name            AS MaterialName,
                 m.material_name            AS MaterialName,
                 m.inspec_standard_version  AS InspecStandardVersion,
                 m.inspec_standard_version  AS InspecStandardVersion,
                 m.inspec_standard_code     AS InspecStandardCode,
                 m.inspec_standard_code     AS InspecStandardCode,
-                CAST(m.source_row_id AS SIGNED) AS SourceRowId
+                CAST(m.source_row_id AS SIGNED) AS SourceRowId,
+                f.business_status          AS BusinessStatus
             FROM mdp_std_ipqc_inspection m
             FROM mdp_std_ipqc_inspection m
+            LEFT JOIN ado_s6_ipqc_inspection_flow_state f
+                   ON f.inspection_id = CAST(m.source_row_id AS SIGNED)
+                  AND f.tenant_id = m.tenant_id
+                  AND f.IsDelete = 0
             WHERE {whereSql}
             WHERE {whereSql}
             ORDER BY {BuildOrderBy(input.SortField, input.SortOrder)}
             ORDER BY {BuildOrderBy(input.SortField, input.SortOrder)}
             LIMIT {pageSize} OFFSET {offset}
             LIMIT {pageSize} OFFSET {offset}