瀏覽代碼

feat(s5): add iqc inspbill readonly list

- 新增 api/S5IqcInspBill/list:qms_qcp_inspbill 单表直读
- 复用 FUNC-S5-001 iqcTaskList.vue(副标题"基于来料检验单 qms_qcp_inspbill")
- 9 查询项、15 列;pd/clfs/FBILLSTATUS raw 展示
- tenantId/FORGID 接收但忽略(表无 tenant_id、FORGID 全 NULL)
- 无 SQL、无菜单、无功能码、无详情、无流程、无通知、无 SQE 表、无库存/入库
- 冒烟 total=5,前端渲染 5 行 UAT;qms 三表与 ApprovalFlow 表前后不变
- 版本 server 1.0.233 / Web 2.4.229
YY968XX 3 天之前
父節點
當前提交
f205144f0b

+ 1 - 1
Web/package.json

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

+ 60 - 0
Web/src/views/aidop/s5/api/iqcInspBill.ts

@@ -0,0 +1,60 @@
+import service from '/@/utils/request';
+import { withAidopTenantParams } from '../../api/aidopTenant';
+
+export interface IqcInspBillPaged<T> {
+	total: number;
+	page: number;
+	pageSize: number;
+	list: T[];
+}
+
+// ── S5 来料检验单列表(只读,数据源 aidopdev.qms_qcp_inspbill 单表直读)──
+// 来料检验流程主入口。pd/clfs/FBILLSTATUS 字典未闭合 → raw 展示。表无 tenant_id / FORGID 全 NULL → 不过滤。
+export interface IqcInspBillRow {
+	/** 主键 id */
+	id?: number;
+	/** 检验单号 FBILLNO */
+	billNo?: string;
+	/** 来源单号 lydjbh */
+	sourceBillNo?: string;
+	/** 物料编码 FMATERIALCFG */
+	materialCode?: string;
+	/** 物料名称 wlmc */
+	materialName?: string;
+	/** 规格型号 ggxh */
+	spec?: string;
+	/** 供应商 gysmc */
+	supplierName?: string;
+	/** 批次 pch */
+	lotNo?: string;
+	/** 报检数量 FRINSQTY */
+	reportQty?: number;
+	/** 检验数量 jysl */
+	inspectQty?: number;
+	/** 合格数量 dhsl */
+	qualifiedQty?: number;
+	/** 不合格数量 bhgsl */
+	unqualifiedQty?: number;
+	/** 判定 pd(raw) */
+	judgement?: number;
+	/** 处理方式 clfs(raw) */
+	processMethod?: number;
+	/** 状态 FBILLSTATUS(raw) */
+	billStatus?: string;
+	/** 检验员 FINSPECTORID */
+	inspector?: string;
+	/** 检验开始时间 FINSPESTARTDATE */
+	inspectStartDate?: string;
+	/** 检验结束时间 FINSPEENDDATE */
+	inspectEndDate?: string;
+	/** 创建时间 FCREATETIME */
+	createTime?: string;
+	/** 组织 FORGID */
+	orgId?: number;
+}
+
+export function fetchIqcInspBillList(params: any) {
+	return service
+		.get<IqcInspBillPaged<IqcInspBillRow>>('/api/S5IqcInspBill/list', { params: withAidopTenantParams(params) })
+		.then((r) => r.data);
+}

+ 105 - 78
Web/src/views/aidop/s5/iqc/iqcTaskList.vue

@@ -1,51 +1,41 @@
 <template>
-	<AidopDemoShell :title="pageTitle" subtitle="只读列表">
-		<div class="filter-hint">查询条件:未检验;</div>
+	<AidopDemoShell :title="pageTitle" subtitle="基于来料检验单 qms_qcp_inspbill(只读列表)">
 		<el-form :inline="true" :model="query" class="mb12" @submit.prevent>
-			<el-form-item label="单据编号">
+			<el-form-item label="检验单号">
 				<el-input v-model="query.billNo" clearable style="width: 200px" />
 			</el-form-item>
