Sfoglia il codice sorgente

feat(s0): add business-code lookups and legacy-value compatibility to routing / production line

S0 REMAINING-3 的前端部分。三个页面的读取与回显合同全部收口;
写路径另有后端遗留校验问题,见下方说明,本提交不触碰后端。

【routing】
- 物料编码:自由文本 -> ItemMaster 业务编码下拉(value = ItemNum,非内部主键)
- 工作中心:自由文本 -> WorkCtrMaster 业务编码下拉(value = WorkCtr,非内部主键)
- 历史遗留工作中心(ZJ01 / ZJ02 / ZJ03,均不在当前 WorkCtrMaster 中):
  仅在 Edit 反显时以「(历史值)」形式补入当前值,保证能正确显示、不被清空、
  不被自动替换;Create 时该值为空,标准选项集不受污染。
- StdOp 不在表单中暴露,故 36 条 StdOp='0010' 不受前端影响;
  实测普通行保存后 StdOp / Op 均原样保留(该表 Update 走
  AsUpdateable(已加载实体),不是删除+重插,故未暴露列不会丢)。

【production-line】
- 主库位既有的当前值兼容选项补上「(历史值)」标注,
  使 T01 的 '脱包车间周转仓' 这类历史描述文本一眼可辨、不与合法库位编码混淆。

【shared】
- 新增 ensureCurrentOption(options, value, label?):当前值不在标准 options 时
  临时追加「(历史值)」选项。仅服务于 Edit 当前值回显,不污染 Create 选项集,
  且返回新数组不改入参。

数据零变更:nbr-control 91 / routing 155 / production-line 20 保持不变;
36 条 StdOp='0010'、3 条 ZJ0x、T01 的 '脱包车间周转仓' 均未改动。
未新增 migration,DB migration max 仍为 1.0.412。

⚠️ 未闭环(需业务拍板,故本批不改后端):
routing 的 91HC0497 全部 36 行(CompanyRefId/FactoryRefId = 1000/1000)与
production-line 的 T01,在 no-op 保存时均被后端引用校验拒绝(400):
  - RoutingOpDetail: 物料 scope 降级门禁要求原记录 scope 为 0/0,这 36 行为 1000/1000
  - ProductionLine : LocationExistsAsync 对历史库位文本无「值未变更」豁免
两者都是既有校验策略问题,与本次前端改动无关(改动前同样保存失败)。

chore: bump version Web 2.4.336
YY968XX 11 ore fa
parent
commit
c5b2a252ee

+ 1 - 1
Web/package.json

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

+ 16 - 0
Web/src/views/aidop/s0/composables/useS0BusinessLookups.ts

@@ -104,6 +104,22 @@ export function loadS0Lookup(key: S0LookupKey): Promise<S0LookupOption[]> {
 	return hit;
 	return hit;
 }
 }
 
 
