| | |
| | | type WarehouseArea, |
| | | type WarehouseLocation, |
| | | } from "../../../api/wls"; |
| | | import { getWorkOrderBomListByTaskId, type WorkOrderBom } from "../../../api/production"; |
| | | import { useAuth } from "../../../stores/auth"; |
| | | import { dateText, dayText, materialOptionText } from "./shared"; |
| | | |
| | |
| | | const lines = ref<WlsDocLine[]>([]); |
| | | const details = ref<Record<number, WlsDocDetail[]>>({}); |
| | | const materials = ref<MaterialItem[]>([]); |
| | | // 生产领料:当前任务所在工序的投入物料(BOM),行物料只能在此范围内选择 |
| | | const taskBoms = ref<WorkOrderBom[]>([]); |
| | | const stocks = ref<MaterialStock[]>([]); |
| | | const workOrders = ref<WlsWorkOrder[]>([]); |
| | | const tasks = ref<WlsTask[]>([]); |
| | |
| | | const moduleName = computed(() => config.value.module as "product-issue" | "product-receipt" | "sales-notice" | "return-issue" | "product-sales" | "return-sales"); |
| | | const detailModule = computed(() => config.value.module as "product-issue" | "product-receipt" | "return-issue" | "product-sales" | "return-sales"); |
| | | const materialOptions = computed(() => materials.value.map(materialOptionText)); |
| | | // 生产领料:选中任务后,行物料只能从该任务工序的投入物料(BOM)中选择 |
| | | const issueMaterialOptions = computed(() => { |
| | | if (props.kind !== "product-issue") return materialOptions.value; |
| | | if (!form.taskId) return []; |
| | | return taskBoms.value.map(item => `${item.itemCode || "--"} - ${item.itemName || "--"} / ${item.itemSpecification || "无规格"} / ${item.unitMeasureName || "无单位"}`); |
| | | }); |
| | | const issueMaterialPlaceholder = computed(() => { |
| | | if (props.kind !== "product-issue") return "请选择物料"; |
| | | if (!form.taskId) return "请先选择生产任务"; |
| | | return taskBoms.value.length ? "请选择当前工序物料" : "当前工序未维护投入物料"; |
| | | }); |
| | | const stockOptions = computed(() => stocks.value.map(item => `${item.itemCode || "--"} - ${item.itemName || "--"} / ${item.specification || "无规格"} / ${item.unitMeasureName || "无单位"} / ${item.batchCode || "无批次"} / ${item.warehouseName || "--"}-${item.locationName || "--"}-${item.areaName || "--"} / 可用${item.availableQuantity ?? item.quantity ?? 0}`)); |
| | | const workOrderOptions = computed(() => workOrders.value.map(item => `${item.code || "--"} - ${item.name || item.itemName || "--"}`)); |
| | | const taskOptions = computed(() => tasks.value.map(item => `${item.taskCode || item.code || "--"} - ${item.taskName || item.name || "--"} / ${item.workstationName || "无工作站"}`)); |
| | |
| | | : await getReturnSales(state.accessToken, id.value); |
| | | clearForm(); Object.assign(form, data); |
| | | if (form.workOrderId) await loadTasks(); |
| | | if (form.taskId) await loadTaskBoms(form.taskId); |
| | | if (form.saleOrderId) await loadSaleOrderItems(form.saleOrderId); |
| | | if (form.noticeId) await loadNoticeLines(); |
| | | const dataLines = await getWlsLinePage(state.accessToken, moduleName.value, { pageNo: 1, pageSize: 100, [lineOwnerKey.value]: id.value }); |
| | |
| | | await loadTasks(); |
| | | await loadLineSources(); |
| | | } |
| | | async function loadTaskBoms(taskId?: number) { |
| | | taskBoms.value = []; |
| | | if (!state.accessToken || props.kind !== "product-issue" || !taskId) return; |
| | | taskBoms.value = await getWorkOrderBomListByTaskId(state.accessToken, taskId); |
| | | } |
| | | function selectTask(event: { detail: { value: number } }) { |
| | | const row = tasks.value[Number(event.detail.value)]; |
| | | form.taskId = row?.id; form.taskCode = row?.taskCode || row?.code; form.taskName = row?.taskName || row?.name; |
| | | form.workstationId = row?.workstationId; form.workstationCode = row?.workstationCode; form.workstationName = row?.workstationName; |
| | | void loadTaskBoms(form.taskId); |
| | | } |
| | | function selectWorkstation(event: { detail: { value: number } }) { |
| | | const row = workstations.value[Number(event.detail.value)]; |
| | |
| | | await loadNoticeLines(); |
| | | } |
| | | function selectMaterial(event: { detail: { value: number } }, line: WlsDocLine) { |
| | | // 生产领料且已选任务:物料来源于当前任务工序投入物料(BOM) |
| | | if (props.kind === "product-issue" && form.taskId) { |
| | | const bom = taskBoms.value[Number(event.detail.value)]; |
| | | Object.assign(line, { itemId: bom?.itemId, itemCode: bom?.itemCode, itemName: bom?.itemName, specification: bom?.itemSpecification, unitMeasureName: bom?.unitMeasureName }); |
| | | return; |
| | | } |
| | | const row = materials.value[Number(event.detail.value)]; |
| | | Object.assign(line, { itemId: row?.id, itemCode: row?.code, itemName: row?.name, specification: row?.specification, unitMeasureName: row?.unitMeasureName }); |
| | | } |
| | |
| | | <view v-else-if="kind === 'product-receipt'" class="field"><text>库存记录 <text class="required">*</text></text><picker v-if="!readonly" :range="stockOptions" @change="selectStockLine($event,line)"><view class="picker"><text>{{ line.materialStockId ? `${lineMaterialText(line)} / ${line.batchCode || '无批次'}` : '请选择库存记录' }}</text><text>›</text></view></picker><text v-else class="value">{{ line.materialStockId ? lineMaterialText(line) : '--' }}</text></view> |
| | | <view v-else-if="kind === 'return-issue' && returnableLines.length" class="field"><text>领料记录 <text class="required">*</text></text><picker v-if="!readonly" :range="returnableLineOptions" @change="selectReturnableLine($event,line)"><view class="picker"><text>{{ line.issueLineId ? `${lineMaterialText(line)} / ${line.batchCode || '无批次'}` : '请选择领料记录' }}</text><text>›</text></view></picker><text v-else class="value">{{ line.issueLineId ? lineMaterialText(line) : '--' }}</text></view> |
| | | <view v-else-if="kind === 'return-sales' && saleOrderItems.length" class="field"><text>物料 <text class="required">*</text></text><picker v-if="!readonly" :range="saleOrderItems.map(i => `${i.productCode || '--'} / ${i.productName || '--'} / ${i.productSpecification || '无规格'} / ${i.productUnitName || '无单位'}`)" @change="selectSaleOrderItem($event,line)"><view class="picker"><text>{{ lineMaterialText(line) }}</text><text>›</text></view></picker><text v-else class="value">{{ line.itemId ? lineMaterialText(line) : '--' }}</text></view> |
| | | <view v-else class="field"><text>{{ lineMaterialLabel() }} <text class="required">*</text></text><picker v-if="!readonly" :range="materialOptions" @change="selectMaterial($event,line)"><view class="picker"><text>{{ lineMaterialText(line) }}</text><text>›</text></view></picker><text v-else class="value">{{ line.itemId ? lineMaterialText(line) : '--' }}</text></view> |
| | | <view v-else class="field"><text>{{ lineMaterialLabel() }} <text class="required">*</text></text><picker v-if="!readonly" :range="issueMaterialOptions" :disabled="kind === 'product-issue' && !issueMaterialOptions.length" @change="selectMaterial($event,line)"><view class="picker"><text :class="{ 'picker-placeholder': !line.itemId }">{{ line.itemId ? lineMaterialText(line) : issueMaterialPlaceholder }}</text><text>›</text></view></picker><text v-else class="value">{{ line.itemId ? lineMaterialText(line) : '--' }}</text></view> |
| | | <view class="field"><text>规格型号</text><text class="value">{{ line.specification || '--' }}</text></view> |
| | | <view class="field"><text>单位</text><text class="value">{{ line.unitMeasureName || '--' }}</text></view> |
| | | <view class="field"><text>{{ config.quantity }} <text class="required">*</text></text><input :value="String(line.quantity || '')" :disabled="readonly" type="digit" :placeholder="`请输入${config.quantity}`" @input="line.quantity=num($event.detail.value)" /></view> |
| | |
| | | </template> |
| | | |
| | | <style lang="scss" scoped> |
| | | .page{min-height:100vh;padding:20rpx 24rpx 60rpx;box-sizing:border-box;background:#f3f6fb}.form-card{margin-bottom:18rpx;background:#fff;border-radius:16rpx;overflow:hidden}.field{display:flex;align-items:center;min-height:86rpx;padding:0 22rpx;border-bottom:1rpx solid #edf1f6;box-sizing:border-box}.field>text:first-child{flex:none;width:180rpx;color:#10233f;font-size:26rpx}.field>picker{display:block;flex:1;min-width:0}.field input,.field textarea,.value,.picker{flex:1;min-width:0;color:#10233f;font-size:26rpx;text-align:right}.field textarea{height:120rpx;padding:22rpx 0}.field.column{align-items:flex-start;flex-direction:column;padding:22rpx}.field.column>text:first-child{width:auto;margin-bottom:12rpx}.field.column textarea{text-align:left;width:100%;height:120rpx;padding:0}.required{color:#e23b3b}.picker{display:flex;width:100%;align-items:center;justify-content:space-between;gap:10rpx}.picker text:first-child{min-width:0;flex:1;overflow:hidden;text-align:right;text-overflow:ellipsis;white-space:nowrap}.picker text:last-child{flex:none;width:24rpx;text-align:right}.code-field input{text-align:left}.code-field button,.section button,.line-head button,.detail-actions button{height:52rpx;min-height:52rpx;padding:0 16rpx;margin:0;color:var(--color-primary, #1769d8);font-size:23rpx;line-height:50rpx;background:var(--color-primary-soft, #f5f8ff);border:1rpx solid var(--color-primary-border, #8ab3ee);border-radius:8rpx}.code-field button::after,.section button::after,.line-head button::after,.detail-actions button::after,.save::after,.submit::after,.modal-actions button::after{border:0}.section{display:flex;align-items:center;justify-content:space-between;height:82rpx;padding:0 22rpx;color:#10233f;font-size:30rpx;font-weight:600}.line-card{margin:0 18rpx 18rpx;border:1rpx solid #edf1f6;border-radius:12rpx;overflow:hidden}.line-head{display:flex;align-items:center;justify-content:space-between;padding:16rpx 18rpx;color:#10233f;font-size:27rpx;font-weight:600;background:#f9fbff}.line-head>view{display:flex;gap:10rpx}.danger{color:#c65050!important;background:#fff7f7!important;border-color:#e3a0a0!important}.detail-list{padding:10rpx 18rpx 16rpx;border-top:1rpx solid #edf1f6}.detail-row{display:flex;align-items:flex-start;justify-content:space-between;gap:14rpx;padding:14rpx 0;color:#66778c;font-size:23rpx;border-bottom:1rpx solid #f2f4f8}.detail-info{display:flex;min-width:0;flex:1;flex-direction:column;gap:8rpx}.detail-info text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.detail-actions{display:flex;flex:none;align-items:center;justify-content:flex-end;gap:10rpx}.detail-actions button{width:84rpx;padding:0}.empty{padding:34rpx;color:#8795a8;font-size:25rpx;text-align:center}.save,.submit{width:100%;height:78rpx;margin:18rpx 0 0;color:#fff;font-size:28rpx;line-height:78rpx;background:var(--color-primary, #1769d8);border-radius:10rpx}.submit{background:#16a36f}.mask{position:fixed;left:0;right:0;top:0;bottom:0;z-index:60;display:flex;align-items:flex-end;background:rgba(0,0,0,.35)}.modal{width:100%;max-height:86vh;padding:0 24rpx 28rpx;box-sizing:border-box;background:#fff;border-radius:24rpx 24rpx 0 0}.modal-title{height:88rpx;line-height:88rpx;color:#10233f;font-size:30rpx;font-weight:600;text-align:center}.modal-actions{display:flex;gap:18rpx;padding-top:24rpx}.modal-actions button{flex:1;height:72rpx;line-height:72rpx;font-size:27rpx;border-radius:10rpx}.modal-actions .primary{color:#fff;background:var(--color-primary, #1769d8)} |
| | | .page{min-height:100vh;padding:20rpx 24rpx 60rpx;box-sizing:border-box;background:#f3f6fb}.form-card{margin-bottom:18rpx;background:#fff;border-radius:16rpx;overflow:hidden}.field{display:flex;align-items:center;min-height:86rpx;padding:0 22rpx;border-bottom:1rpx solid #edf1f6;box-sizing:border-box}.field>text:first-child{flex:none;width:180rpx;color:#10233f;font-size:26rpx}.field>picker{display:block;flex:1;min-width:0}.field input,.field textarea,.value,.picker{flex:1;min-width:0;color:#10233f;font-size:26rpx;text-align:right}.field textarea{height:120rpx;padding:22rpx 0}.field.column{align-items:flex-start;flex-direction:column;padding:22rpx}.field.column>text:first-child{width:auto;margin-bottom:12rpx}.field.column textarea{text-align:left;width:100%;height:120rpx;padding:0}.required{color:#e23b3b}.picker{display:flex;width:100%;align-items:center;justify-content:space-between;gap:10rpx}.picker text:first-child{min-width:0;flex:1;overflow:hidden;text-align:right;text-overflow:ellipsis;white-space:nowrap}.picker text:last-child{flex:none;width:24rpx;text-align:right}.code-field input{text-align:left}.code-field button,.section button,.line-head button,.detail-actions button{height:52rpx;min-height:52rpx;padding:0 16rpx;margin:0;color:var(--color-primary, #1769d8);font-size:23rpx;line-height:50rpx;background:var(--color-primary-soft, #f5f8ff);border:1rpx solid var(--color-primary-border, #8ab3ee);border-radius:8rpx}.code-field button::after,.section button::after,.line-head button::after,.detail-actions button::after,.save::after,.submit::after,.modal-actions button::after{border:0}.section{display:flex;align-items:center;justify-content:space-between;height:82rpx;padding:0 22rpx;color:#10233f;font-size:30rpx;font-weight:600}.line-card{margin:0 18rpx 18rpx;border:1rpx solid #edf1f6;border-radius:12rpx;overflow:hidden}.line-head{display:flex;align-items:center;justify-content:space-between;padding:16rpx 18rpx;color:#10233f;font-size:27rpx;font-weight:600;background:#f9fbff}.line-head>view{display:flex;gap:10rpx}.danger{color:#c65050!important;background:#fff7f7!important;border-color:#e3a0a0!important}.detail-list{padding:10rpx 18rpx 16rpx;border-top:1rpx solid #edf1f6}.detail-row{display:flex;align-items:flex-start;justify-content:space-between;gap:14rpx;padding:14rpx 0;color:#66778c;font-size:23rpx;border-bottom:1rpx solid #f2f4f8}.detail-info{display:flex;min-width:0;flex:1;flex-direction:column;gap:8rpx}.detail-info text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.detail-actions{display:flex;flex:none;align-items:center;justify-content:flex-end;gap:10rpx}.detail-actions button{width:84rpx;padding:0}.empty{padding:34rpx;color:#8795a8;font-size:25rpx;text-align:center}.save,.submit{width:100%;height:78rpx;margin:18rpx 0 0;color:#fff;font-size:28rpx;line-height:78rpx;background:var(--color-primary, #1769d8);border-radius:10rpx}.submit{background:#16a36f}.mask{position:fixed;left:0;right:0;top:0;bottom:0;z-index:60;display:flex;align-items:flex-end;background:rgba(0,0,0,.35)}.modal{width:100%;max-height:86vh;padding:0 24rpx 28rpx;box-sizing:border-box;background:#fff;border-radius:24rpx 24rpx 0 0}.modal-title{height:88rpx;line-height:88rpx;color:#10233f;font-size:30rpx;font-weight:600;text-align:center}.modal-actions{display:flex;gap:18rpx;padding-top:24rpx}.modal-actions button{flex:1;height:72rpx;line-height:72rpx;font-size:27rpx;border-radius:10rpx}.modal-actions .primary{color:#fff;background:var(--color-primary, #1769d8)}.picker-placeholder{color:#9aa6b5} |
| | | </style> |