+			<el-form-item label="来源单号">
+				<el-input v-model="query.sourceBillNo" clearable style="width: 200px" />
+			</el-form-item>
 			<el-form-item label="物料编码">
 				<el-input v-model="query.materialCode" clearable style="width: 200px" />
 			</el-form-item>
 			<el-form-item label="物料名称">
 				<el-input v-model="query.materialName" clearable style="width: 200px" />
 			</el-form-item>
-			<el-form-item label="申请时间-始">
-				<el-date-picker v-model="query.applyTimeStart" type="date" value-format="YYYY-MM-DD" style="width: 180px" />
-			</el-form-item>
-			<el-form-item label="申请时间-终">
-				<el-date-picker v-model="query.applyTimeEnd" type="date" value-format="YYYY-MM-DD" style="width: 180px" />
-			</el-form-item>
-			<el-form-item label="检验进度">
-				<el-input v-model="query.inspectProgress" clearable style="width: 200px" />
-			</el-form-item>
-			<el-form-item label="检验优先级">
-				<el-select v-model="query.priority" placeholder="请选择" clearable style="width: 200px" />
-			</el-form-item>
-			<el-form-item label="检验负责人">
-				<el-select v-model="query.inspector" placeholder="请选择" clearable style="width: 200px" />
-			</el-form-item>
-			<el-form-item label="供应商批号">
-				<el-input v-model="query.supplierBatchNo" clearable style="width: 200px" />
-			</el-form-item>
-			<el-form-item label="供应商简称">
-				<el-input v-model="query.supplierShortName" clearable style="width: 200px" />
-			</el-form-item>
-			<el-form-item label="瑞奇批号">
-				<el-input v-model="query.rqBatchNo" clearable style="width: 200px" />
+			<el-form-item label="供应商">
+				<el-input v-model="query.supplierName" clearable style="width: 200px" />
 			</el-form-item>
-			<el-form-item label="检验开始时间-始">
-				<el-date-picker v-model="query.inspectStartFrom" type="date" value-format="YYYY-MM-DD" style="width: 180px" />
+			<el-form-item label="批次">
+				<el-input v-model="query.lotNo" clearable style="width: 200px" />
 			</el-form-item>
-			<el-form-item label="检验开始时间-终">
-				<el-date-picker v-model="query.inspectStartTo" type="date" value-format="YYYY-MM-DD" style="width: 180px" />
+			<el-form-item label="检验状态">
+				<el-input v-model="query.billStatus" clearable style="width: 160px" />
 			</el-form-item>
-			<el-form-item label="检验完成时间-始">
-				<el-date-picker v-model="query.inspectFinishFrom" type="date" value-format="YYYY-MM-DD" style="width: 180px" />
+			<el-form-item label="检验员">
+				<el-input v-model="query.inspector" clearable style="width: 160px" />
 			</el-form-item>
-			<el-form-item label="检验完成时间-终">
-				<el-date-picker v-model="query.inspectFinishTo" type="date" value-format="YYYY-MM-DD" style="width: 180px" />
+			<el-form-item label="检验日期">
+				<el-date-picker
+					v-model="inspectDateRange"
+					type="daterange"
+					value-format="YYYY-MM-DD"
+					range-separator="-"
+					start-placeholder="开始日期"
+					end-placeholder="结束日期"
+					unlink-panels
+					style="width: 260px"
+				/>
 			</el-form-item>
 			<el-form-item>
 				<el-button type="primary" @click="doSearch">查询</el-button>
@@ -54,22 +44,23 @@
 		</el-form>
 
 		<el-table :data="rows" row-key="id" v-loading="loading" border stripe>