+/**
+ * Edit 当前值兼容:若当前值不在标准 options 中(历史遗留数据),
+ * 临时把它作为一个「(历史值)」选项追加进去,使 Edit 能正确反显并原值保存。
+ *
+ * 约束:
+ * - **只用于 Edit / 当前值回显**,绝不能污染 Create 的标准选项集;
+ * - 不修改传入数组,返回新数组(保持不可变);
+ * - 空值 / 已存在则原样返回。
+ */
+export function ensureCurrentOption(options: S0LookupOption[], value?: string | null, label?: string): S0LookupOption[] {
+	const v = value == null ? '' : String(value).trim();
+	if (!v) return options;
+	if (options.some((o) => o.value === v)) return options;
+	return [...options, { value: v, label: label ?? `${v}(历史值)` }];
+}
+
 /** 新增/编辑成功后调用,避免下拉停留在旧数据上。 */
 /** 新增/编辑成功后调用,避免下拉停留在旧数据上。 */
 export function invalidateS0Lookups(): void {
 export function invalidateS0Lookups(): void {
 	cache.clear();
 	cache.clear();

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

@@ -130,10 +130,16 @@
 						<el-form-item label="主库位">
 						<el-form-item label="主库位">
 							<el-select v-model="form.location" clearable filterable placeholder="请选择主库位" style="width: 100%">
 							<el-select v-model="form.location" clearable filterable placeholder="请选择主库位" style="width: 100%">
 								<el-option v-for="item in lineLocationOptions" :key="item.value" :label="item.label" :value="item.value" />
 								<el-option v-for="item in lineLocationOptions" :key="item.value" :label="item.label" :value="item.value" />
+								<!--
+									历史遗留库位兼容:T01 的 Location 存的是「脱包车间周转仓」这类描述文本,
+									不是合法 Location 编码,故不在标准 options 中。仅在当前值存在时补一个
+									「(历史值)」选项,保证 Edit 能正确反显并原值保存;Create 时 form.location
+									为空,此选项不出现,标准选项集不被污染。
+								-->
 								<el-option
 								<el-option
 									v-if="form.location && !lineLocationOptions.some((o) => o.value === form.location)"
 									v-if="form.location && !lineLocationOptions.some((o) => o.value === form.location)"
 									:key="`__compat_${form.location}`"
 									:key="`__compat_${form.location}`"
-									:label="form.location"
+									:label="`${form.location}(历史值)`"
 									:value="form.location"
 									:value="form.location"
 								/>
 								/>
 							</el-select>
 							</el-select>

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

@@ -87,7 +87,11 @@
 						<el-form-item label="路线名称" prop="routeName"><el-input v-model="form.routeName" /></el-form-item>
 						<el-form-item label="路线名称" prop="routeName"><el-input v-model="form.routeName" /></el-form-item>
 					</el-col>
 					</el-col>
 					<el-col :span="8">
 					<el-col :span="8">
-						<el-form-item label="物料编码" prop="materialCode"><el-input v-model="form.materialCode" /></el-form-item>
+						<el-form-item label="物料编码" prop="materialCode">
+							<el-select v-model="form.materialCode" filterable clearable placeholder="请选择物料" style="width: 100%">
+								<el-option v-for="o in materialOptions" :key="o.value" :label="o.label" :value="o.value" />
+							</el-select>
+						</el-form-item>
 					</el-col>
 					</el-col>
 					<el-col :span="8">
 					<el-col :span="8">
 						<el-form-item label="工序编码" prop="operationCode"><el-input v-model="form.operationCode" /></el-form-item>
 						<el-form-item label="工序编码" prop="operationCode"><el-input v-model="form.operationCode" /></el-form-item>
@@ -96,7 +100,11 @@
 						<el-form-item label="工序名称"><el-input v-model="form.operationDescription" /></el-form-item>
 						<el-form-item label="工序名称"><el-input v-model="form.operationDescription" /></el-form-item>
 					</el-col>
 					</el-col>
 					<el-col :span="8">
 					<el-col :span="8">
-						<el-form-item label="工作中心"><el-input v-model="form.workCenterCode" /></el-form-item>
+						<el-form-item label="工作中心">
+							<el-select v-model="form.workCenterCode" filterable clearable placeholder="请选择工作中心" style="width: 100%">
+								<el-option v-for="o in workCenterOptions" :key="o.value" :label="o.label" :value="o.value" />
+							</el-select>
+						</el-form-item>
 					</el-col>
 					</el-col>
 					<el-col :span="8">
 					<el-col :span="8">
 						<el-form-item label="报工工序"><el-input v-model="form.milestoneOperation" /></el-form-item>
 						<el-form-item label="报工工序"><el-input v-model="form.milestoneOperation" /></el-form-item>
@@ -161,6 +169,23 @@
 </template>
 </template>
 
 
 <script setup lang="ts" name="aidopS0MfgRouting">
 <script setup lang="ts" name="aidopS0MfgRouting">
+import { computed as __computed_lk, reactive as __reactive_lk } from 'vue';
+import { ensureCurrentOption, loadS0Lookup, type S0LookupOption } from '../composables/useS0BusinessLookups';
+
+// 物料 / 工作中心改为业务编码下拉:
+//   物料     -> ItemMaster.ItemNum
+//   工作中心 -> WorkCtrMaster.WorkCtr
+// 均不保存内部主键 / 雪花 id。
+const __lk = __reactive_lk<{ item: S0LookupOption[]; workCenter: S0LookupOption[] }>({ item: [], workCenter: [] });
+void loadS0Lookup('item').then((r) => (__lk.item = r));
+void loadS0Lookup('workCenter').then((r) => (__lk.workCenter = r));
+
+const materialOptions = __computed_lk(() => __lk.item);
+// 历史遗留工作中心(如 ZJ01/ZJ02/ZJ03)不在当前合法 WorkCenter 中:
+// 仅在 Edit 反显时把当前值作为「(历史值)」临时选项补进去,保证能原值保存;
+// Create 时 form.workCenterCode 为空,故标准选项集不受污染。
+const workCenterOptions = __computed_lk(() => ensureCurrentOption(__lk.workCenter, form.workCenterCode));
+
 import { computed, onMounted, reactive, ref } from 'vue';
 import { computed, onMounted, reactive, ref } from 'vue';
 import { useRoute } from 'vue-router';
 import { useRoute } from 'vue-router';
 import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus';
 import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus';