|
|
@@ -93,245 +93,38 @@
|
|
|
/>
|
|
|
</div>
|
|
|
|
|
|
- <el-drawer v-model="detailVisible" title="过程检验单" size="62%" :destroy-on-close="true">
|
|
|
- <div v-loading="detailLoading">
|
|
|
- <el-descriptions :column="3" border size="small" title="检验单信息">
|
|
|
- <el-descriptions-item label="工单编号">{{ detail?.workOrderNo || '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="批次号">{{ detail?.lotSerial || '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="物料编码">{{ detail?.itemCode || '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="物料名称">{{ detail?.itemName || '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="规格型号">{{ detail?.specification || '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="检规编号">{{ detail?.specCode || '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="检规版本">{{ detail?.specVersion || '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="生效日期">{{ detail?.effectiveDate || '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="整单状态">{{ detail?.overallStatus || '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="整单结果">
|
|
|
- <el-tag v-if="detail?.overallResult" :type="judgeTag(detail.overallResult)" size="small" disable-transitions>{{ detail.overallResult }}</el-tag>
|
|
|
- <span v-else>—</span>
|
|
|
- </el-descriptions-item>
|
|
|
- </el-descriptions>
|
|
|
-
|
|
|
- <div class="snap-title">检验录入 · 自动判定(无人工选检规;判定以后端为准)</div>
|
|
|
- <el-table :data="detail?.lines || []" border stripe size="small" max-height="400" row-key="id">
|
|
|
- <el-table-column type="index" label="#" width="42" align="center" />
|
|
|
- <el-table-column prop="processCode" label="工序" min-width="66" show-overflow-tooltip />
|
|
|
- <el-table-column prop="inspectionItem" label="检验项目" min-width="110" show-overflow-tooltip />
|
|
|
- <el-table-column label="录入类型" min-width="96" align="center">
|
|
|
- <template #default="{ row }">
|
|
|
- <el-tag size="small" :type="row.resultType === 'NUMERIC' ? 'success' : 'info'" disable-transitions>{{ row.resultType || '—' }}</el-tag>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="标准 / 上下限" min-width="130">
|
|
|
- <template #default="{ row }">
|
|
|
- <span v-if="row.resultType === 'NUMERIC'">[{{ row.lowerLimit ?? '' }} , {{ row.upperLimit ?? '' }}]</span>
|
|
|
- <span v-else>{{ row.techStandard || row.specValue || '—' }}</span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="inspectionFrequency" label="频次" min-width="70" show-overflow-tooltip />
|
|
|
- <el-table-column label="实测录入" min-width="300">
|
|
|
- <template #default="{ row }">
|
|
|
- <template v-if="row.resultType === 'NUMERIC'">
|
|
|
- <span v-for="i in (row.sampleCount || 1)" :key="i" class="sample-cell">
|
|
|
- <el-input v-model="entry[key(row.id, i)]" size="small" style="width: 74px" :placeholder="'样' + i" :disabled="reviewLocked" />
|
|
|
- <el-tag v-if="sampleJudge(row, i)" size="small" :type="judgeTag(sampleJudge(row, i))" disable-transitions>{{ shortJ(sampleJudge(row, i)) }}</el-tag>
|
|
|
- </span>
|
|
|
- </template>
|
|
|
- <template v-else>
|
|
|
- <el-select v-model="entry[key(row.id, 1)]" size="small" style="width: 100px" placeholder="OK / NG" clearable :disabled="reviewLocked">
|
|
|
- <el-option label="OK" value="OK" />
|
|
|
- <el-option label="NG" value="NG" />
|
|
|
- </el-select>
|
|
|
- </template>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="单项结果" min-width="108" align="center">
|
|
|
- <template #default="{ row }">
|
|
|
- <el-tag :type="judgeTag(row.itemResult)" size="small" disable-transitions>{{ row.itemResult || '—' }}</el-tag>
|
|
|
- <div class="item-status">{{ row.itemStatus || '' }}</div>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
-
|
|
|
- <div class="drawer-foot" v-if="!reviewLocked">
|
|
|
- <span class="foot-hint">判定以后端为准</span>
|
|
|
- <el-button :loading="saving" @click="onSave">保存</el-button>
|
|
|
- <el-button type="primary" :loading="completing" @click="onComplete">完成检验</el-button>
|
|
|
- </div>
|
|
|
- <div class="drawer-foot" v-else>
|
|
|
- <span class="foot-hint">已提交审核,检验结果只读</span>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="review-panel" v-if="detail">
|
|
|
- <div class="snap-title">审核流程 · 复用 ApprovalFlow(质量结果只读,审批不改写)</div>
|
|
|
- <div class="review-row">
|
|
|
- 检验结果:<el-tag :type="judgeTag(detail?.overallResult)" size="small" disable-transitions>{{ review?.qualityText || detail?.overallResult || '—' }}</el-tag>
|
|
|
- <span class="rv-sep">审核状态:</span><b>{{ review?.displayStatus || '—' }}</b>
|
|
|
- <span v-if="review?.currentNodeName" class="rv-sep">当前节点:{{ review.currentNodeName }}</span>
|
|
|
- </div>
|
|
|
- <div v-if="review?.disposition" class="review-disp">
|
|
|
- 质量处置:<el-tag type="warning" size="small" disable-transitions>{{ review.disposition.dispositionTypeText || review.disposition.dispositionType }}</el-tag>
|
|
|
- <span v-if="review.disposition.dispositionOpinion" class="rv-sep">意见:{{ review.disposition.dispositionOpinion }}</span>
|
|
|
- <span v-if="review.disposition.operatorName" class="rv-sep">处置人:{{ review.disposition.operatorName }}</span>
|
|
|
- <span v-if="review.disposition.submittedAt" class="rv-sep">{{ fmtDate(review.disposition.submittedAt) }}</span>
|
|
|
- </div>
|
|
|
- <div v-if="review?.history && review.history.length" class="review-history">
|
|
|
- <div class="hist-title">审核历史</div>
|
|
|
- <el-timeline>
|
|
|
- <el-timeline-item v-for="(h, i) in review.history" :key="i" :timestamp="fmtDateTime(h.time)" placement="top" size="small">
|
|
|
- <span class="hist-node">{{ h.nodeName }}</span> · {{ h.actionText }} <span class="hist-op">{{ h.operatorName }}</span>
|
|
|
- <span v-if="h.comment" class="rv-sep">意见:{{ h.comment }}</span>
|
|
|
- </el-timeline-item>
|
|
|
- </el-timeline>
|
|
|
- </div>
|
|
|
- <div class="review-actions">
|
|
|
- <el-button v-if="review?.canSubmitReview" type="primary" :loading="reviewing" @click="onSubmitReview">提交审核</el-button>
|
|
|
- <el-button v-if="review?.canSupervisorApprove" type="success" :loading="reviewing" @click="onSupervisorApprove">主管通过</el-button>
|
|
|
- <el-button v-if="review?.canSupervisorReturn" :loading="reviewing" @click="onSupervisorReturn">退回</el-button>
|
|
|
- <template v-if="review?.canSubmitDisposition">
|
|
|
- <el-select v-model="dispType" size="small" style="width: 150px" placeholder="处置类型">
|
|
|
- <el-option label="返工 REWORK" value="REWORK" />
|
|
|
- <el-option label="让步 CONCESSION" value="CONCESSION" />
|
|
|
- <el-option label="报废 SCRAP" value="SCRAP" />
|
|
|
- <el-option label="其它 OTHER" value="OTHER" />
|
|
|
- </el-select>
|
|
|
- <el-input v-model="dispOpinion" size="small" style="width: 220px" placeholder="处置意见(必填)" />
|
|
|
- <el-button type="warning" :loading="reviewing" @click="onQeDisposition">提交处置</el-button>
|
|
|
- </template>
|
|
|
- <el-tag v-if="review?.isCompleted" :type="review?.flowStatus === 'Approved' ? 'success' : 'info'" size="small" disable-transitions>{{ review?.flowStatus === 'Approved' ? '审核已闭环 (CLOSED)' : review?.flowStatus }}</el-tag>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </el-drawer>
|
|
|
</AidopDemoShell>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
+/**
|
|
|
+ * S6 生产指令单列表 —— 过程检验的「待检来源」。
|
|
|
+ *
|
|
|
+ * 原先这里还内嵌一个 el-drawer 承担完整的过程检验单操作(逐样本录入、保存、完成检验、
|
|
|
+ * 提交审核、主管通过/退回、QE 处置、审核历史)。该 Drawer 已退役:检查单现在有
|
|
|
+ * 独立 Tab「IPQC 检查单」与独立详情页,完整操作在那里完成。
|
|
|
+ * 本页回归单一职责 —— 只读的待检工单列表 + 一个进入检查单的入口。
|
|
|
+ *
|
|
|
+ * 行操作与 deep-link 的业务语义未变:已建单直接进详情;未建单先调 prepare
|
|
|
+ * (后端自动唯一解析 S0 检规、幂等建单 + 冻结快照),MATCHED 才跳、其余明确阻断。
|
|
|
+ */
|
|
|
import { reactive, ref, computed } from 'vue';
|
|
|
-import { useRoute } from 'vue-router';
|
|
|
+import { useRoute, useRouter } from 'vue-router';
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
import AidopDemoShell from '/@/views/aidop/components/AidopDemoShell.vue';
|
|
|
import {
|
|
|
fetchProductionInstructionPage,
|
|
|
prepareProcessInspection,
|
|
|
- fetchProcessInspectionDetail,
|
|
|
- saveProcessInspectionResult,
|
|
|
- completeProcessInspection,
|
|
|
- fetchReviewState,
|
|
|
- submitReview,
|
|
|
- reviewSupervisorApprove,
|
|
|
- reviewSupervisorReturn,
|
|
|
- reviewQeDisposition,
|
|
|
type ProductionInstructionRow,
|
|
|
- type ProcessInspectionDetail,
|
|
|
- type ProcessInspectionSnapshotLine,
|
|
|
- type SaveResultSample,
|
|
|
- type ReviewState,
|
|
|
} from '../api/productionInstruction';
|
|
|
|
|
|
+const IPQC_DETAIL_PATH = '/aidop/s6/process-quality/ipqc-bill-detail';
|
|
|
+
|
|
|
+const router = useRouter();
|
|
|
const loading = ref(false);
|
|
|
const rows = ref<ProductionInstructionRow[]>([]);
|
|
|
const total = ref(0);
|
|
|
-
|
|
|
const inspecting = ref<string>('');
|
|
|
-const detailVisible = ref(false);
|
|
|
-const detailLoading = ref(false);
|
|
|
-const detail = ref<ProcessInspectionDetail | null>(null);
|
|
|
-
|
|
|
-// Phase 1C 录入模型:key=`${snapshotLineId}#${sampleIndex}` → 实测值字符串(NUMERIC 数值串 / NON_NUMERIC OK|NG)。
|
|
|
-// 前端仅用于即时反馈与提交实测值,判定/整单结果一律以后端计算为准(不提交 judgement)。
|
|
|
-const entry = reactive<Record<string, string>>({});
|
|
|
-const saving = ref(false);
|
|
|
-const completing = ref(false);
|
|
|
-
|
|
|
-// Phase 1D 审核流状态(后端拥有;前端只触发动作、不回传状态/判定)
|
|
|
-const review = ref<ReviewState | null>(null);
|
|
|
-const reviewing = ref(false);
|
|
|
-const dispType = ref('');
|
|
|
-const dispOpinion = ref('');
|
|
|
-
|
|
|
-// 已提交审核后检验结果只读(§14 CLOSED / §15 IN_REVIEW):流程一旦发起即锁录入。
|
|
|
-const reviewLocked = computed(() => {
|
|
|
- const fs = review.value?.flowStatus;
|
|
|
- return !!fs && fs !== 'NotStarted';
|
|
|
-});
|
|
|
-
|
|
|
-function fmtDateTime(v?: string) {
|
|
|
- if (!v) return '—';
|
|
|
- return String(v).replace('T', ' ').slice(0, 19);
|
|
|
-}
|
|
|
-
|
|
|
-async function loadReview(billId: number) {
|
|
|
- try {
|
|
|
- review.value = await fetchReviewState(billId);
|
|
|
- } catch {
|
|
|
- review.value = null;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-async function onSubmitReview() {
|
|
|
- const billId = detail.value?.billId;
|
|
|
- if (!billId || reviewing.value) return;
|
|
|
- reviewing.value = true;
|
|
|
- try {
|
|
|
- review.value = await submitReview(billId);
|
|
|
- ElMessage.success('已提交审核');
|
|
|
- } catch (e: any) {
|
|
|
- const msg = e?.response?.data?.message || e?.message || '';
|
|
|
- ElMessage.error(String(msg).includes('INSPECTION_NOT_COMPLETED') ? '检验未完成,无法提交审核' : '提交审核失败');
|
|
|
- await loadReview(billId);
|
|
|
- } finally {
|
|
|
- reviewing.value = false;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-async function onSupervisorApprove() {
|
|
|
- const billId = detail.value?.billId;
|
|
|
- if (!billId || reviewing.value) return;
|
|
|
- reviewing.value = true;
|
|
|
- try {
|
|
|
- review.value = await reviewSupervisorApprove(billId);
|
|
|
- ElMessage.success('主管审核通过');
|
|
|
- } catch {
|
|
|
- ElMessage.error('主管审核失败');
|
|
|
- await loadReview(billId);
|
|
|
- } finally {
|
|
|
- reviewing.value = false;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-async function onSupervisorReturn() {
|
|
|
- const billId = detail.value?.billId;
|
|
|
- if (!billId || reviewing.value) return;
|
|
|
- try {
|
|
|
- const { value } = await ElMessageBox.prompt('退回意见(必填)', '退回', { inputPattern: /\S+/, inputErrorMessage: '意见必填' });
|
|
|
- reviewing.value = true;
|
|
|
- review.value = await reviewSupervisorReturn(billId, value);
|
|
|
- ElMessage.success('已退回');
|
|
|
- } catch (e) {
|
|
|
- if (e !== 'cancel') ElMessage.error('退回失败');
|
|
|
- } finally {
|
|
|
- reviewing.value = false;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-async function onQeDisposition() {
|
|
|
- const billId = detail.value?.billId;
|
|
|
- if (!billId || reviewing.value) return;
|
|
|
- if (!dispType.value) return ElMessage.warning('请选择处置类型');
|
|
|
- if (!dispOpinion.value?.trim()) return ElMessage.warning('处置意见必填');
|
|
|
- reviewing.value = true;
|
|
|
- try {
|
|
|
- review.value = await reviewQeDisposition({ billId, dispositionType: dispType.value, dispositionOpinion: dispOpinion.value.trim() });
|
|
|
- ElMessage.success('已提交质量处置');
|
|
|
- dispType.value = '';
|
|
|
- dispOpinion.value = '';
|
|
|
- } catch {
|
|
|
- ElMessage.error('提交处置失败');
|
|
|
- await loadReview(billId);
|
|
|
- } finally {
|
|
|
- reviewing.value = false;
|
|
|
- }
|
|
|
-}
|
|
|
|
|
|
const query = reactive({
|
|
|
page: 1,
|
|
|
@@ -404,7 +197,7 @@ async function loadList() {
|
|
|
});
|
|
|
rows.value = res?.list ?? [];
|
|
|
total.value = res?.total ?? 0;
|
|
|
- } catch (e) {
|
|
|
+ } catch {
|
|
|
rows.value = [];
|
|
|
total.value = 0;
|
|
|
ElMessage.error('生产指令单列表加载失败');
|
|
|
@@ -435,14 +228,22 @@ function onSizeChange() {
|
|
|
|
|
|
// Phase 1B:点击 → 后端自动唯一解析检规(无人工选择)。
|
|
|
// MATCHED 幂等生成/复用检验单 + 快照 → 打开录入详情;其余状态明确阻断、不建单。
|
|
|
+/**
|
|
|
+ * 进入检查单:已建单直接跳独立详情页;未建单先 prepare(后端自动唯一解析检规、幂等建单)。
|
|
|
+ * MATCHED 才跳转,其余状态明确阻断、不建单 —— 与退役前的 Drawer 入口行为逐条一致。
|
|
|
+ */
|
|
|
async function onProcessInspect(row: ProductionInstructionRow) {
|
|
|
+ if (row.billId) {
|
|
|
+ router.push(`${IPQC_DETAIL_PATH}/${row.billId}`);
|
|
|
+ return;
|
|
|
+ }
|
|
|
const wo = row.workOrderNo;
|
|
|
if (!wo || inspecting.value) return;
|
|
|
inspecting.value = wo;
|
|
|
try {
|
|
|
const r = await prepareProcessInspection(wo);
|
|
|
if (r?.status === 'MATCHED' && r.billId) {
|
|
|
- await openDetail(r.billId);
|
|
|
+ router.push(`${IPQC_DETAIL_PATH}/${r.billId}`);
|
|
|
} else if (r?.status === 'NO_MATCH') {
|
|
|
await ElMessageBox.alert(r.message || '当前物料未配置有效过程检验规范,请维护 S0 过程检验规范。', '无法生成过程检验单', { type: 'warning' });
|
|
|
} else if (r?.status === 'AMBIGUOUS') {
|
|
|
@@ -459,133 +260,13 @@ async function onProcessInspect(row: ProductionInstructionRow) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 录入模型 key:snapshotLineId + sampleIndex(1-based)。
|
|
|
-function key(lineId?: number, sampleIndex?: number) {
|
|
|
- return `${lineId ?? 0}#${sampleIndex ?? 0}`;
|
|
|
-}
|
|
|
-
|
|
|
-// 前端即时判定(仅反馈,非真值):NUMERIC 闭区间;NON_NUMERIC OK→PASS/NG→FAIL;其余 INVALID。
|
|
|
-function sampleJudge(row: ProcessInspectionSnapshotLine, i: number): string {
|
|
|
- const raw = (entry[key(row.id, i)] || '').trim();
|
|
|
- if (!raw) return '';
|
|
|
- if (row.resultType === 'NUMERIC') {
|
|
|
- const n = Number(raw);
|
|
|
- if (raw === '' || !Number.isFinite(n)) return 'INVALID';
|
|
|
- const lo = row.lowerLimit != null && String(row.lowerLimit).trim() !== '' ? Number(row.lowerLimit) : null;
|
|
|
- const up = row.upperLimit != null && String(row.upperLimit).trim() !== '' ? Number(row.upperLimit) : null;
|
|
|
- if (lo === null && up === null) return 'INVALID';
|
|
|
- if ((lo !== null && n < lo) || (up !== null && n > up)) return 'FAIL';
|
|
|
- return 'PASS';
|
|
|
- }
|
|
|
- const u = raw.toUpperCase();
|
|
|
- if (u === 'OK') return 'PASS';
|
|
|
- if (u === 'NG') return 'FAIL';
|
|
|
- return 'INVALID';
|
|
|
-}
|
|
|
-
|
|
|
-function judgeTag(result?: string): 'success' | 'danger' | 'info' | 'warning' {
|
|
|
- if (result === 'PASS') return 'success';
|
|
|
- if (result === 'FAIL') return 'danger';
|
|
|
- if (result === 'INVALID') return 'warning';
|
|
|
- return 'info';
|
|
|
-}
|
|
|
-
|
|
|
-function shortJ(result: string): string {
|
|
|
- if (result === 'PASS') return '✓';
|
|
|
- if (result === 'FAIL') return '✗';
|
|
|
- if (result === 'INVALID') return '!';
|
|
|
- return '';
|
|
|
-}
|
|
|
-
|
|
|
-// 由录入模型构造提交样本:仅提交非空实测值;后端按快照配置判定并计算整单结果。
|
|
|
-function buildSamples(): SaveResultSample[] {
|
|
|
- const out: SaveResultSample[] = [];
|
|
|
- for (const line of detail.value?.lines ?? []) {
|
|
|
- const cnt = line.resultType === 'NUMERIC' ? line.sampleCount || 1 : 1;
|
|
|
- for (let i = 1; i <= cnt; i++) {
|
|
|
- const raw = (entry[key(line.id, i)] || '').trim();
|
|
|
- if (!raw) continue;
|
|
|
- out.push({ snapshotLineId: line.id as number, sampleIndex: i, actualValue: raw });
|
|
|
- }
|
|
|
- }
|
|
|
- return out;
|
|
|
-}
|
|
|
-
|
|
|
-// 从后端已录样本初始化录入模型(刷新可恢复)。
|
|
|
-function initEntry() {
|
|
|
- for (const k of Object.keys(entry)) delete entry[k];
|
|
|
- for (const line of detail.value?.lines ?? []) {
|
|
|
- for (const s of line.samples ?? []) {
|
|
|
- const val = line.resultType === 'NUMERIC' ? (s.actualNumeric != null ? String(s.actualNumeric) : '') : s.actualNonNumeric || '';
|
|
|
- if (val !== '' && s.sampleIndex != null) entry[key(line.id, s.sampleIndex)] = val;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-async function openDetail(billId: number) {
|
|
|
- detailVisible.value = true;
|
|
|
- detailLoading.value = true;
|
|
|
- detail.value = null;
|
|
|
- try {
|
|
|
- detail.value = await fetchProcessInspectionDetail(billId);
|
|
|
- initEntry();
|
|
|
- await loadReview(billId);
|
|
|
- } catch {
|
|
|
- ElMessage.error('检验单详情加载失败');
|
|
|
- } finally {
|
|
|
- detailLoading.value = false;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-async function reloadDetail() {
|
|
|
- const billId = detail.value?.billId;
|
|
|
- if (!billId) return;
|
|
|
- detail.value = await fetchProcessInspectionDetail(billId);
|
|
|
- initEntry();
|
|
|
- await loadReview(billId);
|
|
|
-}
|
|
|
-
|
|
|
-async function onSave() {
|
|
|
- const billId = detail.value?.billId;
|
|
|
- if (!billId || saving.value) return;
|
|
|
- saving.value = true;
|
|
|
- try {
|
|
|
- await saveProcessInspectionResult(billId, buildSamples());
|
|
|
- await reloadDetail();
|
|
|
- } catch {
|
|
|
- ElMessage.error('保存失败');
|
|
|
- } finally {
|
|
|
- saving.value = false;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-async function onComplete() {
|
|
|
- const billId = detail.value?.billId;
|
|
|
- if (!billId || completing.value) return;
|
|
|
- completing.value = true;
|
|
|
- try {
|
|
|
- await saveProcessInspectionResult(billId, buildSamples());
|
|
|
- const r = await completeProcessInspection(billId);
|
|
|
- await reloadDetail();
|
|
|
- ElMessage.success(`已完成检验:整单 ${r?.overallResult || detail.value?.overallResult || '—'}`);
|
|
|
- } catch (e: any) {
|
|
|
- const msg = e?.response?.data?.message || e?.message || '';
|
|
|
- if (String(msg).includes('INSPECTION_INCOMPLETE')) {
|
|
|
- ElMessage.warning('尚有检验项未按频次录满,无法完成检验。');
|
|
|
- } else {
|
|
|
- ElMessage.error('完成检验失败');
|
|
|
- }
|
|
|
- await reloadDetail();
|
|
|
- } finally {
|
|
|
- completing.value = false;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
const route = useRoute();
|
|
|
loadList();
|
|
|
-// Deep-link(Approval Center / URL 刷新):?billId=X 直接打开对应单据详情,不依赖列表内存 state。
|
|
|
+
|
|
|
+// 兼容既有 deep-link:审批中心与旧书签仍指向 `order-list?billId=X`。
|
|
|
+// Drawer 已退役,这里原地重定向到独立详情页 —— 旧链接继续可用,无需改审批中心。
|
|
|
const deepBillId = Number(route.query.billId);
|
|
|
-if (deepBillId > 0) openDetail(deepBillId);
|
|
|
+if (deepBillId > 0) router.replace(`${IPQC_DETAIL_PATH}/${deepBillId}`);
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
@@ -597,71 +278,4 @@ if (deepBillId > 0) openDetail(deepBillId);
|
|
|
display: flex;
|
|
|
justify-content: flex-end;
|
|
|
}
|
|
|
-.snap-title {
|
|
|
- margin: 14px 0 8px;
|
|
|
- font-weight: 600;
|
|
|
- font-size: 14px;
|
|
|
-}
|
|
|
-.sample-cell {
|
|
|
- display: inline-flex;
|
|
|
- align-items: center;
|
|
|
- gap: 3px;
|
|
|
- margin: 2px 6px 2px 0;
|
|
|
-}
|
|
|
-.item-status {
|
|
|
- font-size: 11px;
|
|
|
- color: #909399;
|
|
|
- margin-top: 2px;
|
|
|
-}
|
|
|
-.drawer-foot {
|
|
|
- margin-top: 14px;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: flex-end;
|
|
|
- gap: 10px;
|
|
|
-}
|
|
|
-.foot-hint {
|
|
|
- color: #909399;
|
|
|
- font-size: 12px;
|
|
|
- margin-right: auto;
|
|
|
-}
|
|
|
-.review-panel {
|
|
|
- margin-top: 16px;
|
|
|
- padding-top: 12px;
|
|
|
- border-top: 1px solid #ebeef5;
|
|
|
-}
|
|
|
-.review-row,
|
|
|
-.review-disp {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- flex-wrap: wrap;
|
|
|
- gap: 4px;
|
|
|
- margin: 6px 0;
|
|
|
- font-size: 13px;
|
|
|
-}
|
|
|
-.rv-sep {
|
|
|
- margin-left: 12px;
|
|
|
- color: #606266;
|
|
|
-}
|
|
|
-.review-actions {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- flex-wrap: wrap;
|
|
|
- gap: 8px;
|
|
|
- margin-top: 10px;
|
|
|
-}
|
|
|
-.review-history {
|
|
|
- margin-top: 12px;
|
|
|
-}
|
|
|
-.hist-title {
|
|
|
- font-weight: 600;
|
|
|
- font-size: 13px;
|
|
|
- margin-bottom: 8px;
|
|
|
-}
|
|
|
-.hist-node {
|
|
|
- font-weight: 600;
|
|
|
-}
|
|
|
-.hist-op {
|
|
|
- color: #409eff;
|
|
|
-}
|
|
|
</style>
|