-			<el-table-column prop="supplierBatchNo" label="供应商批号" min-width="140" show-overflow-tooltip resizable />
-			<el-table-column prop="rqBatchNo" label="瑞奇批号" min-width="140" show-overflow-tooltip resizable />
-			<el-table-column prop="supplierCode" label="供应商编号" min-width="140" show-overflow-tooltip resizable />
-			<el-table-column prop="supplierShortName" label="供应商简称" min-width="140" show-overflow-tooltip resizable />
-			<el-table-column prop="warehouse" label="仓库" min-width="120" show-overflow-tooltip resizable />
-			<el-table-column prop="shelf" label="货架" min-width="120" show-overflow-tooltip resizable />
-			<el-table-column prop="materialCode" label="物料编码" min-width="140" show-overflow-tooltip resizable />
+			<el-table-column prop="billNo" label="检验单号" min-width="170" show-overflow-tooltip resizable />
+			<el-table-column prop="sourceBillNo" label="来源单号" min-width="170" show-overflow-tooltip resizable />
+			<el-table-column prop="materialCode" label="物料编码" min-width="130" show-overflow-tooltip resizable />
 			<el-table-column prop="materialName" label="物料名称" min-width="160" show-overflow-tooltip resizable />
 			<el-table-column prop="spec" label="规格型号" min-width="140" show-overflow-tooltip resizable />
-			<el-table-column prop="inspectQty" label="报检数量" min-width="110" resizable />
-			<el-table-column prop="unit" label="单位" min-width="90" resizable />
-			<el-table-column prop="inspectProgress" label="检验进度" min-width="110" resizable />
-			<el-table-column prop="priority" label="检验优先级" min-width="110" resizable />
-			<el-table-column prop="inspector" label="检验负责人" min-width="120" show-overflow-tooltip resizable />
-			<el-table-column prop="inspectStartTime" label="检验开始时间" min-width="170" resizable />
-			<el-table-column prop="inspectFinishTime" label="检验完成时间" min-width="170" resizable />
+			<el-table-column prop="supplierName" label="供应商" min-width="150" show-overflow-tooltip resizable />
+			<el-table-column prop="lotNo" label="批次" min-width="130" show-overflow-tooltip resizable />
+			<el-table-column prop="reportQty" label="报检数量" min-width="100" align="right" resizable />
+			<el-table-column prop="inspectQty" label="检验数量" min-width="100" align="right" resizable />
+			<el-table-column prop="qualifiedQty" label="合格数量" min-width="100" align="right" resizable />
+			<el-table-column prop="unqualifiedQty" label="不合格数量" min-width="110" align="right" resizable />
+			<el-table-column prop="judgement" label="判定" min-width="90" resizable />
+			<el-table-column prop="processMethod" label="处理方式" min-width="100" resizable />
+			<el-table-column prop="billStatus" label="状态" min-width="90" resizable />
+			<el-table-column prop="inspectStartDate" label="检验开始时间" min-width="150" resizable>
+				<template #default="{ row }">{{ fmtDate(row.inspectStartDate) }}</template>
+			</el-table-column>
 			<template #empty>
 				<el-empty description="暂无数据" />
 			</template>
@@ -82,73 +73,109 @@
 				:total="total"
 				:page-sizes="[10, 20, 50]"
 				layout="total, sizes, prev, pager, next"
+				@current-change="loadList"
+				@size-change="loadList"
 			/>
 		</div>
 	</AidopDemoShell>
 </template>
 
 <script setup lang="ts" name="aidopS5IqcTaskList">
-import { computed, reactive, ref } from 'vue';
+import { computed, onActivated, onMounted, reactive, ref } from 'vue';
 import { useRoute } from 'vue-router';
+import { ElMessage } from 'element-plus';
 import AidopDemoShell from '/@/views/aidop/components/AidopDemoShell.vue';
+import { fetchIqcInspBillList, type IqcInspBillRow } from '../api/iqcInspBill';
 
 const route = useRoute();
 const pageTitle = computed(() => (route.meta?.title as string) || '来料检验任务列表');
 
