Просмотр исходного кода

fix(s0): normalize org ids as strings in frontend

YY968XX 1 месяц назад
Родитель
Сommit
b1c491f9f3
44 измененных файлов с 451 добавлено и 283 удалено
  1. 1 1
      Web/package.json
  2. 2 2
      Web/src/views/aidop/s0/api/s0ManufacturingApi.spec.ts
  3. 28 28
      Web/src/views/aidop/s0/api/s0ManufacturingApi.ts
  4. 5 3
      Web/src/views/aidop/s0/api/s0SalesApi.spec.ts
  5. 31 28
      Web/src/views/aidop/s0/api/s0SalesApi.ts
  6. 22 20
      Web/src/views/aidop/s0/api/s0SupplyApi.ts
  7. 1 1
      Web/src/views/aidop/s0/api/s0WarehouseApi.spec.ts
  8. 46 46
      Web/src/views/aidop/s0/api/s0WarehouseApi.ts
  9. 8 3
      Web/src/views/aidop/s0/composables/useS0MfgOrgScope.ts
  10. 6 6
      Web/src/views/aidop/s0/manufacturing/OrderScheduleCycleList.vue
  11. 7 7
      Web/src/views/aidop/s0/manufacturing/PersonnelSkillList.vue
  12. 6 6
      Web/src/views/aidop/s0/manufacturing/PreprocessElementParamList.vue
  13. 6 6
      Web/src/views/aidop/s0/manufacturing/ProcessFlowCardList.vue
  14. 8 8
      Web/src/views/aidop/s0/manufacturing/ProductionLineList.vue
  15. 5 5
      Web/src/views/aidop/s0/manufacturing/RoutingList.vue
  16. 6 6
      Web/src/views/aidop/s0/manufacturing/SopFileTypeList.vue
  17. 6 6
      Web/src/views/aidop/s0/manufacturing/SopMaintenanceList.vue
  18. 7 7
      Web/src/views/aidop/s0/manufacturing/StandardBomManagement.vue
  19. 8 8
      Web/src/views/aidop/s0/manufacturing/StandardProcessList.vue
  20. 3 3
      Web/src/views/aidop/s0/manufacturing/WorkOrderControlParams.vue
  21. 5 5
      Web/src/views/aidop/s0/sales/ContractReviewCycleList.vue
  22. 18 9
      Web/src/views/aidop/s0/sales/CustomerList.vue
  23. 5 5
      Web/src/views/aidop/s0/sales/MaterialList.vue
  24. 5 5
      Web/src/views/aidop/s0/sales/OrderPriorityRuleList.vue
  25. 5 5
      Web/src/views/aidop/s0/sales/OrderReviewCycleList.vue
  26. 5 5
      Web/src/views/aidop/s0/sales/ProductDesignCycleList.vue
  27. 7 7
      Web/src/views/aidop/s0/supply/CategoryLeadTimeList.vue
  28. 6 6
      Web/src/views/aidop/s0/supply/MaterialPlanCycleList.vue
  29. 7 7
      Web/src/views/aidop/s0/supply/SourcingList.vue
  30. 7 7
      Web/src/views/aidop/s0/supply/SupplierList.vue
  31. 1 1
      Web/src/views/aidop/s0/warehouse/BarcodeRuleList.vue
  32. 2 2
      Web/src/views/aidop/s0/warehouse/CostCenterList.vue
  33. 2 2
      Web/src/views/aidop/s0/warehouse/DepartmentList.vue
  34. 1 1
      Web/src/views/aidop/s0/warehouse/EmpWorkDutyList.vue
  35. 2 2
      Web/src/views/aidop/s0/warehouse/EmployeeList.vue
  36. 1 1
      Web/src/views/aidop/s0/warehouse/ItemPackList.vue
  37. 2 2
      Web/src/views/aidop/s0/warehouse/LabelTypeList.vue
  38. 2 2
      Web/src/views/aidop/s0/warehouse/LocationList.vue
  39. 2 2
      Web/src/views/aidop/s0/warehouse/LocationShelfList.vue
  40. 1 1
      Web/src/views/aidop/s0/warehouse/NbrControlList.vue
  41. 2 2
      Web/src/views/aidop/s0/warehouse/NbrTypeList.vue
  42. 1 1
      Web/src/views/aidop/s0/warehouse/TaskAssignmentList.vue
  43. 147 0
      Web/tests/e2e/s0-org-id-sampling.spec.ts
  44. 3 3
      server/Admin.NET.Web.Entry/Admin.NET.Web.Entry.csproj

+ 1 - 1
Web/package.json

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

+ 2 - 2
Web/src/views/aidop/s0/api/s0ManufacturingApi.spec.ts