+// S5 来料检验单只读列表(S5-IQC-INSPBILL-READONLY-LIST-1)。
+// 数据源 aidopdev.qms_qcp_inspbill 单表直读,后端 api/S5IqcInspBill。作后续检验流程主入口。
+// 只读:无认领/调配/检验完成/结果录入/主管审核/SQE处置/仓储通知/入库/详情/新增/编辑/删除/导出。
+// 表无 tenant_id、FORGID 全 NULL:不做租户/组织过滤。pd/clfs/FBILLSTATUS 字典未闭合 → raw 展示。
 const query = reactive({
 	billNo: '',
+	sourceBillNo: '',
 	materialCode: '',
 	materialName: '',
-	applyTimeStart: '',
-	applyTimeEnd: '',
-	inspectProgress: '未检验',
-	priority: '',
+	supplierName: '',
+	lotNo: '',
+	billStatus: '',
 	inspector: '',
-	supplierBatchNo: '',
-	supplierShortName: '',
-	rqBatchNo: '',
-	inspectStartFrom: '',
-	inspectStartTo: '',
-	inspectFinishFrom: '',
-	inspectFinishTo: '',
+	inspectDateFrom: '',
+	inspectDateTo: '',
 	page: 1,
-	pageSize: 10,
+	pageSize: 20,
 });
 
-// 本批次为只读 UI 骨架,尚无真实检验任务接口:渲染空表,不发请求、不造数据。
+const inspectDateRange = ref<[string, string] | null>(null);
+
 const loading = ref(false);
-const rows = ref<Record<string, unknown>[]>([]);
+const rows = ref<IqcInspBillRow[]>([]);
 const total = ref(0);
 
+function fmtDate(v?: string | null) {
+	if (!v) return '';
+	return String(v).slice(0, 10);
+}
+
+async function loadList() {
+	loading.value = true;
+	try {
+		const data = await fetchIqcInspBillList({
+			billNo: query.billNo,
+			sourceBillNo: query.sourceBillNo,
+			materialCode: query.materialCode,
+			materialName: query.materialName,
+			supplierName: query.supplierName,
+			lotNo: query.lotNo,
+			billStatus: query.billStatus,
+			inspector: query.inspector,
+			inspectDateFrom: query.inspectDateFrom,
+			inspectDateTo: query.inspectDateTo,
+			page: query.page,
+			pageSize: query.pageSize,
+		});
+		rows.value = data.list || [];
+		total.value = data.total || 0;
+	} catch (e: any) {
+		rows.value = [];
+		total.value = 0;
+		ElMessage.error(e?.message || '加载来料检验单列表失败');
+	} finally {
+		loading.value = false;
+	}
+}
+
 function doSearch() {
+	query.inspectDateFrom = inspectDateRange.value?.[0] || '';
+	query.inspectDateTo = inspectDateRange.value?.[1] || '';
 	query.page = 1;
+	loadList();
 }
 
 function resetQuery() {
 	query.billNo = '';
+	query.sourceBillNo = '';
 	query.materialCode = '';
 	query.materialName = '';
-	query.applyTimeStart = '';
-	query.applyTimeEnd = '';
-	query.inspectProgress = '未检验';
-	query.priority = '';
+	query.supplierName = '';
+	query.lotNo = '';
+	query.billStatus = '';
 	query.inspector = '';
-	query.supplierBatchNo = '';
-	query.supplierShortName = '';
-	query.rqBatchNo = '';
-	query.inspectStartFrom = '';
-	query.inspectStartTo = '';
-	query.inspectFinishFrom = '';
-	query.inspectFinishTo = '';
+	inspectDateRange.value = null;
+	query.inspectDateFrom = '';
+	query.inspectDateTo = '';
 	query.page = 1;
+	loadList();
 }
+
+onMounted(() => loadList());
+onActivated(() => loadList());
 </script>
 
 <style scoped lang="scss">
-.filter-hint {
-	font-weight: bold;
-	margin-bottom: 10px;
-}
 .mb12 {
 	margin-bottom: 12px;
 }

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