@@ -29,8 +29,8 @@ describe('searchMaterialsForMfg', () => {
 	it('clamps pageSize to S0_MFG_MATERIAL_SEARCH_MAX_PAGE_SIZE', async () => {
 		await searchMaterialsForMfg({
 			keyword: 'a',
-			companyRefId: 1,
-			factoryRefId: 2,
+			companyRefId: '1',
+			factoryRefId: '2',
 			page: 1,
 			pageSize: 500,
 		});

+ 28 - 28
Web/src/views/aidop/s0/api/s0ManufacturingApi.ts

@@ -14,8 +14,8 @@ export const S0_MFG_MATERIAL_SEARCH_MAX_PAGE_SIZE = 100;
 
 export function searchMaterialsForMfg(params: {
 	keyword?: string;
-	companyRefId?: number;
-	factoryRefId?: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	page?: number;
 	pageSize?: number;
 }) {
@@ -30,8 +30,8 @@ export function searchMaterialsForMfg(params: {
 /** 标准 BOM 行级列表行(路线 A,对齐原平台列表展示字段)。 */
 export interface S0MfgStandardBomLineRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	parentMaterialId: number;
 	parentItemNum: string;
 	parentDescr: string;
@@ -60,8 +60,8 @@ export interface S0MfgStandardBomLineRow {
 
 export interface S0MfgProductStructureMaster {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	parentMaterialId: number;
 	componentMaterialId: number;
 	qty: number;
@@ -85,8 +85,8 @@ export interface S0MfgStandardBomLineDetail {
 }
 
 export interface S0MfgStandardBomLineUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	parentMaterialId: number;
 	componentMaterialId: number;
 	qty: number;
@@ -106,8 +106,8 @@ export interface S0MfgStandardBomLineUpsert {
 /** 标准工艺路线明细行(源 RoutingOpDetail 行级,方案 A)。 */
 export interface S0MfgRoutingOpDetailRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	routeCode: string;
 	routeName: string;
 	materialCode: string;
@@ -142,8 +142,8 @@ export interface S0MfgRoutingOpDetailRow {
 }
 
 export interface S0MfgRoutingOpDetailUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	routeCode: string;
 	routeName: string;
 	materialCode: string;
@@ -311,8 +311,8 @@ const mfg = '/api/s0/manufacturing';
 /** LineMaster 语义(ado_s0_mfg_line_master);列表行含码表占位字段。 */
 export interface S0LineMasterRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domain: string;
 	line: string;
 	describe?: string | null;
@@ -335,8 +335,8 @@ export interface S0LineMasterRow {
 }
 
 export interface S0LineMasterUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domain: string;
 	line: string;
 	describe?: string;
@@ -355,8 +355,8 @@ export interface S0LineMasterUpsert {
 /** StdOpMaster 语义(ado_s0_mfg_std_op_master) */
 export interface S0StdOpMasterRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domain: string;
 	stdOp: string;
 	stdOpCode?: string | null;
@@ -368,8 +368,8 @@ export interface S0StdOpMasterRow {
 }
 
 export interface S0StdOpMasterUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domain: string;
 	stdOp: string;
 	stdOpCode: string;
@@ -392,8 +392,8 @@ export const s0MfgStandardOperationsApi = {
 
 export interface S0ProcessFlowCardRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string | null;
 	workOrderNo: string;
 	workOrderBatchNo?: string | null;
@@ -413,8 +413,8 @@ export interface S0ProcessFlowCardRow {
 }
 
 export interface S0ProcessFlowCardUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	workOrderNo: string;
 	workOrderBatchNo?: string;
@@ -433,8 +433,8 @@ export interface S0ProcessFlowCardUpsert {
 
 export interface S0OrderScheduleCycleRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string | null;
 	orderType?: string | null;
 	stdHours: number;
@@ -447,8 +447,8 @@ export interface S0OrderScheduleCycleRow {
 }
 
 export interface S0OrderScheduleCycleUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	orderType?: string;
 	stdHours: number;

+ 5 - 3
Web/src/views/aidop/s0/api/s0SalesApi.spec.ts

@@ -68,7 +68,7 @@ describe('s0SalesApi load helpers', () => {
 		warnSpy.mockRestore();
 	});
 
-	it('loadOrgList maps org rows and fills id fallback with 0', async () => {
+	it('loadOrgList normalizes id/pid to string (snowflake IDs exceed JS number precision)', async () => {
 		orgListMock.mockResolvedValue({
 			data: {
 				result: [{ id: 12, pid: 1, name: '研发', code: 'DEV', type: 'dept' }, { id: null }],
@@ -79,8 +79,10 @@ describe('s0SalesApi load helpers', () => {
 
 		expect(orgListMock).toHaveBeenCalledWith(0, undefined, undefined, 'dept');
 		expect(result).toEqual([
-			{ id: 12, pid: 1, name: '研发', code: 'DEV', type: 'dept' },
-			{ id: 0, pid: undefined, name: undefined, code: undefined, type: undefined },
+			// id/pid 强制 string,兼容后端历史上偶发 number 返回
+			{ id: '12', pid: '1', name: '研发', code: 'DEV', type: 'dept' },
+			// id=null 归一化为空串,pid 缺省归一化为 null
+			{ id: '', pid: null, name: undefined, code: undefined, type: undefined },
 		]);
 	});
 

+ 31 - 28
Web/src/views/aidop/s0/api/s0SalesApi.ts

@@ -19,8 +19,9 @@ export interface OptionItem {
 }
 
 export interface OrgOption {
-	id: number;
-	pid?: number | null;
+	/** 雪花 ID,后端为字符串,15+ 位超 JS number 精度,前端全链统一 string */
+	id: string;
+	pid?: string | null;
 	name?: string | null;
 	code?: string | null;
 	type?: string | null;
@@ -28,8 +29,8 @@ export interface OrgOption {
 
 export interface S0CustMasterRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string | null;
 	cust: string;
 	sortName?: string | null;
@@ -56,8 +57,8 @@ export interface S0CustMasterRow {
 }
 
 export interface S0CustMasterUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	cust: string;
 	sortName: string;
@@ -84,8 +85,8 @@ export interface S0CustMasterUpsert {
 /** 列表/详情:ItemMaster;locationDescr 为列表拼接展示字段 */
 export interface S0ItemMasterRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string | null;
 	itemNum: string;
 	descr: string;
@@ -146,8 +147,8 @@ export interface S0ItemMasterRow {
 }
 
 export interface S0ItemMasterUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	itemNum: string;
 	descr: string;
@@ -206,8 +207,8 @@ export interface S0ItemMasterUpsert {
 
 export interface S0PriorityCodeRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string | null;
 	descr: string;
 	value?: string | null;
@@ -232,8 +233,8 @@ export interface S0PriorityCodeRow {
 }
 
 export interface S0PriorityCodeUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	descr: string;
 	value?: string;
@@ -380,8 +381,8 @@ export async function loadDictOptions(code: string): Promise<OptionItem[]> {
 
 export interface S0ContractReviewCycleRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string | null;
 	stageCode: string;
 	stageName: string;
@@ -395,8 +396,8 @@ export interface S0ContractReviewCycleRow {
 }
 
 export interface S0ContractReviewCycleUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	stageCode: string;
 	stageName: string;
@@ -426,8 +427,8 @@ export const s0ContractReviewCyclesApi = {
 
 export interface S0ProductDesignCycleRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string | null;
 	itemType: string;
 	ownerApplication: string;
@@ -440,8 +441,8 @@ export interface S0ProductDesignCycleRow {
 }
 
 export interface S0ProductDesignCycleUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	itemType: string;
 	ownerApplication: string;
@@ -470,8 +471,8 @@ export const s0ProductDesignCyclesApi = {
 
 export interface S0OrderReviewCycleRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string | null;
 	orderType?: string | null;
 	stdHours: number;
@@ -484,8 +485,8 @@ export interface S0OrderReviewCycleRow {
 }
 
 export interface S0OrderReviewCycleUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	orderType?: string;
 	stdHours: number;
@@ -514,9 +515,11 @@ export async function loadOrgList(type?: string): Promise<OrgOption[]> {
 	try {
 		const api = new SysOrgApi(undefined, undefined, service);
 		const res = await api.apiSysOrgListGet(0, undefined, undefined, type);
+		// 统一在进入前端前做 string 归一化,兼容后端历史上偶发 number 返回:
+		// 雪花 ID 超 JS Number 安全范围,禁止朝 number 方向强转,只做朝 string 方向的归一化。
 		return (res.data.result ?? []).map((item) => ({
-			id: item.id ?? 0,
-			pid: item.pid,
+			id: item.id == null ? '' : String(item.id),
+			pid: item.pid == null ? null : String(item.pid),
 			name: item.name,
 			code: item.code,
 			type: item.type,

+ 22 - 20
Web/src/views/aidop/s0/api/s0SupplyApi.ts

@@ -18,8 +18,9 @@ export interface OptionItem {
 }
 
 export interface OrgOption {
-	id: number;
-	pid?: number | null;
+	/** 雪花 ID,后端为字符串,15+ 位超 JS number 精度,前端全链统一 string */
+	id: string;
+	pid?: string | null;
 	name?: string | null;
 	code?: string | null;
 	type?: string | null;
@@ -27,8 +28,8 @@ export interface OrgOption {
 
 export interface S0SuppMasterRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string | null;
 	supp: string;
 	sortName?: string | null;
@@ -61,8 +62,8 @@ export interface S0SuppMasterRow {
 }
 
 export interface S0SuppMasterUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	supp: string;
 	sortName?: string;
@@ -107,8 +108,8 @@ export const s0SuppliersApi = {
 /** 货源清单行(srm_purchase + 展示字段) */
 export interface S0SrmPurchaseRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string | null;
 	icitemId: number;
 	icitemName?: string | null;
@@ -145,8 +146,8 @@ export interface S0SrmPurchaseRow {
 }
 
 export interface S0SrmPurchaseUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	icitemId: number;
 	icitemName?: string;
@@ -187,8 +188,8 @@ export const s0SrmPurchasesApi = {
 
 export interface S0CategoryLeadTimeRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string | null;
 	categoryCode: string;
 	categoryName?: string | null;
@@ -203,8 +204,8 @@ export interface S0CategoryLeadTimeRow {
 }
 
 export interface S0CategoryLeadTimeUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	categoryCode: string;
 	categoryName?: string;
@@ -231,8 +232,8 @@ export const s0CategoryLeadTimesApi = {
 
 export interface S0MaterialPlanCycleRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string | null;
 	orderType?: string | null;
 	stdHours: number;
@@ -245,8 +246,8 @@ export interface S0MaterialPlanCycleRow {
 }
 
 export interface S0MaterialPlanCycleUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	orderType?: string;
 	stdHours: number;
@@ -287,9 +288,10 @@ export async function loadOrgList(type?: string): Promise<OrgOption[]> {
 	try {
 		const api = new SysOrgApi(undefined, undefined, service);
 		const res = await api.apiSysOrgListGet(0, undefined, undefined, type);
+		// 朝 string 归一化,对抗后端偶发 number 返回(雪花 ID 禁止转 number,否则丢精度)
 		return (res.data.result ?? []).map((item) => ({
-			id: item.id ?? 0,
-			pid: item.pid,
+			id: item.id == null ? '' : String(item.id),
+			pid: item.pid == null ? null : String(item.pid),
 			name: item.name,
 			code: item.code,
 			type: item.type,

+ 1 - 1
Web/src/views/aidop/s0/api/s0WarehouseApi.spec.ts

@@ -34,7 +34,7 @@ describe('s0WarehouseApi', () => {
 	});
 
 	it('departments create posts body and unwraps', async () => {
-		const body = { companyRefId: 1, factoryRefId: 2, department: 'D01', isActive: true };
+		const body = { companyRefId: '1', factoryRefId: '2', department: 'D01', isActive: true };
 		postMock.mockResolvedValue({ data: { id: 101, ...body } });
 		const result = await s0DepartmentsApi.create(body);
 		expect(postMock).toHaveBeenCalledWith('/api/s0/warehouse/departments', body);

+ 46 - 46
Web/src/views/aidop/s0/api/s0WarehouseApi.ts

@@ -15,8 +15,8 @@ export interface Paged<T> {
 
 export interface S0DepartmentRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode: string;
 	department: string;
 	descr?: string | null;
@@ -28,8 +28,8 @@ export interface S0DepartmentRow {
 }
 
 export interface S0DepartmentUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	department: string;
 	descr?: string;
@@ -52,8 +52,8 @@ export const s0DepartmentsApi = {
 
 export interface S0EmployeeRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode: string;
 	employee: string;
 	name?: string | null;
@@ -80,8 +80,8 @@ export interface S0EmployeeRow {
 }
 
 export interface S0EmployeeUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	employee: string;
 	name?: string;
@@ -117,8 +117,8 @@ export const s0EmployeesApi = {
 
 export interface S0CostCtrRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode: string;
 	costCtr: string;
 	descr?: string | null;
@@ -131,8 +131,8 @@ export interface S0CostCtrRow {
 }
 
 export interface S0CostCtrUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	costCtr: string;
 	descr?: string;
@@ -156,8 +156,8 @@ export const s0CostCentersApi = {
 
 export interface S0LocationRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode: string;
 	location: string;
 	descr?: string | null;
@@ -172,8 +172,8 @@ export interface S0LocationRow {
 }
 
 export interface S0LocationUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	location: string;
 	descr?: string;
@@ -199,8 +199,8 @@ export const s0LocationsApi = {
 
 export interface S0LocationShelfRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode: string;
 	location: string;
 	invShelf: string;
@@ -215,8 +215,8 @@ export interface S0LocationShelfRow {
 }
 
 export interface S0LocationShelfUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	location: string;
 	invShelf: string;
@@ -241,8 +241,8 @@ export const s0LocationShelvesApi = {
 
 export interface S0BarcodeRuleRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode: string;
 	customer?: string | null;
 	suppName?: string | null;
@@ -284,8 +284,8 @@ export const s0BarcodeRulesApi = {
 
 export interface S0LabelTypeRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode: string;
 	barType: string;
 	class?: string | null;
@@ -297,8 +297,8 @@ export interface S0LabelTypeRow {
 }
 
 export interface S0LabelTypeUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	barType: string;
 	class?: string;
@@ -321,8 +321,8 @@ export const s0LabelTypesApi = {
 
 export interface S0NbrTypeRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode: string;
 	dept?: string | null;
 	deptDescr?: string | null;
@@ -337,8 +337,8 @@ export interface S0NbrTypeRow {
 }
 
 export interface S0NbrTypeUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	dept?: string;
 	nbrClass?: string;
@@ -363,8 +363,8 @@ export const s0NbrTypesApi = {
 
 export interface S0NbrControlRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode: string;
 	nbrType: string;
 	description?: string | null;
@@ -386,8 +386,8 @@ export interface S0NbrControlRow {
 }
 
 export interface S0NbrControlUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	nbrType: string;
 	description?: string;
@@ -421,8 +421,8 @@ export const s0NbrControlsApi = {
 
 export interface S0ItemPackRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode: string;
 	itemNum: string;
 	itemDescr?: string | null;
@@ -446,8 +446,8 @@ export interface S0ItemPackRow {
 }
 
 export interface S0ItemPackUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	itemNum: string;
 	packingQty?: number | null;
@@ -480,8 +480,8 @@ export const s0ItemPacksApi = {
 
 export interface S0EmpWorkDutyRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode: string;
 	employee: string;
 	employeeName?: string | null;
@@ -504,8 +504,8 @@ export interface S0EmpWorkDutyRow {
 }
 
 export interface S0EmpWorkDutyUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	employee: string;
 	itemNum?: string;
@@ -535,8 +535,8 @@ export const s0EmpWorkDutiesApi = {
 
 export interface S0TaskAssignmentRow {
 	id: number;
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode: string;
 	tcrq?: string | null;
 	sqr?: string | null;
@@ -558,8 +558,8 @@ export interface S0TaskAssignmentRow {
 }
 
 export interface S0TaskAssignmentUpsert {
-	companyRefId: number;
-	factoryRefId: number;
+	companyRefId?: string;
+	factoryRefId?: string;
 	domainCode?: string;
 	tcrq?: string | null;
 	sqr?: string;

+ 8 - 3
Web/src/views/aidop/s0/composables/useS0MfgOrgScope.ts

@@ -1,7 +1,12 @@
 import { ref } from 'vue';
 import { loadOrgList, type OrgOption } from '../api/s0SalesApi';
 
-/** S0 制造页共用:公司(201) / 工厂(501) 下拉 */
+/**
+ * S0 制造页共用:公司(201) / 工厂(501) 下拉
+ *
+ * 组织 ID 全链路为 string 语义(雪花 ID 超 JS number 精度,禁止转 number)。
+ * 未选状态统一为 undefined / null / '',不使用 0。
+ */
 export function useS0MfgOrgScope() {
 	const companyOptions = ref<OrgOption[]>([]);
 	const factoryOptions = ref<OrgOption[]>([]);
@@ -12,8 +17,8 @@ export function useS0MfgOrgScope() {
 		factoryOptions.value = f;
 	}
 
-	function factoriesForCompany(cid: number | undefined | null) {
-		if (cid == null || cid === 0) return factoryOptions.value;
+	function factoriesForCompany(cid: string | undefined | null) {
+		if (cid == null || cid === '') return factoryOptions.value;
 		return factoryOptions.value.filter((x) => x.pid === cid);
 	}
 

+ 6 - 6
Web/src/views/aidop/s0/manufacturing/OrderScheduleCycleList.vue

@@ -99,8 +99,8 @@ const route = useRoute();
 const pageTitle = computed(() => (route.meta?.title as string) || '订单排程周期');
 
 const query = reactive({
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	orderType: '',
 	isActive: undefined as boolean | undefined,
 	page: 1,
@@ -127,8 +127,8 @@ const formFactoryOptions = computed(() => {
 });
 
 const form = reactive<S0OrderScheduleCycleUpsert>({
-	companyRefId: 0,
-	factoryRefId: 0,
+	companyRefId: undefined,
+	factoryRefId: undefined,
 	domainCode: '',
 	orderType: '',
 	stdHours: 0,
@@ -145,7 +145,7 @@ const rules: FormRules = {
 };
 
 watch(() => form.companyRefId, () => {
-	if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) form.factoryRefId = 0;
+	if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) form.factoryRefId = undefined;
 });
 watch(() => query.companyRefId, () => {
 	if (!filteredFactoryOptions.value.some((item) => item.id === query.factoryRefId)) query.factoryRefId = undefined;
@@ -190,7 +190,7 @@ function resetQuery() {
 
 function resetForm() {
 	editingId.value = null;
-	Object.assign(form, { companyRefId: 0, factoryRefId: 0, domainCode: '', orderType: '', stdHours: 0, remarks: '', isActive: true, createUser: '', updateUser: '' });
+	Object.assign(form, { companyRefId: undefined, factoryRefId: undefined, domainCode: '', orderType: '', stdHours: 0, remarks: '', isActive: true, createUser: '', updateUser: '' });
 	formRef.value?.clearValidate();
 }
 

+ 7 - 7
Web/src/views/aidop/s0/manufacturing/PersonnelSkillList.vue

@@ -135,8 +135,8 @@ const formFactories = computed(() => factoriesForCompany(form.companyRefId));
 
 const query = reactive({
 	keyword: '',
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	isEnabled: undefined as boolean | undefined,
 	page: 1,
 	pageSize: 20,
@@ -152,8 +152,8 @@ const saving = ref(false);
 const formRef = ref<FormInstance>();
 
 const form = reactive({
-	companyRefId: 0,
-	factoryRefId: 0,
+	companyRefId: undefined,
+	factoryRefId: undefined,
 	code: '',
 	name: '',
 	skillLevel: '',
@@ -170,7 +170,7 @@ watch(
 watch(
 	() => form.companyRefId,
 	() => {
-		if (!formFactories.value.some((x) => x.id === form.factoryRefId)) form.factoryRefId = 0;
+		if (!formFactories.value.some((x) => x.id === form.factoryRefId)) form.factoryRefId = undefined;
 	},
 );
 
@@ -214,8 +214,8 @@ function resetQuery() {
 function resetForm() {
 	editingId.value = null;
 	Object.assign(form, {
-		companyRefId: 0,
-		factoryRefId: 0,
+		companyRefId: undefined,
+		factoryRefId: undefined,
 		code: '',
 		name: '',
 		skillLevel: '',

+ 6 - 6
Web/src/views/aidop/s0/manufacturing/PreprocessElementParamList.vue

@@ -123,15 +123,15 @@ const { companyOptions, factoryOptions, loadOrgs, factoriesForCompany } = useS0M
 const queryFactories = computed(() => factoriesForCompany(query.companyRefId));
 const formFactories = computed(() => factoryOptions.value);
 
-function resolveDomain(factoryRefId: number | undefined | null) {
+function resolveDomain(factoryRefId: string | undefined | null) {
 	const opt = factoryOptions.value.find((x) => x.id === factoryRefId);
 	return (opt?.code || (factoryRefId != null ? `${factoryRefId}` : '')).trim();
 }
 
 const query = reactive({
 	keyword: '',
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	page: 1,
 	pageSize: 20,
 });
@@ -146,7 +146,7 @@ const saving = ref(false);
 const formRef = ref<FormInstance>();
 
 const form = reactive({
-	factoryRefId: 0,
+	factoryRefId: undefined,
 	deviceNumber: '',
 	deviceDescr: '',
 	messageCode: '',
@@ -168,7 +168,7 @@ watch(
 watch(
 	() => form.factoryRefId,
 	() => {
-		if (!formFactories.value.some((x) => x.id === form.factoryRefId)) form.factoryRefId = 0;
+		if (!formFactories.value.some((x) => x.id === form.factoryRefId)) form.factoryRefId = undefined;
 	},
 );
 
@@ -214,7 +214,7 @@ function resetQuery() {
 function resetForm() {
 	editingId.value = null;
 	Object.assign(form, {
-		factoryRefId: 0,
+		factoryRefId: undefined,
 		deviceNumber: '',
 		deviceDescr: '',
 		messageCode: '',

+ 6 - 6
Web/src/views/aidop/s0/manufacturing/ProcessFlowCardList.vue

@@ -119,8 +119,8 @@ const route = useRoute();
 const pageTitle = computed(() => (route.meta?.title as string) || '工序流转卡');
 
 const query = reactive({
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	workOrderNo: '',
 	flowCardNo: '',
 	itemNum: '',
@@ -150,8 +150,8 @@ const formFactoryOptions = computed(() => {
 });
 
 const form = reactive<S0ProcessFlowCardUpsert>({
-	companyRefId: 0,
-	factoryRefId: 0,
+	companyRefId: undefined,
+	factoryRefId: undefined,
 	domainCode: '',
 	workOrderNo: '',
 	workOrderBatchNo: '',
@@ -180,7 +180,7 @@ watch(() => query.companyRefId, () => {
 	if (!filteredFactoryOptions.value.some((item) => item.id === query.factoryRefId)) query.factoryRefId = undefined;
 });
 watch(() => form.companyRefId, () => {
-	if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) form.factoryRefId = 0;
+	if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) form.factoryRefId = undefined;
 });
 
 function syncDomainCode() {
@@ -233,7 +233,7 @@ function resetQuery() {
 function resetForm() {
 	editingId.value = null;
 	Object.assign(form, {
-		companyRefId: 0, factoryRefId: 0, domainCode: '', workOrderNo: '', workOrderBatchNo: '',
+		companyRefId: undefined, factoryRefId: undefined, domainCode: '', workOrderNo: '', workOrderBatchNo: '',
 		flowCardNo: '', nature: '', itemNum: '', itemName: '', drawingNo: '', bomVersion: '',
 		routingCode: '', remarks: '', isActive: true, createUser: '', updateUser: '',
 	});

+ 8 - 8
Web/src/views/aidop/s0/manufacturing/ProductionLineList.vue

@@ -134,8 +134,8 @@ const query = reactive({
 	keyword: '',
 	line: '',
 	workshop: '',
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	isEnabled: undefined as boolean | undefined,
 	page: 1,
 	pageSize: 20,
@@ -151,8 +151,8 @@ const saving = ref(false);
 const formRef = ref<FormInstance>();
 
 const form = reactive({
-	companyRefId: 0,
-	factoryRefId: 0,
+	companyRefId: undefined,
+	factoryRefId: undefined,
 	domain: '',
 	line: '',
 	describe: '',
@@ -178,7 +178,7 @@ watch(
 	() => form.companyRefId,
 	() => {
 		if (!formFactories.value.some((x) => x.id === form.factoryRefId)) {
-			form.factoryRefId = 0;
+			form.factoryRefId = undefined;
 			form.domain = '';
 		}
 	},
@@ -197,7 +197,7 @@ function syncDomainFromFactory() {
 }
 
 function onFormCompanyChange() {
-	form.factoryRefId = 0;
+	form.factoryRefId = undefined;
 	form.domain = '';
 }
 
@@ -238,8 +238,8 @@ function resetQuery() {
 function resetForm() {
 	editingId.value = null;
 	Object.assign(form, {
-		companyRefId: 0,
-		factoryRefId: 0,
+		companyRefId: undefined,
+		factoryRefId: undefined,
 		domain: '',
 		line: '',
 		describe: '',

+ 5 - 5
Web/src/views/aidop/s0/manufacturing/RoutingList.vue

@@ -182,8 +182,8 @@ const formFactories = computed(() => factoriesForCompany(form.companyRefId));
 
 const query = reactive({
 	keyword: '',
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	isEnabled: undefined as boolean | undefined,
 	page: 1,
 	pageSize: 20,
@@ -200,8 +200,8 @@ const formRef = ref<FormInstance>();
 
 function emptyForm() {
 	return {
-		companyRefId: 0,
-		factoryRefId: 0,
+		companyRefId: undefined,
+		factoryRefId: undefined,
 		routeCode: '',
 		routeName: '',
 		materialCode: '',
@@ -245,7 +245,7 @@ watch(
 watch(
 	() => form.companyRefId,
 	() => {
-		if (!formFactories.value.some((x) => x.id === form.factoryRefId)) form.factoryRefId = 0;
+		if (!formFactories.value.some((x) => x.id === form.factoryRefId)) form.factoryRefId = undefined;
 	},
 );
 

+ 6 - 6
Web/src/views/aidop/s0/manufacturing/SopFileTypeList.vue

@@ -73,15 +73,15 @@ const { companyOptions, factoryOptions, loadOrgs, factoriesForCompany } = useS0M
 const queryFactories = computed(() => factoriesForCompany(query.companyRefId));
 const formFactories = computed(() => factoryOptions.value);
 
-function resolveDomain(factoryRefId: number | undefined | null) {
+function resolveDomain(factoryRefId: string | undefined | null) {
 	const opt = factoryOptions.value.find((x) => x.id === factoryRefId);
 	return (opt?.code || (factoryRefId != null ? `${factoryRefId}` : '')).trim();
 }
 
 const query = reactive({
 	keyword: '',
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	page: 1,
 	pageSize: 20,
 });
@@ -94,7 +94,7 @@ const editingId = ref<number | null>(null);
 const saving = ref(false);
 const formRef = ref<FormInstance>();
 const form = reactive({
-	factoryRefId: 0,
+	factoryRefId: undefined,
 	imageTypeID: '',
 	descr: '',
 });
@@ -109,7 +109,7 @@ watch(
 watch(
 	() => form.factoryRefId,
 	() => {
-		if (!formFactories.value.some((x) => x.id === form.factoryRefId)) form.factoryRefId = 0;
+		if (!formFactories.value.some((x) => x.id === form.factoryRefId)) form.factoryRefId = undefined;
 	},
 );
 
@@ -154,7 +154,7 @@ function resetQuery() {
 
 function resetForm() {
 	editingId.value = null;
-	Object.assign(form, { factoryRefId: 0, imageTypeID: '', descr: '' });
+	Object.assign(form, { factoryRefId: undefined, imageTypeID: '', descr: '' });
 	formRef.value?.clearValidate();
 }
 

+ 6 - 6
Web/src/views/aidop/s0/manufacturing/SopMaintenanceList.vue

@@ -89,15 +89,15 @@ const { companyOptions, factoryOptions, loadOrgs, factoriesForCompany } = useS0M
 const queryFactories = computed(() => factoriesForCompany(query.companyRefId));
 const formFactories = computed(() => factoryOptions.value);
 
-function resolveDomain(factoryRefId: number | undefined | null) {
+function resolveDomain(factoryRefId: string | undefined | null) {
 	const opt = factoryOptions.value.find((x) => x.id === factoryRefId);
 	return (opt?.code || (factoryRefId != null ? `${factoryRefId}` : '')).trim();
 }
 
 const query = reactive({
 	keyword: '',
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	page: 1,
 	pageSize: 20,
 });
@@ -111,7 +111,7 @@ const saving = ref(false);
 const formRef = ref<FormInstance>();
 
 const form = reactive({
-	factoryRefId: 0,
+	factoryRefId: undefined,
 	line: '',
 	itemNum: '',
 	op: '',
@@ -131,7 +131,7 @@ watch(
 watch(
 	() => form.factoryRefId,
 	() => {
-		if (!formFactories.value.some((x) => x.id === form.factoryRefId)) form.factoryRefId = 0;
+		if (!formFactories.value.some((x) => x.id === form.factoryRefId)) form.factoryRefId = undefined;
 	},
 );
 
@@ -176,7 +176,7 @@ function resetQuery() {
 function resetForm() {
 	editingId.value = null;
 	Object.assign(form, {
-		factoryRefId: 0,
+		factoryRefId: undefined,
 		line: '',
 		itemNum: '',
 		op: '',

+ 7 - 7
Web/src/views/aidop/s0/manufacturing/StandardBomManagement.vue

@@ -169,8 +169,8 @@ const materialLoading = ref(false);
 
 const query = reactive({
 	keyword: '',
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	isEnabled: undefined as boolean | undefined,
 	page: 1,
 	pageSize: 20,
@@ -187,8 +187,8 @@ const saving = ref(false);
 const formRef = ref<FormInstance>();
 
 const form = reactive({
-	companyRefId: 0,
-	factoryRefId: 0,
+	companyRefId: undefined,
+	factoryRefId: undefined,
 	parentMaterialId: undefined as number | undefined,
 	componentMaterialId: undefined as number | undefined,
 	qty: 1,
@@ -214,7 +214,7 @@ watch(
 watch(
 	() => form.companyRefId,
 	() => {
-		if (!formFactories.value.some((x) => x.id === form.factoryRefId)) form.factoryRefId = 0;
+		if (!formFactories.value.some((x) => x.id === form.factoryRefId)) form.factoryRefId = undefined;
 	},
 );
 
@@ -289,8 +289,8 @@ function resetQuery() {
 function resetForm() {
 	editingId.value = null;
 	Object.assign(form, {
-		companyRefId: 0,
-		factoryRefId: 0,
+		companyRefId: undefined,
+		factoryRefId: undefined,
 		parentMaterialId: undefined,
 		componentMaterialId: undefined,
 		qty: 1,

+ 8 - 8
Web/src/views/aidop/s0/manufacturing/StandardProcessList.vue

@@ -117,8 +117,8 @@ const query = reactive({
 	stdOpCode: '',
 	stdOp: '',
 	milestoneOp: '',
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	page: 1,
 	pageSize: 20,
 });
@@ -133,8 +133,8 @@ const saving = ref(false);
 const formRef = ref<FormInstance>();
 
 const form = reactive({
-	companyRefId: 0,
-	factoryRefId: 0,
+	companyRefId: undefined,
+	factoryRefId: undefined,
 	domain: '',
 	stdOpCode: '',
 	stdOp: '',
@@ -151,7 +151,7 @@ watch(
 	() => form.companyRefId,
 	() => {
 		if (!formFactories.value.some((x) => x.id === form.factoryRefId)) {
-			form.factoryRefId = 0;
+			form.factoryRefId = undefined;
 			form.domain = '';
 		}
 	},
@@ -171,7 +171,7 @@ function syncDomainFromFactory() {
 }
 
 function onFormCompanyChange() {
-	form.factoryRefId = 0;
+	form.factoryRefId = undefined;
 	form.domain = '';
 }
 
@@ -212,8 +212,8 @@ function resetQuery() {
 function resetForm() {
 	editingId.value = null;
 	Object.assign(form, {
-		companyRefId: 0,
-		factoryRefId: 0,
+		companyRefId: undefined,
+		factoryRefId: undefined,
 		domain: '',
 		stdOpCode: '',
 		stdOp: '',

+ 3 - 3
Web/src/views/aidop/s0/manufacturing/WorkOrderControlParams.vue

@@ -110,8 +110,8 @@ const formRef = ref<FormInstance>();
 
 const form = reactive({
 	id: undefined as number | undefined,
-	companyRefId: 0,
-	factoryRefId: 0,
+	companyRefId: undefined,
+	factoryRefId: undefined,
 	domain: '',
 	enteringPer: undefined as number | undefined,
 	paintingPer: undefined as number | undefined,
@@ -128,7 +128,7 @@ const form = reactive({
 watch(
 	() => form.companyRefId,
 	() => {
-		if (!factoriesForCompany(form.companyRefId).some((x) => x.id === form.factoryRefId)) form.factoryRefId = 0;
+		if (!factoriesForCompany(form.companyRefId).some((x) => x.id === form.factoryRefId)) form.factoryRefId = undefined;
 	},
 );
 

+ 5 - 5
Web/src/views/aidop/s0/sales/ContractReviewCycleList.vue

@@ -146,8 +146,8 @@ const STAGE_NAME_MAP: Record<string, string> = {
 
 const query = reactive({
 	domainCode: '',
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	isActive: undefined as boolean | undefined,
 	page: 1,
 	pageSize: 20,
@@ -177,8 +177,8 @@ const formFactoryOptions = computed(() => {
 
 function emptyForm(): S0ContractReviewCycleUpsert {
 	return {
-		companyRefId: 0,
-		factoryRefId: 0,
+		companyRefId: undefined,
+		factoryRefId: undefined,
 		domainCode: '',
 		stageCode: '',
 		stageName: '',
@@ -214,7 +214,7 @@ watch(
 	() => form.companyRefId,
 	() => {
 		if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) {
-			form.factoryRefId = 0;
+			form.factoryRefId = undefined;
 		}
 	}
 );

+ 18 - 9
Web/src/views/aidop/s0/sales/CustomerList.vue

@@ -228,8 +228,8 @@ const pageTitle = computed(() => (route.meta?.title as string) || '客户管理'
 const query = reactive({
 	keyword: '',
 	domainCode: '',
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	isActive: undefined as boolean | undefined,
 	isConfirm: undefined as boolean | undefined,
 	page: 1,
@@ -251,8 +251,8 @@ const factoryOptions = ref<OrgOption[]>([]);
 const currencyOptions = ref<OptionItem[]>([]);
 
 type CustomerFormModel = Omit<S0CustMasterUpsert, 'companyRefId' | 'factoryRefId'> & {
-	companyRefId: number | undefined;
-	factoryRefId: number | undefined;
+	companyRefId: string | undefined;
+	factoryRefId: string | undefined;
 };
 
 const form = reactive<CustomerFormModel>({
@@ -282,8 +282,15 @@ const form = reactive<CustomerFormModel>({
 });
 
 function validateRequiredOrgField(message: string) {
-	return (_rule: unknown, value: number | undefined, callback: (error?: Error) => void) => {
-		if (typeof value !== 'number' || value <= 0) {
+	return (_rule: unknown, value: string | number | undefined | null, callback: (error?: Error) => void) => {
+		// 组织 ID 语义:已选=非空字符串(雪花 ID),未选=undefined/null/''
+		// 禁止 typeof === 'number' 假设,否则会拒绝合法的字符串雪花 ID
+		if (value == null) {
+			callback(new Error(message));
+			return;
+		}
+		const s = typeof value === 'string' ? value : String(value);
+		if (s === '' || s === '0') {
 			callback(new Error(message));
 			return;
 		}
@@ -358,7 +365,8 @@ function getCustomerSaveErrorMessage(error: any): string {
 }
 
 function buildPayload(): S0CustMasterUpsert | null {
-	if (!form.companyRefId || form.companyRefId <= 0 || !form.factoryRefId || form.factoryRefId <= 0) {
+	// 组织 ID 语义:非空字符串才算已选;undefined / '' / '0' 视为未选
+	if (!form.companyRefId || form.companyRefId === '0' || !form.factoryRefId || form.factoryRefId === '0') {
 		return null;
 	}
 
@@ -394,8 +402,9 @@ async function loadOptions() {
 		loadOrgList('501'),
 		loadDictOptions('s0_currency'),
 	]);
-	companyOptions.value = companies.filter((item) => item.id > 0);
-	factoryOptions.value = factories.filter((item) => item.id > 0);
+	// 组织 ID 为 string 雪花 ID,过滤掉空/伪 0 项
+	companyOptions.value = companies.filter((item) => item.id && item.id !== '0');
+	factoryOptions.value = factories.filter((item) => item.id && item.id !== '0');
 	currencyOptions.value = currencies;
 
 	if (!companies.length || !factories.length || !currencies.length) {

+ 5 - 5
Web/src/views/aidop/s0/sales/MaterialList.vue

@@ -256,8 +256,8 @@ const activeTab = ref('owner');
 const query = reactive({
 	keyword: '',
 	domainCode: '',
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	itemNum: '',
 	descr: '',
 	descr1: '',
@@ -287,8 +287,8 @@ const stockTypeOptions = ref<OptionItem[]>([]);
 
 function emptyForm(): S0ItemMasterUpsert {
 	return {
-		companyRefId: 0,
-		factoryRefId: 0,
+		companyRefId: undefined,
+		factoryRefId: undefined,
 		domainCode: '',
 		itemNum: '',
 		descr: '',
@@ -374,7 +374,7 @@ watch(
 	() => form.companyRefId,
 	() => {
 		if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) {
-			form.factoryRefId = 0;
+			form.factoryRefId = undefined;
 		}
 	}
 );

+ 5 - 5
Web/src/views/aidop/s0/sales/OrderPriorityRuleList.vue

@@ -175,8 +175,8 @@ const pageTitle = computed(() => (route.meta?.title as string) || '订单优先
 const query = reactive({
 	keyword: '',
 	domainCode: '',
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	descr: '',
 	priority: undefined as number | undefined,
 	sourceTable: '',
@@ -204,8 +204,8 @@ const factoryOptions = ref<OrgOption[]>([]);
 
 function emptyForm(): S0PriorityCodeUpsert {
 	return {
-		companyRefId: 0,
-		factoryRefId: 0,
+		companyRefId: undefined,
+		factoryRefId: undefined,
 		domainCode: '',
 		descr: '',
 		value: '',
@@ -253,7 +253,7 @@ watch(
 	() => form.companyRefId,
 	() => {
 		if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) {
-			form.factoryRefId = 0;
+			form.factoryRefId = undefined;
 		}
 	}
 );

+ 5 - 5
Web/src/views/aidop/s0/sales/OrderReviewCycleList.vue

@@ -131,8 +131,8 @@ const pageTitle = computed(() => (route.meta?.title as string) || '订单评审
 const query = reactive({
 	domainCode: '',
 	orderType: '',
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	isActive: undefined as boolean | undefined,
 	page: 1,
 	pageSize: 20,
@@ -162,8 +162,8 @@ const formFactoryOptions = computed(() => {
 
 function emptyForm(): S0OrderReviewCycleUpsert {
 	return {
-		companyRefId: 0,
-		factoryRefId: 0,
+		companyRefId: undefined,
+		factoryRefId: undefined,
 		domainCode: '',
 		orderType: '',
 		stdHours: 0,
@@ -191,7 +191,7 @@ watch(
 	() => form.companyRefId,
 	() => {
 		if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) {
-			form.factoryRefId = 0;
+			form.factoryRefId = undefined;
 		}
 	}
 );

+ 5 - 5
Web/src/views/aidop/s0/sales/ProductDesignCycleList.vue

@@ -140,8 +140,8 @@ const route = useRoute();
 const pageTitle = computed(() => (route.meta?.title as string) || '产品设计周期');
 
 const query = reactive({
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	itemType: '',
 	ownerApplication: '',
 	isActive: undefined as boolean | undefined,
@@ -174,8 +174,8 @@ const formFactoryOptions = computed(() => {
 
 function emptyForm(): S0ProductDesignCycleUpsert {
 	return {
-		companyRefId: 0,
-		factoryRefId: 0,
+		companyRefId: undefined,
+		factoryRefId: undefined,
 		domainCode: '',
 		itemType: '',
 		ownerApplication: '',
@@ -204,7 +204,7 @@ watch(
 	() => form.companyRefId,
 	() => {
 		if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) {
-			form.factoryRefId = 0;
+			form.factoryRefId = undefined;
 		}
 	}
 );

+ 7 - 7
Web/src/views/aidop/s0/supply/CategoryLeadTimeList.vue

@@ -147,8 +147,8 @@ const pageTitle = computed(() => (route.meta?.title as string) || '品类采购
 
 const query = reactive({
 	keyword: '',
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	isActive: undefined as boolean | undefined,
 	page: 1,
 	pageSize: 20,
@@ -167,8 +167,8 @@ const companyOptions = ref<OrgOption[]>([]);
 const factoryOptions = ref<OrgOption[]>([]);
 
 const form = reactive<S0CategoryLeadTimeUpsert>({
-	companyRefId: 0,
-	factoryRefId: 0,
+	companyRefId: undefined,
+	factoryRefId: undefined,
 	domainCode: '',
 	categoryCode: '',
 	categoryName: '',
@@ -207,7 +207,7 @@ watch(
 watch(
 	() => form.companyRefId,
 	() => {
-		if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) form.factoryRefId = 0;
+		if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) form.factoryRefId = undefined;
 	}
 );
 
@@ -257,8 +257,8 @@ function resetQuery() {
 function resetForm() {
 	editingId.value = null;
 	Object.assign(form, {
-		companyRefId: 0,
-		factoryRefId: 0,
+		companyRefId: undefined,
+		factoryRefId: undefined,
 		domainCode: '',
 		categoryCode: '',
 		categoryName: '',

+ 6 - 6
Web/src/views/aidop/s0/supply/MaterialPlanCycleList.vue

@@ -107,8 +107,8 @@ const route = useRoute();
 const pageTitle = computed(() => (route.meta?.title as string) || '物料计划周期');
 
 const query = reactive({
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	orderType: '',
 	isActive: undefined as boolean | undefined,
 	page: 1,
@@ -135,8 +135,8 @@ const formFactoryOptions = computed(() => {
 });
 
 const form = reactive<S0MaterialPlanCycleUpsert>({
-	companyRefId: 0,
-	factoryRefId: 0,
+	companyRefId: undefined,
+	factoryRefId: undefined,
 	domainCode: '',
 	orderType: '',
 	stdHours: 0,
@@ -153,7 +153,7 @@ const rules: FormRules = {
 };
 
 watch(() => form.companyRefId, () => {
-	if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) form.factoryRefId = 0;
+	if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) form.factoryRefId = undefined;
 });
 watch(() => query.companyRefId, () => {
 	if (!filteredFactoryOptions.value.some((item) => item.id === query.factoryRefId)) query.factoryRefId = undefined;
@@ -198,7 +198,7 @@ function resetQuery() {
 
 function resetForm() {
 	editingId.value = null;
-	Object.assign(form, { companyRefId: 0, factoryRefId: 0, domainCode: '', orderType: '', stdHours: 0, remarks: '', isActive: true, createUser: '', updateUser: '' });
+	Object.assign(form, { companyRefId: undefined, factoryRefId: undefined, domainCode: '', orderType: '', stdHours: 0, remarks: '', isActive: true, createUser: '', updateUser: '' });
 	formRef.value?.clearValidate();
 }
 

+ 7 - 7
Web/src/views/aidop/s0/supply/SourcingList.vue

@@ -286,8 +286,8 @@ const pageTitle = computed(() => (route.meta?.title as string) || '货源清单'
 const query = reactive({
 	keyword: '',
 	domainCode: '',
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	supplierType: '',
 	currencyType: '',
 	isActive: undefined as boolean | undefined,
@@ -318,8 +318,8 @@ const supplierLoading = ref(false);
 const defaultExpiring = '2099-01-01';
 
 const form = reactive<S0SrmPurchaseUpsert & { icitemId: number; supplierId: number }>({
-	companyRefId: 0,
-	factoryRefId: 0,
+	companyRefId: undefined,
+	factoryRefId: undefined,
 	domainCode: '',
 	icitemId: 0,
 	icitemName: '',
@@ -371,7 +371,7 @@ function syncDomainFromFactory() {
 
 function onFormCompanyChange() {
 	if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) {
-		form.factoryRefId = 0;
+		form.factoryRefId = undefined;
 	}
 }
 
@@ -495,8 +495,8 @@ function resetQuery() {
 function resetForm() {
 	editingId.value = null;
 	Object.assign(form, {
-		companyRefId: 0,
-		factoryRefId: 0,
+		companyRefId: undefined,
+		factoryRefId: undefined,
 		domainCode: '',
 		icitemId: 0,
 		icitemName: '',

+ 7 - 7
Web/src/views/aidop/s0/supply/SupplierList.vue

@@ -265,8 +265,8 @@ const pageTitle = computed(() => (route.meta?.title as string) || '供应商维
 const query = reactive({
 	keyword: '',
 	domainCode: '',
-	companyRefId: undefined as number | undefined,
-	factoryRefId: undefined as number | undefined,
+	companyRefId: undefined as string | undefined,
+	factoryRefId: undefined as string | undefined,
 	curr: '',
 	crTerms: '',
 	taxClass: '',
@@ -292,8 +292,8 @@ const crTermsOptions = ref<OptionItem[]>([]);
 const taxClassOptions = ref<OptionItem[]>([]);
 
 const form = reactive<S0SuppMasterUpsert>({
-	companyRefId: 0,
-	factoryRefId: 0,
+	companyRefId: undefined,
+	factoryRefId: undefined,
 	domainCode: '',
 	supp: '',
 	sortName: '',
@@ -343,7 +343,7 @@ watch(
 	() => form.companyRefId,
 	() => {
 		if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) {
-			form.factoryRefId = 0;
+			form.factoryRefId = undefined;
 		}
 	}
 );
@@ -417,8 +417,8 @@ function resetQuery() {
 function resetForm() {
 	editingId.value = null;
 	Object.assign(form, {
-		companyRefId: 0,
-		factoryRefId: 0,
+		companyRefId: undefined,
+		factoryRefId: undefined,
 		domainCode: '',
 		supp: '',
 		sortName: '',

+ 1 - 1
Web/src/views/aidop/s0/warehouse/BarcodeRuleList.vue

@@ -85,7 +85,7 @@ const saving = ref(false);
 const formRef = ref<FormInstance>();
 
 function emptyForm() {
-	return { companyRefId: 0, factoryRefId: 0, domainCode: '', customer: '', type: '', waterRules: '', waterLen: null as number | null, separator: '', firmLength: null as number | null, firmString1: '', firmStringLen1: null, firmString1Note: '', firmString2: '', firmStringLen2: null, firmString2Note: '', firmString3: '', firmStringLen3: null, firmString3Note: '', firmString4: '', firmStringLen4: null, firmString4Note: '', firmString5: '', firmStringLen5: null, firmString5Note: '', firmString6: '', firmStringLen6: null, firmString6Note: '', firmString7: '', firmStringLen7: null, firmString7Note: '', firmString8: '', firmStringLen8: null, firmString8Note: '', firmString9: '', firmStringLen9: null, firmString9Note: '', firmString10: '', firmStringLen10: null, firmString10Note: '', firmString11: '', firmStringLen11: null, firmString11Note: '', firmString12: '', firmStringLen12: null, firmString12Note: '' };
+	return { companyRefId: undefined, factoryRefId: undefined, domainCode: '', customer: '', type: '', waterRules: '', waterLen: null as number | null, separator: '', firmLength: null as number | null, firmString1: '', firmStringLen1: null, firmString1Note: '', firmString2: '', firmStringLen2: null, firmString2Note: '', firmString3: '', firmStringLen3: null, firmString3Note: '', firmString4: '', firmStringLen4: null, firmString4Note: '', firmString5: '', firmStringLen5: null, firmString5Note: '', firmString6: '', firmStringLen6: null, firmString6Note: '', firmString7: '', firmStringLen7: null, firmString7Note: '', firmString8: '', firmStringLen8: null, firmString8Note: '', firmString9: '', firmStringLen9: null, firmString9Note: '', firmString10: '', firmStringLen10: null, firmString10Note: '', firmString11: '', firmStringLen11: null, firmString11Note: '', firmString12: '', firmStringLen12: null, firmString12Note: '' };
 }
 const form = reactive(emptyForm());
 const rules: FormRules = {};

+ 2 - 2
Web/src/views/aidop/s0/warehouse/CostCenterList.vue

@@ -68,7 +68,7 @@ const dialogTitle = ref('');
 const editingId = ref<number | null>(null);
 const saving = ref(false);
 const formRef = ref<FormInstance>();
-const form = reactive<S0CostCtrUpsert>({ companyRefId: 0, factoryRefId: 0, domainCode: '', costCtr: '', descr: '', ufld1: '', effTime: null });
+const form = reactive<S0CostCtrUpsert>({ companyRefId: undefined, factoryRefId: undefined, domainCode: '', costCtr: '', descr: '', ufld1: '', effTime: null });
 const rules: FormRules = { costCtr: [{ required: true, message: '请填写成本中心编码', trigger: 'blur' }] };
 
 async function loadList() {
@@ -79,7 +79,7 @@ async function loadList() {
 	} catch { rows.value = []; total.value = 0; } finally { loading.value = false; }
 }
 function resetQuery() { Object.assign(query, { keyword: '', domainCode: '', page: 1 }); void loadList(); }
-function resetForm() { editingId.value = null; Object.assign(form, { companyRefId: 0, factoryRefId: 0, domainCode: '', costCtr: '', descr: '', ufld1: '', effTime: null }); formRef.value?.clearValidate(); }
+function resetForm() { editingId.value = null; Object.assign(form, { companyRefId: undefined, factoryRefId: undefined, domainCode: '', costCtr: '', descr: '', ufld1: '', effTime: null }); formRef.value?.clearValidate(); }
 function openCreate() { resetForm(); dialogTitle.value = '新增成本中心'; dialogVisible.value = true; }
 function openEdit(row: S0CostCtrRow) {
 	resetForm(); editingId.value = row.id; dialogTitle.value = `编辑成本中心 ${row.costCtr}`;

+ 2 - 2
Web/src/views/aidop/s0/warehouse/DepartmentList.vue

@@ -88,7 +88,7 @@ const editingId = ref<number | null>(null);
 const saving = ref(false);
 const formRef = ref<FormInstance>();
 
-const form = reactive<S0DepartmentUpsert>({ companyRefId: 0, factoryRefId: 0, domainCode: '', department: '', descr: '', isActive: true });
+const form = reactive<S0DepartmentUpsert>({ companyRefId: undefined, factoryRefId: undefined, domainCode: '', department: '', descr: '', isActive: true });
 
 const rules: FormRules = {
 	department: [{ required: true, message: '请填写部门编码', trigger: 'blur' }],
@@ -110,7 +110,7 @@ function resetQuery() {
 
 function resetForm() {
 	editingId.value = null;
-	Object.assign(form, { companyRefId: 0, factoryRefId: 0, domainCode: '', department: '', descr: '', isActive: true });
+	Object.assign(form, { companyRefId: undefined, factoryRefId: undefined, domainCode: '', department: '', descr: '', isActive: true });
 	formRef.value?.clearValidate();
 }
 

+ 1 - 1
Web/src/views/aidop/s0/warehouse/EmpWorkDutyList.vue

@@ -106,7 +106,7 @@ const editingId = ref<number | null>(null);
 const saving = ref(false);
 const formRef = ref<FormInstance>();
 const scopeMode = ref<'single' | 'range'>('range');
-const emptyF = (): S0EmpWorkDutyUpsert => ({ companyRefId: 0, factoryRefId: 0, domainCode: '', employee: '', itemNum: '', itemNum1: '', itemNum2: '', location: '', prodLine: '', duty: '', empType: '', ufld2: '' });
+const emptyF = (): S0EmpWorkDutyUpsert => ({ companyRefId: undefined, factoryRefId: undefined, domainCode: '', employee: '', itemNum: '', itemNum1: '', itemNum2: '', location: '', prodLine: '', duty: '', empType: '', ufld2: '' });
 const form = reactive<S0EmpWorkDutyUpsert>(emptyF());
 const rules: FormRules = { employee: [{ required: true, message: '请填写员工编码', trigger: 'blur' }] };
 

+ 2 - 2
Web/src/views/aidop/s0/warehouse/EmployeeList.vue

@@ -115,7 +115,7 @@ const editingId = ref<number | null>(null);
 const saving = ref(false);
 const formRef = ref<FormInstance>();
 
-const form = reactive<S0EmployeeUpsert>({ companyRefId: 0, factoryRefId: 0, domainCode: '', employee: '', name: '', sex: '', phone: '', email: '', department: '', jobTitle: '', employmentStatus: '', isActive: true });
+const form = reactive<S0EmployeeUpsert>({ companyRefId: undefined, factoryRefId: undefined, domainCode: '', employee: '', name: '', sex: '', phone: '', email: '', department: '', jobTitle: '', employmentStatus: '', isActive: true });
 
 const rules: FormRules = { employee: [{ required: true, message: '请填写员工编码', trigger: 'blur' }] };
 
@@ -130,7 +130,7 @@ async function loadList() {
 function resetQuery() { Object.assign(query, { keyword: '', domainCode: '', department: '', isActive: undefined, page: 1 }); void loadList(); }
 function resetForm() {
 	editingId.value = null;
-	Object.assign(form, { companyRefId: 0, factoryRefId: 0, domainCode: '', employee: '', name: '', sex: '', phone: '', email: '', department: '', jobTitle: '', employmentStatus: '', isActive: true });
+	Object.assign(form, { companyRefId: undefined, factoryRefId: undefined, domainCode: '', employee: '', name: '', sex: '', phone: '', email: '', department: '', jobTitle: '', employmentStatus: '', isActive: true });
 	formRef.value?.clearValidate();
 }
 function openCreate() { resetForm(); dialogTitle.value = '新增雇员'; dialogVisible.value = true; }

+ 1 - 1
Web/src/views/aidop/s0/warehouse/ItemPackList.vue

@@ -94,7 +94,7 @@ const dialogTitle = ref('');
 const editingId = ref<number | null>(null);
 const saving = ref(false);
 const formRef = ref<FormInstance>();
-const emptyF = (): S0ItemPackUpsert => ({ companyRefId: 0, factoryRefId: 0, domainCode: '', itemNum: '', packingQty: null, smallPackingQty: null, packingType: '', netWeight: null, weightUM: '', length: null, width: null, high: null, issSpecific: '', custItem: '', remark: '', isActive: true });
+const emptyF = (): S0ItemPackUpsert => ({ companyRefId: undefined, factoryRefId: undefined, domainCode: '', itemNum: '', packingQty: null, smallPackingQty: null, packingType: '', netWeight: null, weightUM: '', length: null, width: null, high: null, issSpecific: '', custItem: '', remark: '', isActive: true });
 const form = reactive<S0ItemPackUpsert>(emptyF());
 const rules: FormRules = { itemNum: [{ required: true, message: '请填写物料编码', trigger: 'blur' }] };
 

+ 2 - 2
Web/src/views/aidop/s0/warehouse/LabelTypeList.vue

@@ -69,7 +69,7 @@ const dialogTitle = ref('');
 const editingId = ref<number | null>(null);
 const saving = ref(false);
 const formRef = ref<FormInstance>();
-const form = reactive<S0LabelTypeUpsert>({ companyRefId: 0, factoryRefId: 0, domainCode: '', barType: '', class: '', inputString: '' });
+const form = reactive<S0LabelTypeUpsert>({ companyRefId: undefined, factoryRefId: undefined, domainCode: '', barType: '', class: '', inputString: '' });
 const rules: FormRules = { barType: [{ required: true, message: '请填写标签类型编码', trigger: 'blur' }] };
 
 async function loadList() {
@@ -80,7 +80,7 @@ async function loadList() {
 	} catch { rows.value = []; total.value = 0; } finally { loading.value = false; }
 }
 function resetQuery() { Object.assign(query, { keyword: '', domainCode: '', class: '', page: 1 }); void loadList(); }
-function resetForm() { editingId.value = null; Object.assign(form, { companyRefId: 0, factoryRefId: 0, domainCode: '', barType: '', class: '', inputString: '' }); formRef.value?.clearValidate(); }
+function resetForm() { editingId.value = null; Object.assign(form, { companyRefId: undefined, factoryRefId: undefined, domainCode: '', barType: '', class: '', inputString: '' }); formRef.value?.clearValidate(); }
 function openCreate() { resetForm(); dialogTitle.value = '新增标签格式'; dialogVisible.value = true; }
 function openEdit(row: S0LabelTypeRow) {
 	resetForm(); editingId.value = row.id; dialogTitle.value = `编辑标签格式 ${row.barType}`;

+ 2 - 2
Web/src/views/aidop/s0/warehouse/LocationList.vue

@@ -84,7 +84,7 @@ const dialogTitle = ref('');
 const editingId = ref<number | null>(null);
 const saving = ref(false);
 const formRef = ref<FormInstance>();
-const form = reactive<S0LocationUpsert>({ companyRefId: 0, factoryRefId: 0, domainCode: '', location: '', descr: '', storer: '', typed: '', physicalAddress: '', isActive: true });
+const form = reactive<S0LocationUpsert>({ companyRefId: undefined, factoryRefId: undefined, domainCode: '', location: '', descr: '', storer: '', typed: '', physicalAddress: '', isActive: true });
 const rules: FormRules = { location: [{ required: true, message: '请填写库位编码', trigger: 'blur' }] };
 
 async function loadList() {
@@ -95,7 +95,7 @@ async function loadList() {
 	} catch { rows.value = []; total.value = 0; } finally { loading.value = false; }
 }
 function resetQuery() { Object.assign(query, { keyword: '', domainCode: '', typed: '', isActive: undefined, page: 1 }); void loadList(); }
-function resetForm() { editingId.value = null; Object.assign(form, { companyRefId: 0, factoryRefId: 0, domainCode: '', location: '', descr: '', storer: '', typed: '', physicalAddress: '', isActive: true }); formRef.value?.clearValidate(); }
+function resetForm() { editingId.value = null; Object.assign(form, { companyRefId: undefined, factoryRefId: undefined, domainCode: '', location: '', descr: '', storer: '', typed: '', physicalAddress: '', isActive: true }); formRef.value?.clearValidate(); }
 function openCreate() { resetForm(); dialogTitle.value = '新增库位'; dialogVisible.value = true; }
 function openEdit(row: S0LocationRow) {
 	resetForm(); editingId.value = row.id; dialogTitle.value = `编辑库位 ${row.location}`;

+ 2 - 2
Web/src/views/aidop/s0/warehouse/LocationShelfList.vue

@@ -72,7 +72,7 @@ const dialogTitle = ref('');
 const editingId = ref<number | null>(null);
 const saving = ref(false);
 const formRef = ref<FormInstance>();
-const form = reactive<S0LocationShelfUpsert>({ companyRefId: 0, factoryRefId: 0, domainCode: '', location: '', invShelf: '', descr: '', area: '' });
+const form = reactive<S0LocationShelfUpsert>({ companyRefId: undefined, factoryRefId: undefined, domainCode: '', location: '', invShelf: '', descr: '', area: '' });
 const rules: FormRules = {
 	location: [{ required: true, message: '请填写所属库位', trigger: 'blur' }],
 	invShelf: [{ required: true, message: '请填写货架编码', trigger: 'blur' }],
@@ -85,7 +85,7 @@ async function loadList() {
 	} catch { rows.value = []; total.value = 0; } finally { loading.value = false; }
 }
 function resetQuery() { Object.assign(query, { keyword: '', domainCode: '', location: '', page: 1 }); void loadList(); }
-function resetForm() { editingId.value = null; Object.assign(form, { companyRefId: 0, factoryRefId: 0, domainCode: '', location: '', invShelf: '', descr: '', area: '' }); formRef.value?.clearValidate(); }
+function resetForm() { editingId.value = null; Object.assign(form, { companyRefId: undefined, factoryRefId: undefined, domainCode: '', location: '', invShelf: '', descr: '', area: '' }); formRef.value?.clearValidate(); }
 function openCreate() { resetForm(); dialogTitle.value = '新增货架'; dialogVisible.value = true; }
 function openEdit(row: S0LocationShelfRow) {
 	resetForm(); editingId.value = row.id; dialogTitle.value = `编辑货架 ${row.kwhjName || row.invShelf}`;

+ 1 - 1
Web/src/views/aidop/s0/warehouse/NbrControlList.vue

@@ -91,7 +91,7 @@ const dialogTitle = ref('');
 const editingId = ref<number | null>(null);
 const saving = ref(false);
 const formRef = ref<FormInstance>();
-const emptyForm = (): S0NbrControlUpsert => ({ companyRefId: 0, factoryRefId: 0, domainCode: '', nbrType: '', description: '', nbrPre1: '', nbrPre2: '', nbrPre3: '', iniValue: null, minValue: null, maxValue: null, allowReset: false, allowSkip: false, allowManual: false, dateType: '', isDateType: false });
+const emptyForm = (): S0NbrControlUpsert => ({ companyRefId: undefined, factoryRefId: undefined, domainCode: '', nbrType: '', description: '', nbrPre1: '', nbrPre2: '', nbrPre3: '', iniValue: null, minValue: null, maxValue: null, allowReset: false, allowSkip: false, allowManual: false, dateType: '', isDateType: false });
 const form = reactive<S0NbrControlUpsert>(emptyForm());
 const rules: FormRules = { nbrType: [{ required: true, message: '请填写单号类型编码', trigger: 'blur' }] };
 

+ 2 - 2
Web/src/views/aidop/s0/warehouse/NbrTypeList.vue

@@ -83,7 +83,7 @@ const dialogTitle = ref('');
 const editingId = ref<number | null>(null);
 const saving = ref(false);
 const formRef = ref<FormInstance>();
-const form = reactive<S0NbrTypeUpsert>({ companyRefId: 0, factoryRefId: 0, domainCode: '', nbrType: '', descr1: '', nbrClass: '', dept: '', isActive: true });
+const form = reactive<S0NbrTypeUpsert>({ companyRefId: undefined, factoryRefId: undefined, domainCode: '', nbrType: '', descr1: '', nbrClass: '', dept: '', isActive: true });
 const rules: FormRules = { nbrType: [{ required: true, message: '请填写单号类型编码', trigger: 'blur' }] };
 
 async function loadList() {
@@ -94,7 +94,7 @@ async function loadList() {
 	} catch { rows.value = []; total.value = 0; } finally { loading.value = false; }
 }
 function resetQuery() { Object.assign(query, { keyword: '', domainCode: '', dept: '', isActive: undefined, page: 1 }); void loadList(); }
-function resetForm() { editingId.value = null; Object.assign(form, { companyRefId: 0, factoryRefId: 0, domainCode: '', nbrType: '', descr1: '', nbrClass: '', dept: '', isActive: true }); formRef.value?.clearValidate(); }
+function resetForm() { editingId.value = null; Object.assign(form, { companyRefId: undefined, factoryRefId: undefined, domainCode: '', nbrType: '', descr1: '', nbrClass: '', dept: '', isActive: true }); formRef.value?.clearValidate(); }
 function openCreate() { resetForm(); dialogTitle.value = '新增单号类型'; dialogVisible.value = true; }
 function openEdit(row: S0NbrTypeRow) {
 	resetForm(); editingId.value = row.id; dialogTitle.value = `编辑单号类型 ${row.nbrType}`;

+ 1 - 1
Web/src/views/aidop/s0/warehouse/TaskAssignmentList.vue

@@ -91,7 +91,7 @@ const dialogTitle = ref('');
 const editingId = ref<number | null>(null);
 const saving = ref(false);
 const formRef = ref<FormInstance>();
-const emptyF = (): S0TaskAssignmentUpsert => ({ companyRefId: 0, factoryRefId: 0, domainCode: '', tcrq: null, sqr: '', rwlx: '', wlbm: '', sl: null, rqpc: '', yskw: '', mdkw: '', xqsj: null, clr: '', bz: '', zt: '' });
+const emptyF = (): S0TaskAssignmentUpsert => ({ companyRefId: undefined, factoryRefId: undefined, domainCode: '', tcrq: null, sqr: '', rwlx: '', wlbm: '', sl: null, rqpc: '', yskw: '', mdkw: '', xqsj: null, clr: '', bz: '', zt: '' });
 const form = reactive<S0TaskAssignmentUpsert>(emptyF());
 const rules: FormRules = {};
 

+ 147 - 0
Web/tests/e2e/s0-org-id-sampling.spec.ts

@@ -0,0 +1,147 @@
+/**
+ * S0 组织 ID 类型一致性|运行时取样(简化版)
+ *
+ * 目标:不做 Vue 内部反射,只靠「UI 驱动 + 网络请求体 + 可见错误文案」留证。
+ * 每个样本页面分别记录:
+ *   - 打开新增弹窗后拦截到的 GET /api/sysOrg/list 响应体中 id 的 JSON 原文类型
+ *   - 选完公司/工厂后,点保存捕获的 POST/PUT 请求体里 companyRefId / factoryRefId 的 JSON 原文类型
+ *   - 可见错误提示文案(form-item error 或 el-message)
+ *
+ * 本轮只收集证据;结论写入 artifacts/S0-org-id-runtime-sampling-report.md。
+ */
+import { test, expect } from './fixtures/auth';
+import type { Page, Request, Response } from '@playwright/test';
+
+interface Capture {
+  orgListIdSample: string | null; // raw JSON substring, e.g. `"id":"1300000000001"` vs `"id":1300000000001`
+  saveRequestBody: string | null; // raw JSON body
+  saveStatus: number | null;
+  saveResponseBody: string | null;
+  visibleErrors: string[];
+  consoleErrors: string[];
+}
+
+function attachCaptures(page: Page, cap: Capture): void {
+  page.on('response', async (resp: Response) => {
+    try {
+      const url = resp.url();
+      if (/\/api\/sysOrg\/list/.test(url) && cap.orgListIdSample === null) {
+        const txt = await resp.text();
+        const m = txt.match(/"id":\s*("[^"]+"|\d+)/);
+        if (m) cap.orgListIdSample = m[0];
+      }
+    } catch {}
+  });
+  page.on('request', async (req: Request) => {
+    const url = req.url();
+    const method = req.method();
+    if (!/\/api\/s0\//.test(url)) return;
+    if (!['POST', 'PUT'].includes(method)) return;
+    if (cap.saveRequestBody === null) cap.saveRequestBody = req.postData() ?? '';
+  });
+  page.on('console', (m) => {
+    if (m.type() === 'error') cap.consoleErrors.push(m.text());
+  });
+  page.on('pageerror', (e) => cap.consoleErrors.push(`pageerror: ${e.message}`));
+}
+
+async function clickAddButtonInContent(page: Page, name: string): Promise<boolean> {
+  // Scope to the active tab content, not global — avoids picking home-page buttons.
+  const scope = page.locator('.el-main, .layout-view-bg-white, .app-main, main').first();
+  const scoped = scope.isVisible().catch(() => false);
+  const search = (await scoped) ? scope : page;
+  const btn = search.getByRole('button', { name }).first();
+  if (!(await btn.isVisible({ timeout: 10_000 }).catch(() => false))) return false;
+  await btn.click();
+  await page.waitForTimeout(700);
+  return true;
+}
+
+async function pickFirstInSelect(page: Page, selectLocator: string, nth: number): Promise<string | null> {
+  const select = page.locator(selectLocator).nth(nth);
+  if (!(await select.isVisible().catch(() => false))) return null;
+  const wrapper = select.locator('.el-select__wrapper, .select-trigger').first();
+  await wrapper.click({ force: true });
+  await page.waitForTimeout(300);
+  const opt = page
+    .locator('.el-select-dropdown:visible .el-select-dropdown__item:not(.is-disabled)')
+    .first();
+  if (!(await opt.isVisible({ timeout: 5000 }).catch(() => false))) return null;
+  const text = (await opt.innerText()).trim();
+  await opt.click({ force: true }).catch(() => {});
+  await page.waitForTimeout(400);
+  return text;
+}
+
+const SAMPLES: Array<{ name: string; hash: string; addBtnName: RegExp | string }> = [
+  { name: 'sales/CustomerList (Pattern A, baseline)', hash: '#/aidop/s0/sales/customer', addBtnName: /^新增客户$/ },
+  { name: 'sales/MaterialList (Pattern B+C, form default=0)', hash: '#/aidop/s0/sales/material', addBtnName: /新 ?增/ },
+  { name: 'sales/OrderReviewCycleList (Pattern B+C, form default=0)', hash: '#/aidop/s0/sales/order-review-cycle', addBtnName: /^新 ?增$/ },
+  { name: 'manufacturing/ProductionLineList (cascade)', hash: '#/aidop/s0/manufacturing/production-line', addBtnName: /^新 ?增$/ },
+  { name: 'supply/SupplierList (cross-domain)', hash: '#/aidop/s0/supply/supplier', addBtnName: /新 ?增/ },
+];
+
+for (const s of SAMPLES) {
+  test(`runtime sample: ${s.name}`, async ({ authedPage }) => {
+    const page = authedPage;
+    const cap: Capture = {
+      orgListIdSample: null,
+      saveRequestBody: null,
+      saveStatus: null,
+      saveResponseBody: null,
+      visibleErrors: [],
+      consoleErrors: [],
+    };
+    attachCaptures(page, cap);
+
+    await page.goto(`/${s.hash}`, { waitUntil: 'domcontentloaded' });
+    await page.waitForLoadState('networkidle', { timeout: 20_000 }).catch(() => {});
+    await page.waitForTimeout(1000);
+
+    const opened = await clickAddButtonInContent(
+      page,
+      typeof s.addBtnName === 'string' ? s.addBtnName : (s.addBtnName as any),
+    );
+    console.log(`[${s.name}] dialog opened: ${opened}`);
+    await page.waitForTimeout(800);
+
+    if (opened) {
+      // Pick first and second el-select inside the visible dialog.
+      const selInDialog = '.el-dialog:visible .el-select, .el-drawer:visible .el-select';
+      const company = await pickFirstInSelect(page, selInDialog, 0);
+      const factory = await pickFirstInSelect(page, selInDialog, 1);
+      console.log(`[${s.name}] picked company=${company} factory=${factory}`);
+
+      // Attempt to click Save. Response listener is already attached via attachCaptures.
+      const saveBtn = page
+        .locator('.el-dialog:visible, .el-drawer:visible')
+        .getByRole('button', { name: /^(确 ?定|保 ?存|提 ?交)$/ })
+        .first();
+      if (await saveBtn.isVisible().catch(() => false)) {
+        const respPromise = page
+          .waitForResponse(
+            (r) => /\/api\/s0\//.test(r.url()) && ['POST', 'PUT'].includes(r.request().method()),
+            { timeout: 6000 },
+          )
+          .catch(() => null);
+        await saveBtn.click();
+        const resp = await respPromise;
+        if (resp) {
+          cap.saveStatus = resp.status();
+          cap.saveResponseBody = (await resp.text()).slice(0, 400);
+        }
+        await page.waitForTimeout(600);
+        const errs = await page
+          .locator('.el-form-item__error, .el-message--error, .el-message__content')
+          .allInnerTexts()
+          .catch(() => []);
+        cap.visibleErrors = errs.map((x) => x.trim()).filter(Boolean);
+      } else {
+        console.log(`[${s.name}] save button not visible`);
+      }
+    }
+
+    console.log(`\n=== EVIDENCE: ${s.name} ===\n${JSON.stringify(cap, null, 2)}\n`);
+    expect(true).toBe(true);
+  });
+}

+ 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.59</AssemblyVersion>
-    <FileVersion>1.0.59</FileVersion>
-    <Version>1.0.59</Version>
+    <AssemblyVersion>1.0.60</AssemblyVersion>
+    <FileVersion>1.0.60</FileVersion>
+    <Version>1.0.60</Version>
   </PropertyGroup>
 
   <ItemGroup>