@@ -11,9 +11,9 @@
     <GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
     <Copyright>Admin.NET</Copyright>
     <Description>Admin.NET ͨ��Ȩ�޿���ƽ̨</Description>
-    <AssemblyVersion>1.0.232</AssemblyVersion>
-    <FileVersion>1.0.232</FileVersion>
-    <Version>1.0.232</Version>
+    <AssemblyVersion>1.0.233</AssemblyVersion>
+    <FileVersion>1.0.233</FileVersion>
+    <Version>1.0.233</Version>
   </PropertyGroup>
 
   <ItemGroup>

+ 138 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/MaterialWarehouse/Dto/IqcInspBillDto.cs

@@ -0,0 +1,138 @@
+namespace Admin.NET.Plugin.AiDOP.MaterialWarehouse.Dto;
+
+/// <summary>
+/// S5 来料检验单(qms_qcp_inspbill)只读列表 查询入参。
+///
+/// 数据源:aidopdev.qms_qcp_inspbill 单表直读(不 join 申请主表/明细/SuppMaster/ApprovalFlow)。
+/// 该表**无 tenant_id**:TenantId 仅前端 withAidopTenantParams 注入,本查询接收即忽略、不做租户过滤。
+/// FORGID 当前全 NULL:ForgId 接收但本批不生效、不加 WHERE。
+/// pd/clfs/FBILLSTATUS 字典未闭合,raw 展示,不做映射。仅 SELECT,无写入/详情/流程/SQE。
+/// </summary>
+public class IqcInspBillListInput
+{
+    /// <summary>页码(从 1 开始)</summary>
+    public int Page { get; set; } = 1;
+
+    /// <summary>每页条数</summary>
+    public int PageSize { get; set; } = 20;
+
+    /// <summary>检验单号(FBILLNO,模糊)</summary>
+    public string? BillNo { get; set; }
+
+    /// <summary>来源单号(lydjbh,模糊)</summary>
+    public string? SourceBillNo { get; set; }
+
+    /// <summary>物料编码(FMATERIALCFG,模糊)</summary>
+    public string? MaterialCode { get; set; }
+
+    /// <summary>物料名称(wlmc,模糊)</summary>
+    public string? MaterialName { get; set; }
+
+    /// <summary>供应商(gysmc,模糊)</summary>
+    public string? SupplierName { get; set; }
+
+    /// <summary>批次(pch,模糊)</summary>
+    public string? LotNo { get; set; }
+
+    /// <summary>检验状态(FBILLSTATUS,等值,raw)</summary>
+    public string? BillStatus { get; set; }
+
+    /// <summary>判定(pd,等值,raw;前端本批不展示,后端接收)</summary>
+    public long? Judgement { get; set; }
+
+    /// <summary>处理方式(clfs,等值,raw;前端本批不展示,后端接收)</summary>
+    public long? ProcessMethod { get; set; }
+
+    /// <summary>检验员(FINSPECTORID,模糊)</summary>
+    public string? Inspector { get; set; }
+
+    /// <summary>检验开始日期起(FINSPESTARTDATE,含当日,yyyy-MM-dd)</summary>
+    public string? InspectDateFrom { get; set; }
+
+    /// <summary>检验开始日期止(FINSPESTARTDATE,含当日,yyyy-MM-dd)</summary>
+    public string? InspectDateTo { get; set; }
+
+    /// <summary>创建日期起(FCREATETIME,含当日;前端本批不展示,后端接收)</summary>
+    public string? CreateDateFrom { get; set; }
+
+    /// <summary>创建日期止(FCREATETIME,含当日;前端本批不展示,后端接收)</summary>
+    public string? CreateDateTo { get; set; }
+
+    /// <summary>组织(FORGID)——本批不生效(FORGID 全 NULL),接收不过滤</summary>
+    public long? ForgId { get; set; }
+
+    /// <summary>排序字段(白名单:billNo/sourceBillNo/materialCode/materialName/supplierName/lotNo/billStatus/judgement/inspectDate/createTime)</summary>
+    public string? OrderBy { get; set; }
+
+    /// <summary>排序方向(asc/desc,非法回落 desc)</summary>
+    public string? OrderDir { get; set; }
+
+    /// <summary>租户 ID——本批不生效(表无 tenant_id),接收不过滤</summary>
+    public long? TenantId { get; set; }
+}
+
+/// <summary>
+/// S5 来料检验单(qms_qcp_inspbill)只读列表 行。字段对齐前端列 prop(camelCase),源列见注释。
+/// </summary>
+public class IqcInspBillListRow
+{
+    /// <summary>主键(qms_qcp_inspbill.id)</summary>
+    public long? Id { get; set; }
+
+    /// <summary>检验单号(FBILLNO)</summary>
+    public string? BillNo { get; set; }
+
+    /// <summary>来源单号(lydjbh → 申请单 FBILLNO,本批不 join,仅原值)</summary>
+    public string? SourceBillNo { get; set; }
+
+    /// <summary>物料编码(FMATERIALCFG)</summary>
+    public string? MaterialCode { get; set; }
+
+    /// <summary>物料名称(wlmc)</summary>
+    public string? MaterialName { get; set; }
+
+    /// <summary>规格型号(ggxh)</summary>
+    public string? Spec { get; set; }
+
+    /// <summary>供应商(gysmc,已内嵌)</summary>
+    public string? SupplierName { get; set; }
+
+    /// <summary>批次(pch)</summary>
+    public string? LotNo { get; set; }
+
+    /// <summary>报检数量(FRINSQTY)</summary>
+    public decimal? ReportQty { get; set; }
+
+    /// <summary>检验数量(jysl)</summary>
+    public decimal? InspectQty { get; set; }
+
+    /// <summary>合格数量(dhsl)</summary>
+    public decimal? QualifiedQty { get; set; }
+
+    /// <summary>不合格数量(bhgsl)</summary>
+    public decimal? UnqualifiedQty { get; set; }
+
+    /// <summary>判定(pd,raw)</summary>
+    public long? Judgement { get; set; }
+
+    /// <summary>处理方式(clfs,raw)</summary>
+    public long? ProcessMethod { get; set; }
+
+    /// <summary>状态(FBILLSTATUS,raw)</summary>
+    public string? BillStatus { get; set; }
+
+    /// <summary>检验员(FINSPECTORID)</summary>
+    public string? Inspector { get; set; }
+
+    /// <summary>检验开始时间(FINSPESTARTDATE)</summary>
+    public DateTime? InspectStartDate { get; set; }
+
+    /// <summary>检验结束时间(FINSPEENDDATE)</summary>
+    public DateTime? InspectEndDate { get; set; }
+
+    /// <summary>创建时间(FCREATETIME)</summary>
+    public DateTime? CreateTime { get; set; }
+
+    /// <summary>组织(FORGID,当前全 NULL)</summary>
+    public long? OrgId { get; set; }
+}

+ 160 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/MaterialWarehouse/IqcInspBillService.cs

@@ -0,0 +1,160 @@
+using Admin.NET.Plugin.AiDOP.MaterialWarehouse.Dto;
+
+namespace Admin.NET.Plugin.AiDOP.MaterialWarehouse;
+
+/// <summary>
+/// S5 来料检验单(qms_qcp_inspbill)只读 list 服务 —— 来料检验流程主入口。
+///
+/// 数据源:aidopdev.qms_qcp_inspbill 单表直读(不 join 申请主表/明细/SuppMaster/ApprovalFlow/SQE)。
+/// 口径:检验单号=FBILLNO;来源单号=lydjbh;物料=FMATERIALCFG/wlmc/ggxh;供应商=gysmc(内嵌);
+/// 批次=pch;数量=FRINSQTY/jysl/dhsl/bhgsl;判定=pd(raw);处理方式=clfs(raw);状态=FBILLSTATUS(raw);
+/// 检验员=FINSPECTORID;检验起止=FINSPESTARTDATE/FINSPEENDDATE;创建=FCREATETIME。默认排序 FCREATETIME DESC, id DESC。
+///
+/// tenant/组织:qms_qcp_inspbill **无 tenant_id**,不做租户过滤;FORGID 当前全 NULL,不做 FORGID 过滤。
+/// TenantId/ForgId 接收即忽略。本服务仅 SELECT:无新增/编辑/删除/详情/判定录入/主管审核/SQE 处置/仓储通知/
+/// 库存事务/流程实例;不写 qms_qcp_inspbill、不改 FBIZTYPE、不碰 ApprovalFlow。pd/clfs/FBILLSTATUS 字典未闭合,raw 展示。
+/// </summary>
+[ApiDescriptionSettings(Order = 309, Description = "来料检验单列表")]
+[Route("api/S5IqcInspBill")]
+[AllowAnonymous]
+[NonUnify]
+public class IqcInspBillService : IDynamicApiController, ITransient
+{
+    private readonly ISqlSugarClient _db;
+
+    public IqcInspBillService(ISqlSugarClient db)
+    {
+        _db = db;
+    }
+
+    /// <summary>排序字段白名单(前端 prop → 物理列),杜绝 orderBy 注入。</summary>
+    private static readonly Dictionary<string, string> OrderByWhitelist = new(StringComparer.OrdinalIgnoreCase)
+    {
+        ["billNo"] = "ib.FBILLNO",
+        ["sourceBillNo"] = "ib.lydjbh",
+        ["materialCode"] = "ib.FMATERIALCFG",
+        ["materialName"] = "ib.wlmc",
+        ["supplierName"] = "ib.gysmc",
+        ["lotNo"] = "ib.pch",
+        ["billStatus"] = "ib.FBILLSTATUS",
+        ["judgement"] = "ib.pd",
+        ["inspectDate"] = "ib.FINSPESTARTDATE",
+        ["createTime"] = "ib.FCREATETIME",
+    };
+
+    /// <summary>
+    /// 来料检验单列表(只读分页查询)。
+    /// </summary>
+    [DisplayName("来料检验单列表")]
+    [HttpGet("list")]
+    public async Task<object> GetList([FromQuery] IqcInspBillListInput input)
+    {
+        var page = input.Page <= 0 ? 1 : input.Page;
+        var pageSize = input.PageSize <= 0 ? 20 : (input.PageSize > 200 ? 200 : input.PageSize);
+        var offset = (page - 1) * pageSize;
+
+        // qms_qcp_inspbill 无 tenant_id、FORGID 全 NULL:不做租户/组织过滤(TenantId/ForgId 接收即忽略)。
+        var where = new List<string> { "1=1" };
+        var pars = new List<SugarParameter>();
+
+        void AddLike(string? v, string col, string p)
+        {
+            if (string.IsNullOrWhiteSpace(v)) return;
+            where.Add($"{col} LIKE @{p}");
+            pars.Add(new SugarParameter($"@{p}", $"%{v.Trim()}%"));
+        }
+
+        AddLike(input.BillNo, "ib.FBILLNO", "BillNo");
+        AddLike(input.SourceBillNo, "ib.lydjbh", "SourceBillNo");
+        AddLike(input.MaterialCode, "ib.FMATERIALCFG", "MaterialCode");
+        AddLike(input.MaterialName, "ib.wlmc", "MaterialName");
+        AddLike(input.SupplierName, "ib.gysmc", "SupplierName");
+        AddLike(input.LotNo, "ib.pch", "LotNo");
+        AddLike(input.Inspector, "ib.FINSPECTORID", "Inspector");
+
+        if (!string.IsNullOrWhiteSpace(input.BillStatus))
+        {
+            where.Add("ib.FBILLSTATUS = @BillStatus");
+            pars.Add(new SugarParameter("@BillStatus", input.BillStatus.Trim()));
+        }
+        if (input.Judgement.HasValue)
+        {
+            where.Add("ib.pd = @Judgement");
+            pars.Add(new SugarParameter("@Judgement", input.Judgement.Value));
+        }
+        if (input.ProcessMethod.HasValue)
+        {
+            where.Add("ib.clfs = @ProcessMethod");
+            pars.Add(new SugarParameter("@ProcessMethod", input.ProcessMethod.Value));
+        }
+        if (!string.IsNullOrWhiteSpace(input.InspectDateFrom))
+        {
+            where.Add("ib.FINSPESTARTDATE >= @InspectDateFrom");
+            pars.Add(new SugarParameter("@InspectDateFrom", $"{input.InspectDateFrom.Trim()} 00:00:00"));
+        }
+        if (!string.IsNullOrWhiteSpace(input.InspectDateTo))
+        {
+            where.Add("ib.FINSPESTARTDATE <= @InspectDateTo");
+            pars.Add(new SugarParameter("@InspectDateTo", $"{input.InspectDateTo.Trim()} 23:59:59"));
+        }
+        if (!string.IsNullOrWhiteSpace(input.CreateDateFrom))
+        {
+            where.Add("ib.FCREATETIME >= @CreateDateFrom");
+            pars.Add(new SugarParameter("@CreateDateFrom", $"{input.CreateDateFrom.Trim()} 00:00:00"));
+        }
+        if (!string.IsNullOrWhiteSpace(input.CreateDateTo))
+        {
+            where.Add("ib.FCREATETIME <= @CreateDateTo");
+            pars.Add(new SugarParameter("@CreateDateTo", $"{input.CreateDateTo.Trim()} 23:59:59"));
+        }
+
+        var whereSql = string.Join(" AND ", where);
+
+        // 排序:白名单回落,方向 asc/desc 回落 desc;默认 FCREATETIME DESC, id DESC。
+        string orderSql;
+        if (!string.IsNullOrWhiteSpace(input.OrderBy) && OrderByWhitelist.TryGetValue(input.OrderBy.Trim(), out var col))
+        {
+            var dir = string.Equals(input.OrderDir?.Trim(), "asc", StringComparison.OrdinalIgnoreCase) ? "ASC" : "DESC";
+            orderSql = $"{col} {dir}, ib.id DESC";
+        }
+        else
+        {
+            orderSql = "ib.FCREATETIME DESC, ib.id DESC";
+        }
+
+        var total = await _db.Ado.GetIntAsync(
+            $"SELECT COUNT(1) FROM qms_qcp_inspbill ib WHERE {whereSql}", pars);
+
+        var list = await _db.Ado.SqlQueryAsync<IqcInspBillListRow>(
+            $"""
+            SELECT
+                ib.id              AS Id,
+                ib.FBILLNO         AS BillNo,
+                ib.lydjbh          AS SourceBillNo,
+                ib.FMATERIALCFG    AS MaterialCode,
+                ib.wlmc            AS MaterialName,
+                ib.ggxh            AS Spec,
+                ib.gysmc           AS SupplierName,
+                ib.pch             AS LotNo,
+                ib.FRINSQTY        AS ReportQty,
+                ib.jysl            AS InspectQty,
+                ib.dhsl            AS QualifiedQty,
+                ib.bhgsl           AS UnqualifiedQty,
+                ib.pd              AS Judgement,
+                ib.clfs            AS ProcessMethod,
+                ib.FBILLSTATUS     AS BillStatus,
+                ib.FINSPECTORID    AS Inspector,
+                ib.FINSPESTARTDATE AS InspectStartDate,
+                ib.FINSPEENDDATE   AS InspectEndDate,
+                ib.FCREATETIME     AS CreateTime,
+                ib.FORGID          AS OrgId
+            FROM qms_qcp_inspbill ib
+            WHERE {whereSql}
+            ORDER BY {orderSql}
+            LIMIT {pageSize} OFFSET {offset}
+            """,
+            pars);
+
+        return new { total, page, pageSize, list };
+    }
+}