You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3352 lines
112 KiB
3352 lines
112 KiB
<template>
|
|
<div class="customer-css">
|
|
<el-dialog :title="titleCon" v-drag v-bind="$attrs" v-on="$listeners" width="830px" style="height: 680px;" class="customer-dialog" :show-close="!exceptionRuleLocked" :close-on-press-escape="!exceptionRuleLocked" :before-close="handleMainDialogBeforeClose" @close="onDialogClosed">
|
|
<el-form :inline="true" label-position="top" style="height: auto;">
|
|
<!-- 时间和固定载具 -->
|
|
<el-row style="margin-top: -10px;">
|
|
<el-col :span="11">
|
|
<el-form-item :label=labels.currentTime>
|
|
<el-date-picker style="width: 110px;"
|
|
v-model="pageData.reportDate"
|
|
format="yyyy-MM-dd"
|
|
value-format="yyyy-MM-dd"
|
|
placeholder="">
|
|
</el-date-picker>
|
|
</el-form-item>
|
|
<el-form-item :label="''" style="margin-top: 23px">
|
|
<el-time-picker style="width: 100px;"
|
|
v-model="pageData.reportTime"
|
|
format="HH:mm:ss"
|
|
value-format="HH:mm:ss"
|
|
placeholder="">
|
|
</el-time-picker>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item>
|
|
<span style="cursor: pointer" slot="label" @click="queryFixedCarrierList"><a>固定载具</a></span>
|
|
<el-input v-model="pageData.fixture" readonly style="width: 120px;" placeholder="固定载具">
|
|
<i v-if="pageData.fixture"
|
|
slot="suffix"
|
|
class="el-icon-circle-close fixture-clear-btn"
|
|
@click="pageData.fixture = ''">
|
|
</i>
|
|
</el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="3">
|
|
<el-form-item label="排数">
|
|
<el-input-number :controls="false" :step="0" v-model="pageData.rowCount" @change="handleRowCountChange" style="width: 60px;"></el-input-number>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="3">
|
|
<el-form-item label="分切卷数">
|
|
<el-input-number :controls="false" :step="0" v-model="pageData.rollCount" @change="handleRollCountChange" style="width: 60px;"></el-input-number>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<!-- 排数据表格 -->
|
|
<div class="rq">
|
|
<el-table
|
|
:data="rowDataList"
|
|
border
|
|
style="width: 100%; margin-top: 10px;"
|
|
max-height="350"
|
|
:span-method="objectSpanMethod">
|
|
<el-table-column prop="rowNumber" label="NO." width="50" align="center">
|
|
</el-table-column>
|
|
<el-table-column label="良品数" width="80" align="center">
|
|
<template slot-scope="scope">
|
|
<el-input-number
|
|
:controls="false" :step="0"
|
|
v-model="scope.row.goodQty"
|
|
@change="handleQtyChange(scope.row)"
|
|
style="width: 100%"
|
|
class="good-qty-input"
|
|
:style="{'color': '#67c23a', 'font-weight': 'bold'}">
|
|
</el-input-number>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="面损" width="80" align="center">
|
|
<template slot-scope="scope">
|
|
<el-input-number
|
|
:controls="false" :step="0"
|
|
v-model="scope.row.surfaceLossQty"
|
|
@change="handleQtyChange(scope.row)"
|
|
style="width: 100%">
|
|
</el-input-number>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="性能不良" width="80" align="center">
|
|
<template slot-scope="scope">
|
|
<el-input-number
|
|
:controls="false" :step="0"
|
|
v-model="scope.row.poorPerformanceQty"
|
|
@change="handleQtyChange(scope.row)"
|
|
style="width: 100%">
|
|
</el-input-number>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="不良数" width="80" align="right">
|
|
<template slot-scope="scope">
|
|
<div class="defect-display" :style="{'color': '#f56c6c', 'font-weight': 'bold', 'text-align': 'right', 'padding-right': '10px'}">
|
|
{{ scope.row.defectQty }}
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="良率" width="80" align="center">
|
|
<template slot-scope="scope">
|
|
<div class="yield-display" :style="{'color': '#409eff', 'font-weight': 'bold'}">
|
|
{{ scope.row.yieldRate }}
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="总数" width="80" align="right">
|
|
<template slot-scope="scope">
|
|
<div class="total-display" :style="{'text-align': 'right', 'padding-right': '10px'}">
|
|
{{ scope.row.totalQty }}
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column v-if="scheduleData.packingListFlag === 'Y'" label="Packing List" align="center" class-name="packing-list-column">
|
|
<template slot-scope="scope">
|
|
<div class="remark-wrapper">
|
|
<el-input
|
|
type="textarea"
|
|
v-model="scope.row.packingList"
|
|
resize="none"
|
|
:autosize="false"
|
|
class="remark-textarea">
|
|
</el-input>
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="备注" align="center" class-name="remark-column">
|
|
<template slot-scope="scope">
|
|
<div class="remark-wrapper">
|
|
<el-input
|
|
type="textarea"
|
|
v-model="scope.row.remark"
|
|
resize="none"
|
|
:autosize="false"
|
|
class="remark-textarea">
|
|
</el-input>
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="操作" align="center" width="110" class-name="operation-column">
|
|
<template slot-scope="scope">
|
|
<el-button type="text" size="mini" @click="openRollDefectDialog(scope.$index)">不良原因</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</div>
|
|
</el-form>
|
|
<span slot="footer" class="dialog-footer">
|
|
<el-button type="primary" @click="checkCreateSeparateRolllBun" :loading="yieldReportLoading" :disabled="exceptionRuleLocked">产量报告</el-button>
|
|
<el-button type="success" @click="saveShiftChangeTransfer" :loading="shiftChangeLoading" :disabled="exceptionRuleLocked">换班结转</el-button>
|
|
<el-button type="warning" @click="executeAbnormalRollCut" :loading="abnormalCutLoading" :disabled="exceptionRuleLocked">异常截卷</el-button>
|
|
<el-button @click="closeDialog" :disabled="exceptionRuleLocked">{{ buttons.closeButton }}</el-button>
|
|
</span>
|
|
<div
|
|
v-if="exceptionRuleLocked"
|
|
:class="['exception-rule-mask-screen', isWarningLevelStyle() ? 'warning' : 'serious']">
|
|
<div class="exception-rule-bg-grid"></div>
|
|
<div class="exception-rule-vignette"></div>
|
|
<div class="exception-rule-scanline">
|
|
<div class="lines"></div>
|
|
<div class="beam"></div>
|
|
</div>
|
|
<div class="exception-rule-corners">
|
|
<div class="corner tl">
|
|
<svg viewBox="0 0 80 80">
|
|
<path d="M4 40 L4 4 L40 4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" opacity="0.7" />
|
|
<line x1="4" y1="16" x2="10" y2="16" stroke="currentColor" stroke-width="1" opacity="0.4" />
|
|
<line x1="4" y1="28" x2="8" y2="28" stroke="currentColor" stroke-width="1" opacity="0.4" />
|
|
<line x1="16" y1="4" x2="16" y2="10" stroke="currentColor" stroke-width="1" opacity="0.4" />
|
|
<line x1="28" y1="4" x2="28" y2="8" stroke="currentColor" stroke-width="1" opacity="0.4" />
|
|
<g class="arc">
|
|
<circle cx="4" cy="4" r="14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-dasharray="14 74" opacity="0.6" />
|
|
</g>
|
|
<rect x="1" y="1" width="6" height="6" fill="currentColor" opacity="0.9" />
|
|
</svg>
|
|
</div>
|
|
<div class="corner tr">
|
|
<svg viewBox="0 0 80 80">
|
|
<path d="M4 40 L4 4 L40 4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" opacity="0.7" />
|
|
<line x1="4" y1="16" x2="10" y2="16" stroke="currentColor" stroke-width="1" opacity="0.4" />
|
|
<line x1="4" y1="28" x2="8" y2="28" stroke="currentColor" stroke-width="1" opacity="0.4" />
|
|
<line x1="16" y1="4" x2="16" y2="10" stroke="currentColor" stroke-width="1" opacity="0.4" />
|
|
<line x1="28" y1="4" x2="28" y2="8" stroke="currentColor" stroke-width="1" opacity="0.4" />
|
|
<g class="arc">
|
|
<circle cx="4" cy="4" r="14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-dasharray="14 74" opacity="0.6" />
|
|
</g>
|
|
<rect x="1" y="1" width="6" height="6" fill="currentColor" opacity="0.9" />
|
|
</svg>
|
|
</div>
|
|
<div class="corner bl">
|
|
<svg viewBox="0 0 80 80">
|
|
<path d="M4 40 L4 4 L40 4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" opacity="0.7" />
|
|
<line x1="4" y1="16" x2="10" y2="16" stroke="currentColor" stroke-width="1" opacity="0.4" />
|
|
<line x1="4" y1="28" x2="8" y2="28" stroke="currentColor" stroke-width="1" opacity="0.4" />
|
|
<line x1="16" y1="4" x2="16" y2="10" stroke="currentColor" stroke-width="1" opacity="0.4" />
|
|
<line x1="28" y1="4" x2="28" y2="8" stroke="currentColor" stroke-width="1" opacity="0.4" />
|
|
<g class="arc">
|
|
<circle cx="4" cy="4" r="14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-dasharray="14 74" opacity="0.6" />
|
|
</g>
|
|
<rect x="1" y="1" width="6" height="6" fill="currentColor" opacity="0.9" />
|
|
</svg>
|
|
</div>
|
|
<div class="corner br">
|
|
<svg viewBox="0 0 80 80">
|
|
<path d="M4 40 L4 4 L40 4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" opacity="0.7" />
|
|
<line x1="4" y1="16" x2="10" y2="16" stroke="currentColor" stroke-width="1" opacity="0.4" />
|
|
<line x1="4" y1="28" x2="8" y2="28" stroke="currentColor" stroke-width="1" opacity="0.4" />
|
|
<line x1="16" y1="4" x2="16" y2="10" stroke="currentColor" stroke-width="1" opacity="0.4" />
|
|
<line x1="28" y1="4" x2="28" y2="8" stroke="currentColor" stroke-width="1" opacity="0.4" />
|
|
<g class="arc">
|
|
<circle cx="4" cy="4" r="14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-dasharray="14 74" opacity="0.6" />
|
|
</g>
|
|
<rect x="1" y="1" width="6" height="6" fill="currentColor" opacity="0.9" />
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
<div class="exception-rule-statusbar">
|
|
<div class="left">
|
|
<span class="mes">Boing MES</span>
|
|
<span>Site - {{ scheduleData.site || '--' }} / Bu - {{ scheduleData.buNo || '--' }}</span>
|
|
</div>
|
|
<div class="right">
|
|
<span class="lockdown"><span class="dot"></span>{{ getExceptionRuleActiveText() }}</span>
|
|
<span>{{ exceptionRuleClockDate || getNowDateText() }}</span>
|
|
<span class="clock">{{ exceptionRuleClockTime || getNowTimeText() }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="exception-rule-main">
|
|
<div class="exception-rule-alert-icon">
|
|
<div class="pulse-ring d0"></div>
|
|
<div class="pulse-ring d1"></div>
|
|
<div class="pulse-ring d2"></div>
|
|
<div class="alert-glow"></div>
|
|
<div class="alert-core">
|
|
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/>
|
|
<line x1="12" y1="9" x2="12" y2="13"/>
|
|
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="exception-rule-title-block">
|
|
<div class="exception-rule-title-cn">{{ getExceptionRuleTitleCn() }}</div>
|
|
<div class="exception-rule-title-en">PROCESS QUALITY FAULT</div>
|
|
<div class="exception-rule-title-rule"></div>
|
|
<div class="exception-rule-title-lock">{{ getExceptionRuleTitleLock() }}</div>
|
|
</div>
|
|
|
|
<div class="exception-rule-info-cards">
|
|
<div class="exception-rule-info-card">
|
|
<span class="label">设备编号 / EQUIPMENT ID</span>
|
|
<span class="value">{{ getExceptionRuleEquipmentText() }}</span>
|
|
</div>
|
|
<div class="exception-rule-info-card">
|
|
<span class="label">锁定时间 / LOCKED AT</span>
|
|
<span class="value">{{ getExceptionRuleLockedTimeText() }}</span>
|
|
</div>
|
|
<div class="exception-rule-info-card work-order-card">
|
|
<span class="label">工单编号 / WORK ORDER</span>
|
|
<div class="work-order-value-line">
|
|
<span class="value hl">{{ getExceptionRuleWorkOrderText() }}</span>
|
|
<span class="work-order-seq">{{ getExceptionRuleSeqNoText() }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="exception-rule-flow-steps">
|
|
<div class="flow-step done"><div class="dot"></div><div class="label">异常检测</div></div>
|
|
<div class="flow-line done"></div>
|
|
<div class="flow-step done"><div class="dot"></div><div class="label">工位锁定</div></div>
|
|
<div class="flow-line"></div>
|
|
<div class="flow-step active"><div class="dot"></div><div class="label">质量审核</div></div>
|
|
<div class="flow-line"></div>
|
|
<div class="flow-step"><div class="dot"></div><div class="label">解锁生产</div></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="exception-rule-bottombar">
|
|
<div class="inner">
|
|
<div class="left">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M22 12h-4l-3 9L9 3l-3 9H2"/>
|
|
</svg>
|
|
<span class="ttl">生产质量指标 / QUALITY METRICS</span>
|
|
</div>
|
|
<div class="exception-rule-quality-cards">
|
|
<div class="quality-card">
|
|
<span class="qlabel">不良品率 / DEFECT RATE</span>
|
|
<span class="qvalue hl">{{ formatExceptionRuleDefectRateText() }}</span>
|
|
</div>
|
|
<div class="quality-card">
|
|
<span class="qlabel">影响数量 / AFFECTED</span>
|
|
<span class="qvalue hl">{{ formatExceptionRuleAffectedQtyText() }}</span>
|
|
</div>
|
|
<div class="quality-card">
|
|
<span class="qlabel">已生产数量 / PRODUCED</span>
|
|
<span class="qvalue">{{ formatExceptionRuleProducedQtyText() }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="right">
|
|
<div>实时监控中</div>
|
|
<div class="auth">
|
|
REAL-TIME MONITORING
|
|
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<polyline points="9 18 15 12 9 6"/>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</el-dialog>
|
|
|
|
<!-- 分卷不良原因对话框 -->
|
|
<el-dialog
|
|
title="不良原因"
|
|
:close-on-click-modal="false"
|
|
:visible.sync="rollDefectDialogVisible"
|
|
width="700px"
|
|
append-to-body>
|
|
<div class="roll-defect-summary">
|
|
<span>当前卷:第{{ currentRollDefectIndex }}卷</span>
|
|
<span>面损合计:{{ formatQty(getCurrentRollSurfaceLossQty()) }}</span>
|
|
<span>已选不良合计:{{ formatQty(getCurrentRollDefectTotal()) }}</span>
|
|
</div>
|
|
<el-form :inline="true" label-position="top" class="roll-defect-form">
|
|
<el-form-item>
|
|
<span slot="label" class="defect-code-label" @click="getBaseList(89)"><a>不良代码</a></span>
|
|
<el-input
|
|
v-model="rollDefectForm.defectCode"
|
|
readonly
|
|
class="defect-code-input"
|
|
style="width: 130px"
|
|
placeholder="请选择"
|
|
@click.native="getBaseList(89)">
|
|
</el-input>
|
|
</el-form-item>
|
|
<el-form-item label="不良数量">
|
|
<el-input-number
|
|
v-model="rollDefectForm.defectQty"
|
|
:controls="false"
|
|
:min="0"
|
|
style="width: 120px">
|
|
</el-input-number>
|
|
</el-form-item>
|
|
<el-form-item label="不良描述">
|
|
<el-input
|
|
v-model="rollDefectForm.defectDesc"
|
|
readonly
|
|
style="width: 250px">
|
|
</el-input>
|
|
</el-form-item>
|
|
<el-form-item label=" ">
|
|
<el-button type="primary" @click="addRollDefectItem">添加</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
<el-table
|
|
:data="currentRollDefectList"
|
|
border
|
|
height="260"
|
|
style="width: 100%;">
|
|
<el-table-column prop="defectCode" label="不良代码" width="120" align="center"></el-table-column>
|
|
<el-table-column prop="defectDesc" label="不良描述" min-width="260" align="left"></el-table-column>
|
|
<el-table-column prop="defectQty" label="不良数量" width="110" align="right"></el-table-column>
|
|
<el-table-column label="操作" width="80" align="center">
|
|
<template slot-scope="scope">
|
|
<el-button type="text" @click="removeRollDefectItem(scope.$index)">删除</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
<span slot="footer" class="dialog-footer">
|
|
<el-button type="primary" @click="rollDefectDialogVisible = false">确定</el-button>
|
|
</span>
|
|
</el-dialog>
|
|
|
|
<!-- 固定载具选择对话框 -->
|
|
<el-dialog title="固定载具清单" :close-on-click-modal="false" v-drag :visible.sync="carrierModelFlag" width="950px">
|
|
<div class="rq">
|
|
<el-form :inline="true" label-position="top" :model="carrierSearchData">
|
|
<el-form-item label="标签条码">
|
|
<el-input v-model="carrierSearchData.carrierNo" clearable style="width: 150px"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="载具类型名称">
|
|
<el-input v-model="carrierSearchData.carrierTypeName" clearable style="width: 150px"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="规格描述">
|
|
<el-input v-model="carrierSearchData.specification" clearable style="width: 150px"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label=" ">
|
|
<el-button type="primary" @click="queryFixedCarrierList">查询</el-button>
|
|
<el-button v-if="!editBatchVisible" @click="editBatchModel" type="success" icon="el-icon-edit">批量编辑</el-button>
|
|
<el-button v-if="editBatchVisible" @click="batchSaveCarrier" type="primary" icon="el-icon-check" :loading="saveLoading">批量保存</el-button>
|
|
<el-button type="success" icon="el-icon-plus" @click="addCarrierModal">新增</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
<el-table
|
|
:height="400"
|
|
:data="carrierList"
|
|
@row-dblclick="selectCarrier"
|
|
border
|
|
style="width: 100%;">
|
|
<el-table-column
|
|
prop="carrierNo"
|
|
label="标签条码"
|
|
header-align="center"
|
|
align="left"
|
|
min-width="120">
|
|
</el-table-column>
|
|
<el-table-column
|
|
prop="carrierTypeCode"
|
|
label="载具类型编码"
|
|
header-align="center"
|
|
align="left"
|
|
min-width="120">
|
|
</el-table-column>
|
|
<el-table-column
|
|
prop="carrierTypeName"
|
|
label="载具类型名称"
|
|
header-align="center"
|
|
align="left"
|
|
min-width="150">
|
|
</el-table-column>
|
|
<el-table-column
|
|
prop="specification"
|
|
label="规格描述"
|
|
header-align="center"
|
|
align="left"
|
|
min-width="150">
|
|
</el-table-column>
|
|
<el-table-column
|
|
prop="dimensions"
|
|
label="尺寸"
|
|
header-align="center"
|
|
align="left"
|
|
min-width="100">
|
|
</el-table-column>
|
|
<el-table-column
|
|
label="可用数量"
|
|
header-align="center"
|
|
align="right"
|
|
min-width="120">
|
|
<template slot-scope="scope">
|
|
<el-input-number
|
|
v-if="editBatchVisible"
|
|
v-model="scope.row.availableQty"
|
|
:controls="false"
|
|
:min="0"
|
|
:precision="0"
|
|
style="width: 100%">
|
|
</el-input-number>
|
|
<span v-else>{{ scope.row.availableQty || 0 }}</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column
|
|
label="操作"
|
|
header-align="center"
|
|
align="center"
|
|
width="80"
|
|
fixed="right">
|
|
<template slot-scope="scope">
|
|
<el-button
|
|
type="text"
|
|
icon="el-icon-printer"
|
|
@click="printCarrier(scope.row)"
|
|
style="color: #409eff;">
|
|
打印
|
|
</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</div>
|
|
<el-footer style="height:40px;margin-top: 20px;text-align:center">
|
|
<el-button type="primary" @click="carrierModelFlag=false">关闭</el-button>
|
|
</el-footer>
|
|
</el-dialog>
|
|
|
|
<!-- 新增固定载具对话框 -->
|
|
<el-dialog title="新增固定载具" :close-on-click-modal="false" v-drag :visible.sync="addCarrierFlag" width="487px">
|
|
<el-form ref="addCarrierForm" :inline="true" label-position="top" :model="addCarrierData" :rules="addCarrierRules" style="margin-left: 7px;margin-top: -5px;">
|
|
<el-form-item label="状态" prop="status">
|
|
<el-select v-model="addCarrierData.status" placeholder="请选择" style="width: 120px">
|
|
<el-option label="空闲" :value="1"></el-option>
|
|
<el-option label="占用" :value="2"></el-option>
|
|
<el-option label="维修" :value="3"></el-option>
|
|
<el-option label="报废" :value="4"></el-option>
|
|
<el-option label="外借" :value="5"></el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-form>
|
|
<el-form :inline="true" label-position="top" :model="addCarrierData" :rules="addCarrierRules" style="margin-left: 7px">
|
|
<el-form-item label="载具类型编码" prop="carrierTypeCode">
|
|
<el-input v-model="addCarrierData.carrierTypeCode" style="width: 150px"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="载具类型名称" prop="carrierTypeName">
|
|
<el-input v-model="addCarrierData.carrierTypeName" style="width: 285px"></el-input>
|
|
</el-form-item>
|
|
</el-form>
|
|
<el-form :inline="true" label-position="top" :model="addCarrierData" style="margin-left: 7px">
|
|
<el-form-item label="固定资产编号">
|
|
<el-input v-model="addCarrierData.assetNo" style="width: 150px"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="规格描述">
|
|
<el-input v-model="addCarrierData.specification" style="width: 285px"></el-input>
|
|
</el-form-item>
|
|
</el-form>
|
|
<el-form :inline="true" label-position="top" :model="addCarrierData" style="margin-left: 7px">
|
|
<el-form-item label="采购日期">
|
|
<el-date-picker v-model="addCarrierData.purchaseDate" type="date" value-format="yyyy-MM-dd" style="width: 150px"></el-date-picker>
|
|
</el-form-item>
|
|
<el-form-item label="当前位置">
|
|
<el-input v-model="addCarrierData.currentLocation" style="width: 150px"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="尺寸">
|
|
<el-input v-model="addCarrierData.dimensions" style="width: 120px"></el-input>
|
|
</el-form-item>
|
|
</el-form>
|
|
<el-form :inline="true" label-position="top" :model="addCarrierData" style="margin-left: 7px">
|
|
<el-form-item label="可用数量">
|
|
<el-input-number v-model="addCarrierData.availableQty" :controls="false" style="width: 102px"></el-input-number>
|
|
</el-form-item>
|
|
<el-form-item label="最大承重">
|
|
<el-input-number v-model="addCarrierData.maxWeight" :controls="false" style="width: 102px"></el-input-number>
|
|
</el-form-item>
|
|
<el-form-item label="预期使用次数">
|
|
<el-input-number v-model="addCarrierData.expectedLifeCycles" :controls="false" style="width: 102px"></el-input-number>
|
|
</el-form-item>
|
|
<el-form-item label="预期使用寿命">
|
|
<el-input-number v-model="addCarrierData.expectedLifeDays" :controls="false" style="width: 101px"></el-input-number>
|
|
</el-form-item>
|
|
</el-form>
|
|
<el-form :inline="true" label-position="top" :model="addCarrierData" style="margin-left: 7px">
|
|
<el-form-item label="备注">
|
|
<el-input type="textarea" v-model="addCarrierData.remark" :rows="3" resize='none' show-word-limit style="width: 450px;height: 30px"></el-input>
|
|
</el-form-item>
|
|
</el-form>
|
|
<el-footer style="height:35px;margin-top: 55px;text-align:center">
|
|
<el-button type="primary" :loading="addCarrierLoading" @click="saveAddCarrier">保存</el-button>
|
|
<el-button @click="addCarrierFlag=false">关闭</el-button>
|
|
</el-footer>
|
|
</el-dialog>
|
|
<Chooselist ref="baseList" @getBaseData="getBaseData"></Chooselist>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import {
|
|
checkSplitRollExceptionRule,/*创建分卷前异常规则校验*/
|
|
getSplitRuleLockStatus,/*查询创建分卷规则锁定状态*/
|
|
completeSplitRuleTodoOperation,/*创建分卷完成后回写代办操作标识*/
|
|
createSplitSfdcRoll,/*执行创建分卷的操作*/
|
|
getFixedCarrierList,/*获取固定载具列表*/
|
|
getUnprocessedShiftChangeData,/*查询未处理的换班结转数据*/
|
|
saveShiftChangeData,/*保存换班结转数据*/
|
|
markShiftChangeAsProcessed,/*更新换班结转数据为已处理*/
|
|
saveAbnormalRollData,/*保存异常截卷数据*/
|
|
} from '@/api/yieldReport/com_separate_roll.js';
|
|
import { checkDefectCode } from '@/api/yieldReport/com_defect_roll.js';
|
|
import { fixedCarrierSave } from '@/api/fixedCarrier/fixedCarrier.js';
|
|
import Chooselist from '@/views/modules/common/Chooselist';
|
|
|
|
/*打印标签专用的js*/
|
|
import {
|
|
printSfdcLabel,
|
|
} from "@/views/modules/yieldReport/print_roll_label.js";
|
|
|
|
import {
|
|
searchSysLanguagePackList,
|
|
searchSysLanguageParam,
|
|
searchFunctionButtonList,
|
|
saveButtonList,
|
|
searchSysLanguage,
|
|
searchLanguageListByLanguageCode,
|
|
saveSysLanguageOne,
|
|
searchPageLanguageData,
|
|
removerLanguage,
|
|
saveSysLanguageList
|
|
} from "@/api/sysLanguage.js";
|
|
import {
|
|
getPartLabelTemplateList,
|
|
callUspPartLabelTemplate
|
|
} from '@/api/wms/wms';
|
|
|
|
import getLodop from '@/utils/LodopFuncs.js';
|
|
import labelPrintTemplates from '@/mixins/labelPrintTemplates.js';
|
|
|
|
var functionId = 'C10000004';
|
|
|
|
export default {
|
|
name: "com_separate_roll",
|
|
mixins: [labelPrintTemplates],
|
|
components: {
|
|
Chooselist
|
|
},
|
|
data() {
|
|
return {
|
|
titleCon: '创建分卷',
|
|
showDefault: false,
|
|
// 按钮loading状态
|
|
yieldReportLoading: false, // 产量报告loading
|
|
shiftChangeLoading: false, // 换班结转loading
|
|
abnormalCutLoading: false, // 异常截卷loading
|
|
exceptionRuleLocked: false, // 异常规则触发后的强制锁定
|
|
exceptionRuleLockMessage: '', // 锁定提示文案
|
|
exceptionRuleWarningLevel: '', // 预警等级(轻微/一般/严重)
|
|
exceptionRuleLockedAt: '', // 锁定时间
|
|
exceptionRuleMatchedRollIndex: null, // 触发规则卷序号
|
|
exceptionRuleMatchedDefectRate: null, // 触发规则卷不良率
|
|
exceptionRuleMatchedTotalQty: null, // 触发规则卷总数
|
|
exceptionRuleClockDate: '', // 锁定页面日期
|
|
exceptionRuleClockTime: '', // 锁定页面时间
|
|
exceptionRuleClockTimer: null, // 锁定页面时钟定时器
|
|
pendingRuleFollowupAction: '', // 提示类规则在成功后需打开的功能动作
|
|
pendingRuleFollowupResultMap: null,
|
|
scheduleData: {
|
|
site: this.$store.state.user.site,
|
|
username: this.$store.state.user.name,
|
|
seqNo: '',
|
|
orderNo: '',
|
|
itemNo: 0,
|
|
resourceId: '',
|
|
scheduledDate: '',
|
|
shiftNo: '',
|
|
partNo: '',
|
|
buNo: '',
|
|
workCenterNo: '',
|
|
workCenterDesc: '',
|
|
resourceDesc: '',
|
|
rollNo: '',
|
|
partDesc: '',
|
|
totalRollQty: 0,
|
|
planStartTime: '',
|
|
planFinishTime: '',
|
|
qtyRequiredOriginal: 0,
|
|
preItemDesc: '',
|
|
nextItemDesc: '',
|
|
nextItemNo: 0,
|
|
operatorId: '',
|
|
functionName: '',
|
|
currentRollFlag: false
|
|
},
|
|
pageData: {
|
|
site: this.$store.state.user.site,
|
|
username: this.$store.state.user.name,
|
|
orderNo: '',
|
|
itemNo: '',
|
|
seqNo: '',
|
|
rollNo: '',
|
|
reportDate: '',
|
|
reportTime: '',
|
|
operatorId: '',
|
|
rollQty: 0,
|
|
rollNums: 1,
|
|
// 新增字段
|
|
fixture: '', // 固定载具
|
|
rowCount: 0, // 排数
|
|
rollCount: 0, // 卷数
|
|
carrierNo: ''
|
|
},
|
|
rowDataList: [], // 排数据列表
|
|
// 分卷不良原因(前端暂存)
|
|
rollDefectDialogVisible: false,
|
|
currentRollDefectIndex: 1,
|
|
tagNo: '',
|
|
rollDefectForm: {
|
|
defectCode: '',
|
|
defectDesc: '',
|
|
defectQty: null
|
|
},
|
|
rollDefectMap: {},
|
|
currentRollDefectList: [],
|
|
operatorData: {
|
|
site: this.$store.state.user.site,
|
|
username: this.$store.state.user.name,
|
|
operatorId: '',
|
|
operatorName: '',
|
|
status: '',
|
|
seqNo: '',
|
|
showFlag: false
|
|
},
|
|
carrierModelFlag: false, // 固定载具对话框标记
|
|
carrierList: [], // 固定载具列表
|
|
carrierSearchData: { // 固定载具查询条件
|
|
carrierNo: '',
|
|
carrierTypeName: '',
|
|
specification: ''
|
|
},
|
|
editBatchVisible: false, // 批量编辑状态
|
|
saveLoading: false, // 保存loading状态
|
|
// 新增固定载具相关
|
|
addCarrierFlag: false,
|
|
addCarrierLoading: false,
|
|
addCarrierData: {
|
|
bu: '',
|
|
site: '',
|
|
buNo: '',
|
|
carrierTypeCode: '',
|
|
carrierTypeName: '',
|
|
assetNo: '',
|
|
specification: '',
|
|
purchaseDate: null,
|
|
currentLocation: '',
|
|
dimensions: '',
|
|
availableQty: null,
|
|
maxWeight: null,
|
|
expectedLifeCycles: null,
|
|
expectedLifeDays: null,
|
|
status: 1,
|
|
remark: ''
|
|
},
|
|
addCarrierRules: {
|
|
carrierTypeCode: [{ required: true, message: '请输入载具类型编码', trigger: 'blur' }],
|
|
carrierTypeName: [{ required: true, message: '请输入载具类型名称', trigger: 'blur' }]
|
|
},
|
|
hasCachedData: false, // 是否有缓存数据
|
|
cachedDataCount: 0, // 缓存数据条数
|
|
cachedRowDataList: [], // 缓存的原始行数据(用于校验)
|
|
isLoadingCachedData: false, // 是否正在加载缓存数据(防止触发handleRowCountChange)
|
|
lockStatusTimer: null, // 强制锁定状态轮询定时器
|
|
buttons: {
|
|
confirmButton: '确定',
|
|
closeButton: '关闭',
|
|
},
|
|
buttonList: [
|
|
{
|
|
functionId: functionId,
|
|
languageValue: '确定',
|
|
objectId: 'confirmButton',
|
|
objectType: 'button',
|
|
tableId: '*'
|
|
}, {
|
|
functionId: functionId,
|
|
languageValue: '关闭',
|
|
objectId: 'closeButton',
|
|
objectType: 'button',
|
|
tableId: '*'
|
|
},
|
|
],
|
|
queryButton: {
|
|
functionId: functionId,
|
|
table_id: '*',
|
|
languageCode: this.$i18n.locale,
|
|
objectType: 'button'
|
|
},
|
|
labels: {
|
|
componentTitle: '创建分卷',
|
|
currentTime: '当前时间:',
|
|
rollQty: '良品数量:',
|
|
rollNums: '卷数:',
|
|
approvedQtyMustBeInteger: '良品数量必须是正整数!',
|
|
rollQtyMustBeInteger: '卷数必须是正整数!',
|
|
pleaseSwitchOperator: '请先切换人员!',
|
|
approvedQtyMustMoreTanZeroAndBeInteger: '良品数量必须大于零且是整数!',
|
|
rollQtyMustMoreTanZeroAndBeInteger: '卷数必须是正整数!',
|
|
confirmLabel: '确认',
|
|
cancelLabel: '取消',
|
|
},
|
|
labelsList: [
|
|
{
|
|
functionId: functionId,
|
|
languageValue: '创建分卷',
|
|
objectId: 'componentTitle',
|
|
objectType: 'label',
|
|
tableId: '*'
|
|
}, {
|
|
functionId: functionId,
|
|
languageValue: '当前时间:',
|
|
objectId: 'currentTime',
|
|
objectType: 'label',
|
|
tableId: '*'
|
|
}, {
|
|
functionId: functionId,
|
|
languageValue: '良品数量:',
|
|
objectId: 'rollQty',
|
|
objectType: 'label',
|
|
tableId: '*'
|
|
}, {
|
|
functionId: functionId,
|
|
languageValue: '卷数:',
|
|
objectId: 'rollNums',
|
|
objectType: 'label',
|
|
tableId: '*'
|
|
}, {
|
|
functionId: functionId,
|
|
languageValue: '良品数量必须是正整数!',
|
|
objectId: 'approvedQtyMustBeInteger',
|
|
objectType: 'label',
|
|
tableId: '*'
|
|
}, {
|
|
functionId: functionId,
|
|
languageValue: '卷数必须是正整数!',
|
|
objectId: 'rollQtyMustBeInteger',
|
|
objectType: 'label',
|
|
tableId: '*'
|
|
}, {
|
|
functionId: functionId,
|
|
languageValue: '良品数量必须大于零且是整数!',
|
|
objectId: 'approvedQtyMustMoreTanZeroAndBeInteger',
|
|
objectType: 'label',
|
|
tableId: '*'
|
|
}, {
|
|
functionId: functionId,
|
|
languageValue: '卷数必须大于零且是整数!',
|
|
objectId: 'rollQtyMustMoreTanZeroAndBeInteger',
|
|
objectType: 'label',
|
|
tableId: '*'
|
|
}, {
|
|
functionId: functionId,
|
|
languageValue: '请先切换人员!',
|
|
objectId: 'pleaseSwitchOperator',
|
|
objectType: 'label',
|
|
tableId: '*'
|
|
}, {
|
|
functionId: functionId,
|
|
languageValue: '确认',
|
|
objectId: 'confirmLabel',
|
|
objectType: 'label',
|
|
tableId: '*'
|
|
}, {
|
|
functionId: functionId,
|
|
languageValue: '取消',
|
|
objectId: 'cancelLabel',
|
|
objectType: 'label',
|
|
tableId: '*'
|
|
},
|
|
],
|
|
queryLabel: {
|
|
functionId: functionId,
|
|
table_id: '*',
|
|
languageCode: this.$i18n.locale,
|
|
objectType: 'label'
|
|
},
|
|
}
|
|
},
|
|
methods: {
|
|
//页面初始化是的方法
|
|
async init(scheduleData, operatorData) {
|
|
// 设置标记,防止初始化过程中触发handleRowCountChange
|
|
this.isLoadingCachedData = true;
|
|
|
|
//初始化参数
|
|
this.scheduleData = scheduleData;
|
|
//初始化操作员对象
|
|
this.operatorData = JSON.parse(JSON.stringify(operatorData));
|
|
//重置时间
|
|
this.pageData.reportDate = this.dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss');
|
|
this.pageData.reportTime = this.dayjs(new Date()).format('HH:mm:ss');
|
|
//设置参数
|
|
this.pageData.orderNo = scheduleData.orderNo;
|
|
this.pageData.itemNo = scheduleData.itemNo;
|
|
this.pageData.seqNo = scheduleData.seqNo;
|
|
this.pageData.rollNo = scheduleData.rollNo;
|
|
this.pageData.operatorId = operatorData.operatorId;
|
|
//清空参数
|
|
this.pageData.rollQty = 0;
|
|
this.pageData.rollNums = 1;
|
|
// 初始化新增字段(先设置默认值)
|
|
this.pageData.rowCount = scheduleData.rowCount;
|
|
this.pageData.rollCount = scheduleData.rollCount;
|
|
this.pageData.fixture = scheduleData.carrierNo;
|
|
// 重置异常规则锁定状态
|
|
this.exceptionRuleLocked = false;
|
|
this.exceptionRuleLockMessage = '';
|
|
this.exceptionRuleWarningLevel = '';
|
|
this.exceptionRuleLockedAt = '';
|
|
this.exceptionRuleMatchedRollIndex = null;
|
|
this.exceptionRuleMatchedDefectRate = null;
|
|
this.exceptionRuleMatchedTotalQty = null;
|
|
this.resetPendingRuleFollowup();
|
|
// 重置缓存数据标记
|
|
this.hasCachedData = false;
|
|
this.cachedDataCount = 0;
|
|
this.cachedRowDataList = []; // 重置缓存的原始行数据
|
|
// 重置分卷不良原因暂存
|
|
this.resetRollDefectData();
|
|
// 清空行数据列表
|
|
this.rowDataList = [];
|
|
//判断是否启用多语言
|
|
// this.getMultiLanguageList(); //刷新多语言的信息
|
|
//获取焦点
|
|
this.$nextTick(() => {
|
|
this.$refs.rollQty && this.$refs.rollQty.focus();
|
|
});
|
|
this.titleCon = this.labels.componentTitle;//重置标题
|
|
|
|
// 查询是否有缓存的换班结转数据(等待完成)
|
|
await this.loadCachedShiftChangeData();
|
|
|
|
// 如果没有缓存数据,初始化空白行
|
|
if (!this.hasCachedData) {
|
|
this.initRowDataList();
|
|
}
|
|
|
|
// 初始化后立即同步一次强制锁定状态
|
|
try {
|
|
await this.refreshSplitRuleLockStatus(false);
|
|
} catch (error) {
|
|
console.error('初始化锁定状态失败:', error);
|
|
}
|
|
if (this.exceptionRuleLocked) {
|
|
this.startSplitRuleLockPolling();
|
|
} else {
|
|
this.stopSplitRuleLockPolling();
|
|
}
|
|
|
|
// 取消标记
|
|
this.isLoadingCachedData = false;
|
|
},
|
|
|
|
// 查询缓存的换班结转数据
|
|
async loadCachedShiftChangeData() {
|
|
try {
|
|
const params = {
|
|
site: this.scheduleData.site,
|
|
orderNo: this.scheduleData.orderNo,
|
|
seqNo: parseInt(this.scheduleData.seqNo) || 0
|
|
};
|
|
|
|
console.log('查询缓存数据参数:', params);
|
|
|
|
const {data} = await getUnprocessedShiftChangeData(params);
|
|
|
|
console.log('查询缓存数据结果:', data);
|
|
|
|
if (data && data.code === 0 && data.data && data.data.length > 0) {
|
|
// 有缓存数据,恢复到界面
|
|
this.hasCachedData = true;
|
|
this.cachedDataCount = data.count;
|
|
|
|
const cachedList = data.data;
|
|
|
|
console.log('缓存数据列表:', cachedList);
|
|
|
|
// 恢复排数和卷数
|
|
if (cachedList.length > 0) {
|
|
this.pageData.rowCount = cachedList[0].rowCount || 0;
|
|
this.pageData.rollCount = cachedList[0].rollCount || 0;
|
|
this.pageData.fixture = cachedList[0].fixtureNo || '';
|
|
}
|
|
|
|
// 恢复行数据列表
|
|
this.rowDataList = cachedList.map(item => ({
|
|
rowNumber: item.rowBumber,
|
|
goodQty: Number(item.goodQty) || 0,
|
|
surfaceLossQty: Number(item.surfaceLossQty) || 0,
|
|
poorPerformanceQty: Number(item.poorPerformanceQty) || 0,
|
|
defectQty: Number(item.defectQty) || 0,
|
|
yieldRate: this.calculateYieldRate(Number(item.goodQty) || 0, Number(item.defectQty) || 0),
|
|
totalQty: (Number(item.goodQty) || 0) + (Number(item.defectQty) || 0),
|
|
packingList: item.packingList || '',
|
|
remark: item.remark || ''
|
|
}));
|
|
|
|
// 保存缓存的原始数据(用于产量报告时校验)
|
|
this.cachedRowDataList = JSON.parse(JSON.stringify(this.rowDataList));
|
|
|
|
console.log('恢复后的行数据:', this.rowDataList);
|
|
|
|
this.$message.info(`已加载上次换班结转的缓存数据(${this.cachedDataCount}条)`);
|
|
}
|
|
// 如果没有缓存数据,不做任何处理,由init方法负责初始化
|
|
} catch (error) {
|
|
console.error('加载缓存数据失败:', error);
|
|
// 加载失败时不做处理,由init方法负责初始化
|
|
}
|
|
},
|
|
|
|
// 计算良率
|
|
calculateYieldRate(goodQty, defectQty) {
|
|
const totalQty = goodQty + defectQty;
|
|
if (totalQty > 0) {
|
|
return ((goodQty / totalQty) * 100).toFixed(2) + '%';
|
|
}
|
|
return '0.00%';
|
|
},
|
|
|
|
// ===================== 新增分卷优化方法 =====================
|
|
|
|
// 初始化排数据列表
|
|
initRowDataList() {
|
|
console.log('initRowDataList被调用, isLoadingCachedData=', this.isLoadingCachedData, ', 调用栈:', new Error().stack);
|
|
this.rowDataList = []
|
|
for (let i = 0; i < this.pageData.rowCount; i++) {
|
|
this.rowDataList.push({
|
|
rowNumber: i,
|
|
goodQty: 0,
|
|
surfaceLossQty: 0,
|
|
poorPerformanceQty: 0,
|
|
defectQty: 0,
|
|
yieldRate: '0.00%',
|
|
totalQty: 0,
|
|
packingList: '',
|
|
remark: ''
|
|
})
|
|
}
|
|
},
|
|
|
|
// 排数变化时重新初始化
|
|
handleRowCountChange() {
|
|
console.log('handleRowCountChange被调用, isLoadingCachedData=', this.isLoadingCachedData);
|
|
// 如果是从缓存加载数据,不要重新初始化
|
|
if (this.isLoadingCachedData) {
|
|
console.log('跳过初始化,因为正在加载缓存数据');
|
|
return;
|
|
}
|
|
if (this.pageData.rollCount > this.pageData.rowCount) {
|
|
this.pageData.rollCount = this.pageData.rowCount
|
|
}
|
|
this.initRowDataList()
|
|
this.resetRollDefectData()
|
|
},
|
|
|
|
// 卷数变化时校验并清理不良原因缓存
|
|
handleRollCountChange() {
|
|
if (this.pageData.rollCount > this.pageData.rowCount) {
|
|
this.pageData.rollCount = this.pageData.rowCount
|
|
}
|
|
const hasDefectReason = Object.keys(this.rollDefectMap).some(key => {
|
|
const list = this.rollDefectMap[key]
|
|
return Array.isArray(list) && list.length > 0
|
|
})
|
|
if (hasDefectReason) {
|
|
this.$message.warning('卷数已变更,原不良原因已清空,请重新维护')
|
|
}
|
|
this.resetRollDefectData()
|
|
},
|
|
|
|
// 数量变化时自动计算不良数、良率和总数
|
|
handleQtyChange(row) {
|
|
// 计算不良数 = 面损 + 性能不良
|
|
row.defectQty = (row.surfaceLossQty || 0) + (row.poorPerformanceQty || 0)
|
|
|
|
// 计算总数 = 良品数 + 不良数
|
|
row.totalQty = (row.goodQty || 0) + row.defectQty
|
|
|
|
// 计算良率 = 良品数 / 总数 * 100%
|
|
if (row.totalQty > 0) {
|
|
const yieldValue = ((row.goodQty || 0) / row.totalQty * 100).toFixed(2)
|
|
row.yieldRate = yieldValue + '%'
|
|
} else {
|
|
row.yieldRate = '0.00%'
|
|
}
|
|
},
|
|
|
|
resetRollDefectData() {
|
|
this.rollDefectDialogVisible = false
|
|
this.currentRollDefectIndex = 1
|
|
this.rollDefectMap = {}
|
|
this.currentRollDefectList = []
|
|
this.resetRollDefectForm()
|
|
},
|
|
|
|
resetRollDefectForm() {
|
|
this.rollDefectForm = {
|
|
defectCode: '',
|
|
defectDesc: '',
|
|
defectQty: null
|
|
}
|
|
},
|
|
|
|
normalizeRollDefectMap() {
|
|
const maxRollCount = Number(this.pageData.rollCount || 0)
|
|
Object.keys(this.rollDefectMap).forEach(key => {
|
|
if (Number(key) > maxRollCount) {
|
|
this.$delete(this.rollDefectMap, key)
|
|
}
|
|
})
|
|
},
|
|
|
|
getRollSegments() {
|
|
const totalRows = Array.isArray(this.rowDataList) ? this.rowDataList.length : 0
|
|
const rollCount = Number(this.pageData.rollCount || 0)
|
|
if (totalRows <= 0 || rollCount <= 0) {
|
|
return []
|
|
}
|
|
const rowsPerRoll = Math.floor(totalRows / rollCount)
|
|
const remainingRows = totalRows % rollCount
|
|
const segments = []
|
|
let startRow = 0
|
|
for (let i = 0; i < rollCount; i++) {
|
|
const currentRollRows = rowsPerRoll + (i < remainingRows ? 1 : 0)
|
|
if (currentRollRows <= 0) {
|
|
continue
|
|
}
|
|
segments.push({
|
|
rollIndex: i + 1,
|
|
startRow: startRow,
|
|
endRow: startRow + currentRollRows - 1,
|
|
rowCount: currentRollRows
|
|
})
|
|
startRow += currentRollRows
|
|
}
|
|
return segments
|
|
},
|
|
|
|
getRollSegmentByRowIndex(rowIndex) {
|
|
const segments = this.getRollSegments()
|
|
return segments.find(item => rowIndex >= item.startRow && rowIndex <= item.endRow) || null
|
|
},
|
|
|
|
getRollSegmentByRollIndex(rollIndex) {
|
|
const segments = this.getRollSegments()
|
|
return segments.find(item => item.rollIndex === rollIndex) || null
|
|
},
|
|
|
|
ensureRollDefectList(rollIndex) {
|
|
const key = String(rollIndex)
|
|
if (!Array.isArray(this.rollDefectMap[key])) {
|
|
this.$set(this.rollDefectMap, key, [])
|
|
}
|
|
},
|
|
|
|
openRollDefectDialog(rowIndex) {
|
|
const segment = this.getRollSegmentByRowIndex(rowIndex)
|
|
if (!segment) {
|
|
this.$message.warning('当前卷信息无效,请先确认排数和卷数')
|
|
return
|
|
}
|
|
this.currentRollDefectIndex = segment.rollIndex
|
|
this.ensureRollDefectList(segment.rollIndex)
|
|
this.currentRollDefectList = this.rollDefectMap[String(segment.rollIndex)]
|
|
this.resetRollDefectForm()
|
|
this.rollDefectDialogVisible = true
|
|
},
|
|
|
|
getRollSurfaceLossQty(rollIndex) {
|
|
const segment = this.getRollSegmentByRollIndex(rollIndex)
|
|
if (!segment) {
|
|
return 0
|
|
}
|
|
let total = 0
|
|
for (let i = segment.startRow; i <= segment.endRow; i++) {
|
|
const row = this.rowDataList[i]
|
|
total += Number(row && row.surfaceLossQty ? row.surfaceLossQty : 0)
|
|
}
|
|
return total
|
|
},
|
|
|
|
getCurrentRollSurfaceLossQty() {
|
|
return this.getRollSurfaceLossQty(this.currentRollDefectIndex)
|
|
},
|
|
|
|
getRollDefectTotal(rollIndex) {
|
|
const list = this.rollDefectMap[String(rollIndex)] || []
|
|
return list.reduce((sum, item) => sum + Number(item && item.defectQty ? item.defectQty : 0), 0)
|
|
},
|
|
|
|
getCurrentRollDefectTotal() {
|
|
return this.getRollDefectTotal(this.currentRollDefectIndex)
|
|
},
|
|
|
|
formatQty(value) {
|
|
const num = Number(value || 0)
|
|
return Number(num.toFixed(6)).toString()
|
|
},
|
|
|
|
getCurrentWorkCenterNo() {
|
|
return (this.scheduleData.workCenterNo || '').trim()
|
|
},
|
|
|
|
buildDefectCheckParams(defectCode, workCenterNo) {
|
|
return {
|
|
site: this.pageData.site,
|
|
orderNo: this.pageData.orderNo,
|
|
itemNo: this.pageData.itemNo,
|
|
seqNo: this.pageData.seqNo,
|
|
rollNo: this.pageData.rollNo,
|
|
defectCode: defectCode,
|
|
workCenterNo: workCenterNo
|
|
}
|
|
},
|
|
|
|
async addRollDefectItem() {
|
|
if (!this.currentRollDefectList) {
|
|
return
|
|
}
|
|
const workCenterNo = this.getCurrentWorkCenterNo()
|
|
if (!workCenterNo) {
|
|
this.$message.error('当前派工单未维护加工中心,不能选择不良原因')
|
|
return
|
|
}
|
|
this.rollDefectForm.defectCode = (this.rollDefectForm.defectCode || '').trim().toUpperCase()
|
|
if (!this.rollDefectForm.defectCode) {
|
|
this.$message.error('请先选择不良代码')
|
|
return
|
|
}
|
|
let checkResultMap = {}
|
|
try {
|
|
const { data } = await checkDefectCode(this.buildDefectCheckParams(this.rollDefectForm.defectCode, workCenterNo))
|
|
if (data.code == 500) {
|
|
this.$message.error(data.msg || '不良代码不属于当前加工中心')
|
|
return
|
|
}
|
|
checkResultMap = data.resultMap || {}
|
|
} catch (e) {
|
|
this.$message.error('不良代码校验失败,请稍后重试')
|
|
return
|
|
}
|
|
if (checkResultMap.defectDesc) {
|
|
this.rollDefectForm.defectDesc = checkResultMap.defectDesc
|
|
}
|
|
if (!this.rollDefectForm.defectDesc) {
|
|
this.$message.error('请选择有效的不良代码')
|
|
return
|
|
}
|
|
const defectQty = Number(this.rollDefectForm.defectQty)
|
|
if (!defectQty || defectQty <= 0) {
|
|
this.$message.error('不良数量必须大于0')
|
|
return
|
|
}
|
|
|
|
const existing = this.currentRollDefectList.find(item => item.defectCode === this.rollDefectForm.defectCode)
|
|
if (existing) {
|
|
existing.defectQty = Number(existing.defectQty || 0) + defectQty
|
|
existing.defectDesc = this.rollDefectForm.defectDesc
|
|
} else {
|
|
this.currentRollDefectList.push({
|
|
defectCode: this.rollDefectForm.defectCode,
|
|
defectDesc: this.rollDefectForm.defectDesc,
|
|
defectQty: defectQty
|
|
})
|
|
}
|
|
this.resetRollDefectForm()
|
|
},
|
|
|
|
removeRollDefectItem(index) {
|
|
if (!this.currentRollDefectList) {
|
|
return
|
|
}
|
|
this.currentRollDefectList.splice(index, 1)
|
|
},
|
|
|
|
getBaseData(val) {
|
|
if (this.tagNo === 89) {
|
|
this.rollDefectForm.defectCode = val.DefectCode || ''
|
|
this.rollDefectForm.defectDesc = val.DefectDesc || ''
|
|
}
|
|
},
|
|
|
|
getBaseList(val) {
|
|
this.tagNo = val
|
|
this.$nextTick(() => {
|
|
let strVal = ''
|
|
let conSql = ''
|
|
if (val === 89) {
|
|
const workCenterNo = this.getCurrentWorkCenterNo()
|
|
if (!workCenterNo) {
|
|
this.$message.error('当前派工单未维护加工中心,无法选择不良代码')
|
|
return
|
|
}
|
|
strVal = this.rollDefectForm.defectCode || ''
|
|
conSql = " and isnull(A.workCenter_no,'') = '" + workCenterNo + "'"
|
|
}
|
|
this.$refs.baseList.init(val, strVal, conSql)
|
|
})
|
|
},
|
|
|
|
getRollDefectDetails(rollIndex) {
|
|
const list = this.rollDefectMap[String(rollIndex)] || []
|
|
return list
|
|
.map(item => ({
|
|
defectCode: item.defectCode,
|
|
defectDesc: item.defectDesc || '',
|
|
defectQty: Number(item.defectQty || 0)
|
|
}))
|
|
.filter(item => item.defectCode && item.defectQty > 0)
|
|
},
|
|
|
|
// 校验每卷不良原因合计必须等于该卷面损合计
|
|
validateRollDefectQtyBySurfaceLoss() {
|
|
const segments = this.getRollSegments()
|
|
if (!segments.length) {
|
|
return true
|
|
}
|
|
this.normalizeRollDefectMap()
|
|
for (let i = 0; i < segments.length; i++) {
|
|
const rollIndex = segments[i].rollIndex
|
|
const surfaceLossQty = this.getRollSurfaceLossQty(rollIndex)
|
|
const defectReasonQty = this.getRollDefectTotal(rollIndex)
|
|
if (Math.abs(surfaceLossQty - defectReasonQty) > 0.000001) {
|
|
this.$message.error(`第${rollIndex}卷不良原因数量合计(${this.formatQty(defectReasonQty)})必须等于该卷面损数合计(${this.formatQty(surfaceLossQty)})`)
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
|
|
// 验证排数据
|
|
validateRowData() {
|
|
if (this.pageData.rowCount <= 0) {
|
|
this.$message.warning('请输入有效的排数')
|
|
return false
|
|
}
|
|
if (this.pageData.rollCount <= 0) {
|
|
this.$message.warning('请输入有效的卷数')
|
|
return false
|
|
}
|
|
if (this.pageData.rollCount > this.pageData.rowCount) {
|
|
this.$message.warning('卷数不能大于排数')
|
|
return false
|
|
}
|
|
|
|
// 检查是否有数据
|
|
const hasData = this.rowDataList.some(row => (row.goodQty > 0 || row.defectQty > 0))
|
|
if (!hasData) {
|
|
this.$message.warning('请至少输入一行数据')
|
|
return false
|
|
}
|
|
|
|
// 检查每一行的良品数必须大于0
|
|
for (let i = 0; i < this.rowDataList.length; i++) {
|
|
const row = this.rowDataList[i]
|
|
if (row.goodQty === null || row.goodQty === undefined || row.goodQty <= 0) {
|
|
this.$message.error(`第 ${i + 1} 排的良品数必须大于0`)
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
},
|
|
|
|
// Packing List 条件必填校验(仅在 packingListFlag = Y 时生效)
|
|
validatePackingListRequired() {
|
|
if (this.scheduleData.packingListFlag !== 'Y') {
|
|
return true
|
|
}
|
|
|
|
const segments = this.getRollSegments()
|
|
if (!segments.length) {
|
|
return true
|
|
}
|
|
|
|
for (let i = 0; i < segments.length; i++) {
|
|
const segment = segments[i]
|
|
let hasPackingList = false
|
|
for (let rowIndex = segment.startRow; rowIndex <= segment.endRow; rowIndex++) {
|
|
const row = this.rowDataList[rowIndex]
|
|
const packingList = row && row.packingList !== undefined && row.packingList !== null
|
|
? String(row.packingList).trim()
|
|
: ''
|
|
if (packingList) {
|
|
hasPackingList = true
|
|
break
|
|
}
|
|
}
|
|
if (!hasPackingList) {
|
|
this.$message.error(`第${segment.rollIndex}卷的Packing List不能为空`)
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
},
|
|
|
|
// 换班结转保存
|
|
async saveShiftChangeTransfer() {
|
|
// 人员判断
|
|
if (this.pageData.operatorId === '' || this.pageData.operatorId == null) {
|
|
this.$message.error(this.labels.pleaseSwitchOperator);
|
|
return false;
|
|
}
|
|
|
|
// 基础校验
|
|
if (this.pageData.rowCount <= 0) {
|
|
this.$message.warning('请输入有效的排数');
|
|
return false;
|
|
}
|
|
if (this.pageData.rollCount <= 0) {
|
|
this.$message.warning('请输入有效的卷数');
|
|
return false;
|
|
}
|
|
if (this.pageData.rollCount > this.pageData.rowCount) {
|
|
this.$message.warning('卷数不能大于排数');
|
|
return false;
|
|
}
|
|
|
|
// 如果有缓存数据,校验本次数据条数必须大于等于之前的数据
|
|
if (this.hasCachedData && this.rowDataList.length < this.cachedDataCount) {
|
|
this.$message.error(`数据条数不能减少!当前缓存数据${this.cachedDataCount}条,本次提交${this.rowDataList.length}条`);
|
|
return false;
|
|
}
|
|
|
|
// 开启loading
|
|
this.shiftChangeLoading = true;
|
|
|
|
try {
|
|
// 构建保存数据
|
|
const saveData = {
|
|
site: this.scheduleData.site,
|
|
buNo: this.scheduleData.buNo,
|
|
orderNo: this.scheduleData.orderNo,
|
|
seqNo: parseInt(this.scheduleData.seqNo) || 0,
|
|
fixtureNo: this.pageData.fixture,
|
|
shiftFrom: this.scheduleData.shiftNo || '', // 当前班次
|
|
rowCount: this.pageData.rowCount,
|
|
rollCount: this.pageData.rollCount,
|
|
createdBy: this.pageData.operatorId,
|
|
rowDataList: this.rowDataList.map(row => ({
|
|
rowNumber: row.rowNumber,
|
|
goodQty: row.goodQty || 0,
|
|
surfaceLossQty: row.surfaceLossQty || 0,
|
|
poorPerformanceQty: row.poorPerformanceQty || 0,
|
|
defectQty: row.defectQty || 0,
|
|
packingList: row.packingList || '',
|
|
remark: row.remark || ''
|
|
}))
|
|
};
|
|
|
|
const {data} = await saveShiftChangeData(saveData);
|
|
|
|
if (data && data.code === 0) {
|
|
this.$message.success('换班结转数据保存成功!');
|
|
// 更新缓存状态
|
|
this.hasCachedData = true;
|
|
this.cachedDataCount = this.rowDataList.length;
|
|
// 关闭对话框
|
|
this.closeDialog();
|
|
} else {
|
|
this.$message.error(data.msg || '换班结转保存失败');
|
|
}
|
|
} catch (error) {
|
|
console.error('换班结转保存失败:', error);
|
|
this.$message.error('换班结转保存失败:' + error.message);
|
|
} finally {
|
|
this.shiftChangeLoading = false;
|
|
}
|
|
},
|
|
|
|
// 异常截卷操作(产量报告 + 换班结转)
|
|
async executeAbnormalRollCut() {
|
|
// 人员判断
|
|
if (this.pageData.operatorId === '' || this.pageData.operatorId == null) {
|
|
this.$message.error(this.labels.pleaseSwitchOperator);
|
|
return false;
|
|
}
|
|
|
|
// 基础校验
|
|
if (this.pageData.rowCount <= 0) {
|
|
this.$message.warning('请输入有效的排数');
|
|
return false;
|
|
}
|
|
if (this.pageData.rollCount <= 0) {
|
|
this.$message.warning('请输入有效的卷数');
|
|
return false;
|
|
}
|
|
if (this.pageData.rollCount > this.pageData.rowCount) {
|
|
this.$message.warning('卷数不能大于排数');
|
|
return false;
|
|
}
|
|
|
|
// 验证排数据
|
|
if (!this.validateRowData()) {
|
|
return false;
|
|
}
|
|
|
|
// Packing List 显示时要求必填
|
|
if (!this.validatePackingListRequired()) {
|
|
return false;
|
|
}
|
|
|
|
// 校验每卷不良原因数量是否等于该卷面损数量
|
|
if (!this.validateRollDefectQtyBySurfaceLoss()) {
|
|
return false;
|
|
}
|
|
|
|
// 确认框
|
|
try {
|
|
await this.$confirm('确定执行异常截卷操作吗?该操作将执行产量报告。', '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
});
|
|
} catch (e) {
|
|
return false; // 用户取消
|
|
}
|
|
|
|
try {
|
|
const passRuleCheck = await this.checkSplitRuleBeforeAction('异常截卷')
|
|
if (!passRuleCheck) {
|
|
return false
|
|
}
|
|
} catch (error) {
|
|
this.$message.error(error.message || '异常规则校验失败')
|
|
return false
|
|
}
|
|
|
|
// 开启loading
|
|
this.abnormalCutLoading = true;
|
|
|
|
try {
|
|
// ========== 第一步:执行产量报告 + 保存缓存数据 ==========
|
|
// 计算分卷
|
|
const rowsPerRoll = Math.floor(this.pageData.rowCount / this.pageData.rollCount);
|
|
const remainingRows = this.pageData.rowCount % this.pageData.rollCount;
|
|
|
|
let currentRowIndex = 0;
|
|
const allPrintList = []; // 收集所有打印数据
|
|
|
|
// 循环创建每一卷
|
|
for (let rollIndex = 0; rollIndex < this.pageData.rollCount; rollIndex++) {
|
|
const currentRollRows = rowsPerRoll + (rollIndex < remainingRows ? 1 : 0);
|
|
|
|
// 计算当前卷的良品和不良总数
|
|
let totalGoodQty = 0;
|
|
let totalDefectQty = 0;
|
|
const rollRows = [];
|
|
|
|
// 获取当前卷的备注
|
|
let rollRemark = '';
|
|
let rollPackingList = '';
|
|
|
|
// 收集当前卷的行数据
|
|
const currentRollRowDataList = [];
|
|
|
|
for (let i = 0; i < currentRollRows; i++) {
|
|
const row = this.rowDataList[currentRowIndex + i];
|
|
totalGoodQty += row.goodQty || 0;
|
|
totalDefectQty += row.defectQty || 0;
|
|
|
|
if (i === 0) {
|
|
rollRemark = row.remark || '';
|
|
rollPackingList = row.packingList || '';
|
|
}
|
|
|
|
rollRows.push({
|
|
rowNumber: row.rowNumber,
|
|
goodQty: row.goodQty || 0,
|
|
surfaceLossQty: row.surfaceLossQty || 0,
|
|
poorPerformanceQty: row.poorPerformanceQty || 0,
|
|
defectQty: row.defectQty || 0,
|
|
totalQty: row.totalQty || 0,
|
|
packingList: row.packingList || '',
|
|
remark: row.remark || ''
|
|
});
|
|
|
|
// 收集当前卷的行数据用于保存缓存
|
|
currentRollRowDataList.push({
|
|
rowNumber: row.rowNumber,
|
|
goodQty: row.goodQty || 0,
|
|
surfaceLossQty: row.surfaceLossQty || 0,
|
|
poorPerformanceQty: row.poorPerformanceQty || 0,
|
|
defectQty: row.defectQty || 0,
|
|
packingList: row.packingList || '',
|
|
remark: row.remark || ''
|
|
});
|
|
}
|
|
|
|
// 构建请求数据
|
|
const requestData = {
|
|
...this.pageData,
|
|
buNo: this.scheduleData.buNo,
|
|
rollQty: totalGoodQty,
|
|
rollNums: 1,
|
|
defectQty: totalDefectQty,
|
|
rollRows: rollRows,
|
|
rollDefectDetails: this.getRollDefectDetails(rollIndex + 1),
|
|
packingList: rollPackingList,
|
|
remark: rollRemark
|
|
};
|
|
|
|
// 调用后端创建单个卷
|
|
const {data} = await createSplitSfdcRoll(requestData);
|
|
|
|
if (data.code === 500) {
|
|
throw new Error(data.msg);
|
|
}
|
|
|
|
// 获取当前卷返回的新卷号
|
|
let currentNewRollNo = '';
|
|
if (data.printList && data.printList.length > 0) {
|
|
currentNewRollNo = data.printList[0].rollNo || '';
|
|
// 收集打印数据
|
|
allPrintList.push(...data.printList);
|
|
}
|
|
|
|
const abnormalRollData = {
|
|
site: this.scheduleData.site,
|
|
buNo: this.scheduleData.buNo,
|
|
orderNo: this.scheduleData.orderNo,
|
|
seqNo: parseInt(this.scheduleData.seqNo) || 0,
|
|
fixtureNo: this.pageData.fixture,
|
|
shiftFrom: this.scheduleData.shiftNo || '',
|
|
rowCount: currentRollRows,
|
|
rollCount: 1,
|
|
createdBy: this.pageData.operatorId,
|
|
currentRollNo: this.scheduleData.rollNo || '',
|
|
newRollNo: currentNewRollNo,
|
|
operatorId: this.pageData.operatorId,
|
|
rowDataList: currentRollRowDataList
|
|
};
|
|
|
|
const {data: abnormalData} = await saveAbnormalRollData(abnormalRollData);
|
|
|
|
if (!abnormalData || abnormalData.code !== 0) {
|
|
console.error(`第${rollIndex + 1}卷缓存数据保存失败:`, abnormalData && abnormalData.msg);
|
|
}
|
|
|
|
this.$message.success(`第${rollIndex + 1}/${this.pageData.rollCount}卷创建成功`);
|
|
|
|
currentRowIndex += currentRollRows;
|
|
}
|
|
|
|
// 如果有换班结转缓存数据,标记为已处理
|
|
if (this.hasCachedData) {
|
|
try {
|
|
await markShiftChangeAsProcessed({
|
|
site: this.scheduleData.site,
|
|
orderNo: this.scheduleData.orderNo,
|
|
seqNo: parseInt(this.scheduleData.seqNo) || 0,
|
|
processedBy: this.pageData.operatorId
|
|
});
|
|
this.hasCachedData = false;
|
|
this.cachedDataCount = 0;
|
|
} catch (err) {
|
|
console.error('标记换班结转数据为已处理失败:', err);
|
|
}
|
|
}
|
|
|
|
this.$message.success('异常截卷操作完成!数据已保存。');
|
|
|
|
// ========== 第二步:打印标签(和产量报告一样)==========
|
|
if (allPrintList.length > 0) {
|
|
await this.printLabelsWithTemplate(allPrintList);
|
|
}
|
|
|
|
// 放行执行完成后:回写operate_flag=Y并清理对应缓存
|
|
try {
|
|
await this.completeSplitRuleTodoOperationIfNeeded();
|
|
await this.refreshSplitRuleLockStatus(false);
|
|
} catch (completeError) {
|
|
console.error('回写代办操作状态失败:', completeError);
|
|
this.$message.warning(completeError.message || '回写代办操作状态失败,请联系管理员检查');
|
|
}
|
|
|
|
if (this.triggerPendingRuleFollowupIfNeeded()) {
|
|
this.abnormalCutLoading = false;
|
|
return true;
|
|
}
|
|
|
|
// 延时关闭弹窗
|
|
setTimeout(() => {
|
|
this.closeDialog();
|
|
this.abnormalCutLoading = false;
|
|
}, 1000);
|
|
|
|
} catch (error) {
|
|
console.error('异常截卷操作失败:', error);
|
|
this.$message.error('异常截卷操作失败:' + error.message);
|
|
this.abnormalCutLoading = false;
|
|
}
|
|
},
|
|
|
|
// 表格合并行方法(备注列根据卷数合并)
|
|
objectSpanMethod({ row, column, rowIndex, columnIndex }) {
|
|
// 对备注、Packing List和操作列进行按卷合并
|
|
if (column.label === '备注' || column.label === 'Packing List' || column.label === '操作') {
|
|
const segment = this.getRollSegmentByRowIndex(rowIndex)
|
|
if (!segment) {
|
|
return
|
|
}
|
|
if (rowIndex === segment.startRow) {
|
|
return {
|
|
rowspan: segment.rowCount,
|
|
colspan: 1
|
|
}
|
|
}
|
|
return {
|
|
rowspan: 0,
|
|
colspan: 0
|
|
}
|
|
}
|
|
},
|
|
|
|
// 计算备注wrapper的样式(使用padding-top实现垂直居中)
|
|
getRemarkWrapperStyle(rowIndex) {
|
|
if (this.pageData.rowCount <= 0 || this.pageData.rollCount <= 0) {
|
|
return {}
|
|
}
|
|
const segment = this.getRollSegmentByRowIndex(rowIndex)
|
|
if (!segment) {
|
|
return {}
|
|
}
|
|
const currentRollRows = segment.rowCount
|
|
|
|
// 每行高度约为32px
|
|
const rowHeight = 32
|
|
const totalHeight = currentRollRows * rowHeight
|
|
|
|
// textarea的实际高度约为24px(单行)
|
|
const textareaHeight = 24
|
|
|
|
// 计算需要的padding-top来实现垂直居中
|
|
const paddingTop = (totalHeight - textareaHeight) / 2
|
|
|
|
// 返回样式对象
|
|
return {
|
|
paddingTop: paddingTop + 'px'
|
|
}
|
|
},
|
|
|
|
// ===================== 分卷优化方法结束 =====================
|
|
|
|
// 查询固定载具列表
|
|
queryFixedCarrierList() {
|
|
const params = {
|
|
site: this.scheduleData.site,
|
|
carrierNo: this.carrierSearchData.carrierNo,
|
|
carrierTypeName: this.carrierSearchData.carrierTypeName,
|
|
specification: this.carrierSearchData.specification
|
|
}
|
|
getFixedCarrierList(params).then(({data}) => {
|
|
if (data && data.code === 0) {
|
|
this.carrierList = data.data
|
|
this.carrierModelFlag = true
|
|
} else {
|
|
this.$message.error(data.msg || '获取固定载具列表失败')
|
|
}
|
|
}).catch(error => {
|
|
this.$message.error('获取固定载具列表失败: ' + error.message)
|
|
})
|
|
},
|
|
|
|
// 选中固定载具
|
|
selectCarrier(row) {
|
|
this.pageData.fixture = row.carrierNo
|
|
this.carrierModelFlag = false
|
|
},
|
|
|
|
// 批量编辑模态框
|
|
editBatchModel() {
|
|
if (this.editBatchVisible) {
|
|
this.editBatchVisible = false
|
|
} else {
|
|
this.editBatchVisible = true
|
|
}
|
|
},
|
|
|
|
// 批量保存固定载具可用数量
|
|
batchSaveCarrier() {
|
|
this.saveLoading = true
|
|
// 调用API保存
|
|
import('@/api/yieldReport/com_separate_roll.js').then(module => {
|
|
const { updateFixedCarrierBatch } = module
|
|
updateFixedCarrierBatch(this.carrierList).then(({data}) => {
|
|
if (data && data.code === 0) {
|
|
this.$message.success('批量保存成功')
|
|
this.editBatchVisible = false
|
|
this.queryFixedCarrierList()
|
|
} else {
|
|
this.$message.error(data.msg || '批量保存失败')
|
|
}
|
|
}).catch(error => {
|
|
this.$message.error('批量保存失败: ' + error.message)
|
|
}).finally(() => {
|
|
this.saveLoading = false
|
|
})
|
|
})
|
|
},
|
|
|
|
// 打开新增固定载具对话框
|
|
addCarrierModal() {
|
|
console.log(this.scheduleData)
|
|
this.addCarrierData = {
|
|
bu: this.scheduleData.site + '_' + this.scheduleData.buNo || '',
|
|
site: this.scheduleData.site || '',
|
|
buNo: this.scheduleData.buNo || '',
|
|
carrierTypeCode: '',
|
|
carrierTypeName: '',
|
|
assetNo: '',
|
|
specification: '',
|
|
purchaseDate: null,
|
|
currentLocation: '',
|
|
dimensions: '',
|
|
availableQty: null,
|
|
maxWeight: null,
|
|
expectedLifeCycles: null,
|
|
expectedLifeDays: null,
|
|
status: 1,
|
|
remark: ''
|
|
};
|
|
this.addCarrierFlag = true;
|
|
},
|
|
|
|
// 保存新增固定载具
|
|
saveAddCarrier() {
|
|
this.$refs.addCarrierForm.validate((valid) => {
|
|
if (valid) {
|
|
this.addCarrierLoading = true;
|
|
fixedCarrierSave(this.addCarrierData).then(({data}) => {
|
|
if (data && data.code === 0) {
|
|
this.$message.success('新增成功');
|
|
this.addCarrierFlag = false;
|
|
this.queryFixedCarrierList();
|
|
} else {
|
|
this.$message.error(data.msg || '新增失败');
|
|
}
|
|
}).catch(error => {
|
|
this.$message.error('新增失败: ' + error.message);
|
|
}).finally(() => {
|
|
this.addCarrierLoading = false;
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
// 打印固定载具
|
|
printCarrier(row) {
|
|
const LODOP = getLodop()
|
|
if (!LODOP) {
|
|
this.$message.error('未检测到打印控件,请安装CLodop打印控件!')
|
|
return
|
|
}
|
|
|
|
try {
|
|
// 初始化打印
|
|
LODOP.PRINT_INIT('固定载具标签打印')
|
|
|
|
// 设置纸张尺寸:70mm x 20mm
|
|
LODOP.SET_PRINT_PAGESIZE(0, 700, 200, '')
|
|
|
|
// 绘制外边框
|
|
LODOP.ADD_PRINT_RECT('1mm', '1mm', '68mm', '18mm', 0, 1)
|
|
|
|
// 绘制中间竖线(左右分隔)
|
|
LODOP.ADD_PRINT_LINE('1mm', '50mm', '19mm', '50mm', 0, 1)
|
|
|
|
// 绘制左侧横线(三行文本之间的分隔)
|
|
LODOP.ADD_PRINT_LINE('7mm', '1mm', '7mm', '50mm', 0, 1)
|
|
LODOP.ADD_PRINT_LINE('13mm', '1mm', '13mm', '50mm', 0, 1)
|
|
|
|
// 左侧文字信息 - 标签条码(第一行)
|
|
const text1 = LODOP.ADD_PRINT_TEXT('2.5mm', '2mm', '46mm', '5mm', 'NO.:' + (row.carrierNo || ''))
|
|
LODOP.SET_PRINT_STYLEA(text1, "FontName", "Microsoft YaHei")
|
|
LODOP.SET_PRINT_STYLEA(text1, "FontSize", 5)
|
|
LODOP.SET_PRINT_STYLEA(text1, "Bold", 1)
|
|
|
|
// 左侧文字信息 - 载具类型编码(第二行)
|
|
const text2 = LODOP.ADD_PRINT_TEXT('8.5mm', '2mm', '46mm', '5mm', 'Type:' + (row.carrierTypeCode || ''))
|
|
LODOP.SET_PRINT_STYLEA(text2, "FontName", "Microsoft YaHei")
|
|
LODOP.SET_PRINT_STYLEA(text2, "FontSize", 5)
|
|
LODOP.SET_PRINT_STYLEA(text2, "Bold", 1)
|
|
|
|
// 左侧文字信息 - 载具类型名称(第三行)
|
|
const text3 = LODOP.ADD_PRINT_TEXT('14.5mm', '2mm', '46mm', '5mm', 'TypeDesc:' + (row.carrierTypeName || ''))
|
|
LODOP.SET_PRINT_STYLEA(text3, "FontName", "Microsoft YaHei")
|
|
LODOP.SET_PRINT_STYLEA(text3, "FontSize", 5)
|
|
LODOP.SET_PRINT_STYLEA(text3, "Bold", 1)
|
|
|
|
// 右侧二维码(使用 LODOP 原生二维码)
|
|
const qrIndex = LODOP.ADD_PRINT_BARCODE('1.5mm', '50mm', '18.5mm', '18.5mm', 'QRCode', row.carrierNo || '')
|
|
LODOP.SET_PRINT_STYLEA(qrIndex, "QRCodeVersion", 0)
|
|
LODOP.SET_PRINT_STYLEA(qrIndex, "QRCodeErrorLevel", "M")
|
|
|
|
// 执行打印
|
|
LODOP.PRINT()
|
|
|
|
} catch (error) {
|
|
console.error('打印失败:', error)
|
|
this.$message.error('打印失败: ' + error.message)
|
|
}
|
|
},
|
|
|
|
/*关闭modal*/
|
|
closeDialog(){
|
|
if (this.exceptionRuleLocked) {
|
|
return
|
|
}
|
|
this.stopSplitRuleLockPolling()
|
|
this.clearExceptionRuleVisualMeta()
|
|
this.resetPendingRuleFollowup()
|
|
//刷新报工的页面
|
|
this.$emit('refreshPageData');
|
|
//关闭当前的页面
|
|
this.$emit('update:visible', false);
|
|
},
|
|
|
|
handleMainDialogBeforeClose (done) {
|
|
if (this.exceptionRuleLocked) {
|
|
return
|
|
}
|
|
done()
|
|
},
|
|
|
|
onDialogClosed () {
|
|
this.stopSplitRuleLockPolling()
|
|
this.clearExceptionRuleVisualMeta()
|
|
},
|
|
|
|
normalizeWarningLevel (value) {
|
|
return String(value || '').trim()
|
|
},
|
|
|
|
isWarningLevelStyle () {
|
|
const warningLevel = this.normalizeWarningLevel(this.exceptionRuleWarningLevel)
|
|
return warningLevel === '轻微' || warningLevel === '一般'
|
|
},
|
|
|
|
getExceptionRuleTitleCn () {
|
|
return this.isWarningLevelStyle() ? '过程质量预警' : '过程质量异常'
|
|
},
|
|
|
|
getExceptionRuleTitleLock () {
|
|
return this.isWarningLevelStyle() ? '系统预警 / SYSTEM WARNING' : '系统锁定 / SYSTEM LOCKDOWN'
|
|
},
|
|
|
|
getExceptionRuleActiveText () {
|
|
return this.isWarningLevelStyle() ? 'WARNING ACTIVE' : 'LOCKDOWN ACTIVE'
|
|
},
|
|
|
|
getNowDateText () {
|
|
return this.dayjs(new Date()).format('YYYY-MM-DD')
|
|
},
|
|
|
|
getNowTimeText () {
|
|
return this.dayjs(new Date()).format('HH:mm:ss')
|
|
},
|
|
|
|
refreshExceptionRuleClock () {
|
|
const now = this.dayjs(new Date())
|
|
this.exceptionRuleClockDate = now.format('YYYY-MM-DD')
|
|
this.exceptionRuleClockTime = now.format('HH:mm:ss')
|
|
},
|
|
|
|
startExceptionRuleClock () {
|
|
this.refreshExceptionRuleClock()
|
|
if (this.exceptionRuleClockTimer) {
|
|
return
|
|
}
|
|
this.exceptionRuleClockTimer = setInterval(() => {
|
|
this.refreshExceptionRuleClock()
|
|
}, 1000)
|
|
},
|
|
|
|
stopExceptionRuleClock () {
|
|
if (this.exceptionRuleClockTimer) {
|
|
clearInterval(this.exceptionRuleClockTimer)
|
|
this.exceptionRuleClockTimer = null
|
|
}
|
|
},
|
|
|
|
toRuleNumber (value) {
|
|
const num = Number(value)
|
|
return Number.isFinite(num) ? num : null
|
|
},
|
|
|
|
formatRuleNumber (value, scale) {
|
|
const num = this.toRuleNumber(value)
|
|
if (num === null) {
|
|
return '--'
|
|
}
|
|
const digits = Number.isInteger(scale) && scale >= 0 ? scale : 2
|
|
const fixed = num.toFixed(digits)
|
|
return fixed.replace(/\.?0+$/, '')
|
|
},
|
|
|
|
getExceptionRuleEquipmentText () {
|
|
return this.scheduleData.resourceDesc || this.scheduleData.resourceId || '--'
|
|
},
|
|
|
|
getExceptionRuleLockedTimeText () {
|
|
return this.exceptionRuleLockedAt || `${this.getNowDateText()} ${this.getNowTimeText()}`
|
|
},
|
|
|
|
getExceptionRuleWorkOrderText () {
|
|
return this.scheduleData.orderNo || '--'
|
|
},
|
|
|
|
getExceptionRuleSeqNoText () {
|
|
return this.scheduleData.seqNo || '--'
|
|
},
|
|
|
|
formatExceptionRuleDefectRateText () {
|
|
const text = this.formatRuleNumber(this.exceptionRuleMatchedDefectRate, 2)
|
|
return text === '--' ? '--' : `${text}%`
|
|
},
|
|
|
|
formatExceptionRuleAffectedQtyText () {
|
|
const text = this.formatRuleNumber(this.exceptionRuleMatchedTotalQty, 2)
|
|
return text === '--' ? '--' : `${text} pcs`
|
|
},
|
|
|
|
formatExceptionRuleProducedQtyText () {
|
|
const text = this.formatRuleNumber(this.scheduleData.qtyApprove, 2)
|
|
return text === '--' ? '--' : `${text} pcs`
|
|
},
|
|
|
|
applyExceptionRuleVisualMeta (resultMap, refreshLockTime) {
|
|
this.startExceptionRuleClock()
|
|
const warningLevel = this.normalizeWarningLevel(resultMap && resultMap.warningLevel)
|
|
if (warningLevel) {
|
|
this.exceptionRuleWarningLevel = warningLevel
|
|
}
|
|
const lockedTime = String((resultMap && resultMap.lockedTime) || '').trim()
|
|
if (lockedTime) {
|
|
this.exceptionRuleLockedAt = lockedTime
|
|
} else if (refreshLockTime || !this.exceptionRuleLockedAt) {
|
|
this.exceptionRuleLockedAt = `${this.getNowDateText()} ${this.getNowTimeText()}`
|
|
}
|
|
|
|
if (resultMap && Object.prototype.hasOwnProperty.call(resultMap, 'matchedRollIndex')) {
|
|
this.exceptionRuleMatchedRollIndex = resultMap.matchedRollIndex
|
|
}
|
|
if (resultMap && Object.prototype.hasOwnProperty.call(resultMap, 'matchedDefectRate')) {
|
|
this.exceptionRuleMatchedDefectRate = resultMap.matchedDefectRate
|
|
}
|
|
if (resultMap && Object.prototype.hasOwnProperty.call(resultMap, 'matchedTotalQty')) {
|
|
this.exceptionRuleMatchedTotalQty = resultMap.matchedTotalQty
|
|
}
|
|
},
|
|
|
|
clearExceptionRuleVisualMeta () {
|
|
this.stopExceptionRuleClock()
|
|
this.exceptionRuleWarningLevel = ''
|
|
this.exceptionRuleLockedAt = ''
|
|
this.exceptionRuleMatchedRollIndex = null
|
|
this.exceptionRuleMatchedDefectRate = null
|
|
this.exceptionRuleMatchedTotalQty = null
|
|
this.exceptionRuleClockDate = ''
|
|
this.exceptionRuleClockTime = ''
|
|
},
|
|
|
|
buildSplitRuleStatusPayload () {
|
|
return {
|
|
site: this.scheduleData.site,
|
|
buNo: this.scheduleData.buNo,
|
|
orderNo: this.scheduleData.orderNo,
|
|
seqNo: this.scheduleData.seqNo
|
|
}
|
|
},
|
|
|
|
async refreshSplitRuleLockStatus (showUnlockHint) {
|
|
const payload = this.buildSplitRuleStatusPayload()
|
|
if (!payload.site || !payload.buNo || !payload.orderNo || !payload.seqNo) {
|
|
return
|
|
}
|
|
const wasLocked = this.exceptionRuleLocked
|
|
const { data } = await getSplitRuleLockStatus(payload)
|
|
if (!data || data.code === 500) {
|
|
return
|
|
}
|
|
const resultMap = data.resultMap || {}
|
|
const locked = !!resultMap.locked
|
|
this.exceptionRuleLocked = locked
|
|
if (locked) {
|
|
this.applyExceptionRuleVisualMeta(resultMap, !wasLocked)
|
|
this.exceptionRuleLockMessage = resultMap.message || '当前异常代办尚未审核,已锁定创建分卷操作。'
|
|
if (!wasLocked) {
|
|
this.$emit('lockPageByExceptionRule', resultMap)
|
|
}
|
|
if (!this.lockStatusTimer) {
|
|
this.startSplitRuleLockPolling()
|
|
}
|
|
return
|
|
}
|
|
this.stopSplitRuleLockPolling()
|
|
this.exceptionRuleLockMessage = ''
|
|
this.clearExceptionRuleVisualMeta()
|
|
this.$emit('unlockPageByExceptionRule', resultMap)
|
|
if (wasLocked && showUnlockHint && resultMap.bypassByAudit) {
|
|
this.$message.success('QC已审核,已解除锁定,可继续操作。')
|
|
}
|
|
},
|
|
|
|
startSplitRuleLockPolling () {
|
|
this.stopSplitRuleLockPolling()
|
|
this.lockStatusTimer = setInterval(() => {
|
|
this.refreshSplitRuleLockStatus(true).catch(err => {
|
|
console.error('轮询锁定状态失败:', err)
|
|
})
|
|
}, 10000)
|
|
},
|
|
|
|
stopSplitRuleLockPolling () {
|
|
if (this.lockStatusTimer) {
|
|
clearInterval(this.lockStatusTimer)
|
|
this.lockStatusTimer = null
|
|
}
|
|
},
|
|
|
|
resetPendingRuleFollowup () {
|
|
this.pendingRuleFollowupAction = ''
|
|
this.pendingRuleFollowupResultMap = null
|
|
},
|
|
|
|
triggerPendingRuleFollowupIfNeeded () {
|
|
const action = this.pendingRuleFollowupAction
|
|
if (!action) {
|
|
return false
|
|
}
|
|
const resultMap = this.pendingRuleFollowupResultMap || {}
|
|
this.resetPendingRuleFollowup()
|
|
this.$emit('refreshPageData')
|
|
this.$emit('update:visible', false)
|
|
if (action === 'OPEN_DEFECT') {
|
|
this.$emit('openDefectByExceptionRule', resultMap)
|
|
return true
|
|
}
|
|
if (action === 'OPEN_DOWNTIME') {
|
|
this.$emit('openDownTimeByExceptionRule', resultMap)
|
|
return true
|
|
}
|
|
return false
|
|
},
|
|
|
|
async completeSplitRuleTodoOperationIfNeeded () {
|
|
const payload = this.buildSplitRuleStatusPayload()
|
|
if (!payload.site || !payload.buNo || !payload.orderNo || !payload.seqNo) {
|
|
return
|
|
}
|
|
payload.operatorId = this.pageData.operatorId
|
|
payload.functionName = this.scheduleData.functionName || ''
|
|
const { data } = await completeSplitRuleTodoOperation(payload)
|
|
if (data && data.code === 500) {
|
|
throw new Error(data.msg || '回写代办操作状态失败')
|
|
}
|
|
},
|
|
|
|
buildSplitRuleCheckPayload (actionName) {
|
|
return {
|
|
site: this.scheduleData.site,
|
|
buNo: this.scheduleData.buNo,
|
|
orderNo: this.scheduleData.orderNo,
|
|
itemNo: this.scheduleData.itemNo,
|
|
seqNo: this.scheduleData.seqNo,
|
|
rollNo: this.scheduleData.rollNo,
|
|
operatorId: this.pageData.operatorId,
|
|
functionName: actionName || '',
|
|
fixture: this.pageData.fixture || '',
|
|
shiftNo: this.scheduleData.shiftNo || '',
|
|
rowCount: this.pageData.rowCount || 0,
|
|
rollCount: this.pageData.rollCount || 0,
|
|
totalRollQty: Number(this.scheduleData.totalRollQty || 0),
|
|
rollRows: (this.rowDataList || []).map(row => ({
|
|
rowNumber: row.rowNumber,
|
|
goodQty: row.goodQty || 0,
|
|
surfaceLossQty: row.surfaceLossQty || 0,
|
|
poorPerformanceQty: row.poorPerformanceQty || 0,
|
|
defectQty: row.defectQty || 0,
|
|
totalQty: row.totalQty || 0,
|
|
packingList: row.packingList || '',
|
|
remark: row.remark || ''
|
|
}))
|
|
}
|
|
},
|
|
|
|
async checkSplitRuleBeforeAction (actionName) {
|
|
this.resetPendingRuleFollowup()
|
|
const payload = this.buildSplitRuleCheckPayload(actionName)
|
|
const { data } = await checkSplitRollExceptionRule(payload)
|
|
if (!data || data.code === 500) {
|
|
throw new Error((data && data.msg) || '异常规则校验失败')
|
|
}
|
|
const resultMap = data.resultMap || {}
|
|
if (resultMap.bypassByAudit) {
|
|
this.exceptionRuleLocked = false
|
|
this.exceptionRuleLockMessage = ''
|
|
this.clearExceptionRuleVisualMeta()
|
|
this.stopSplitRuleLockPolling()
|
|
this.$emit('unlockPageByExceptionRule', resultMap)
|
|
}
|
|
if (!resultMap.matched) {
|
|
return true
|
|
}
|
|
|
|
const action = resultMap.action || 'NONE'
|
|
const message = resultMap.message || '触发异常规则,请先处理。'
|
|
if (action === 'OPEN_DEFECT') {
|
|
this.$message.warning(message)
|
|
this.pendingRuleFollowupAction = action
|
|
this.pendingRuleFollowupResultMap = resultMap
|
|
return true
|
|
}
|
|
if (action === 'OPEN_DOWNTIME') {
|
|
this.$message.warning(message)
|
|
this.pendingRuleFollowupAction = action
|
|
this.pendingRuleFollowupResultMap = resultMap
|
|
return true
|
|
}
|
|
if (action === 'LOCK_PAGE') {
|
|
this.exceptionRuleLocked = true
|
|
this.applyExceptionRuleVisualMeta(resultMap, true)
|
|
this.exceptionRuleLockMessage = message
|
|
this.$emit('lockPageByExceptionRule', resultMap)
|
|
this.startSplitRuleLockPolling()
|
|
return false
|
|
}
|
|
return true
|
|
},
|
|
|
|
/*检查材料卷号的数据*/
|
|
checkValidQty() {
|
|
//获取当前是的数量
|
|
let rollQty = parseFloat(this.pageData.rollQty);
|
|
//判断是否可以修改
|
|
if (rollQty <= 0 || !Number.isInteger(rollQty)){
|
|
this.$message.error(this.labels.approvedQtyMustBeInteger);
|
|
return false;
|
|
}
|
|
},
|
|
|
|
/*检查材料卷号的数据*/
|
|
checkRollNums() {
|
|
//获取当前是的数量
|
|
let rollNums = parseFloat(this.pageData.rollNums);
|
|
//判断是否是正整数
|
|
if (rollNums <= 0 || !Number.isInteger(rollNums)){
|
|
this.$message.error(this.labels.rollQtyMustBeInteger);
|
|
return false;
|
|
}
|
|
},
|
|
|
|
checkCreateSeparateRolllBun() {
|
|
//人员判断
|
|
if (this.pageData.operatorId === '' || this.pageData.operatorId == null) {
|
|
this.$message.error(this.labels.pleaseSwitchOperator);
|
|
return false;
|
|
}
|
|
|
|
// 如果有缓存数据,校验本次数据条数必须大于等于之前的数据
|
|
if (this.hasCachedData && this.rowDataList.length < this.cachedDataCount) {
|
|
this.$message.error(`数据条数不能减少!当前缓存数据${this.cachedDataCount}条,本次提交${this.rowDataList.length}条`);
|
|
return false;
|
|
}
|
|
|
|
// 如果有缓存数据,校验缓存中带过来的数据的良品数、面损、性能不良不能小于缓存中的数据
|
|
if (this.hasCachedData && this.cachedRowDataList.length > 0) {
|
|
for (let i = 0; i < this.cachedRowDataList.length; i++) {
|
|
const cachedRow = this.cachedRowDataList[i];
|
|
const currentRow = this.rowDataList[i];
|
|
|
|
if (currentRow) {
|
|
// 校验良品数不能小于缓存值
|
|
if ((currentRow.goodQty || 0) < (cachedRow.goodQty || 0)) {
|
|
this.$message.error(`第${i + 1}排的良品数量(${currentRow.goodQty})不能小于缓存值(${cachedRow.goodQty})`);
|
|
return false;
|
|
}
|
|
// 校验面损数量不能小于缓存值
|
|
if ((currentRow.surfaceLossQty || 0) < (cachedRow.surfaceLossQty || 0)) {
|
|
this.$message.error(`第${i + 1}排的面损数量(${currentRow.surfaceLossQty})不能小于缓存值(${cachedRow.surfaceLossQty})`);
|
|
return false;
|
|
}
|
|
// 校验性能不良数量不能小于缓存值
|
|
if ((currentRow.poorPerformanceQty || 0) < (cachedRow.poorPerformanceQty || 0)) {
|
|
this.$message.error(`第${i + 1}排的性能不良数量(${currentRow.poorPerformanceQty})不能小于缓存值(${cachedRow.poorPerformanceQty})`);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 开启loading
|
|
this.yieldReportLoading = true;
|
|
|
|
this.createSeparateRolllOperation();
|
|
},
|
|
|
|
/*执行创建分卷的操作*/
|
|
async createSeparateRolllOperation() {
|
|
// 验证排数据
|
|
if (!this.validateRowData()) {
|
|
this.yieldReportLoading = false;
|
|
return false
|
|
}
|
|
|
|
// Packing List 显示时要求必填
|
|
if (!this.validatePackingListRequired()) {
|
|
this.yieldReportLoading = false;
|
|
return false
|
|
}
|
|
|
|
// 校验每卷不良原因数量是否等于该卷面损数量
|
|
if (!this.validateRollDefectQtyBySurfaceLoss()) {
|
|
this.yieldReportLoading = false;
|
|
return false
|
|
}
|
|
|
|
try {
|
|
const passRuleCheck = await this.checkSplitRuleBeforeAction('产量报告')
|
|
if (!passRuleCheck) {
|
|
this.yieldReportLoading = false
|
|
return false
|
|
}
|
|
} catch (error) {
|
|
this.$message.error(error.message || '异常规则校验失败')
|
|
this.yieldReportLoading = false
|
|
return false
|
|
}
|
|
|
|
try {
|
|
// 计算分卷
|
|
const rowsPerRoll = Math.floor(this.pageData.rowCount / this.pageData.rollCount)
|
|
const remainingRows = this.pageData.rowCount % this.pageData.rollCount
|
|
|
|
let currentRowIndex = 0
|
|
const allPrintList = []
|
|
|
|
// 循环创建每一卷
|
|
for (let rollIndex = 0; rollIndex < this.pageData.rollCount; rollIndex++) {
|
|
// 计算当前卷的排数
|
|
const currentRollRows = rowsPerRoll + (rollIndex < remainingRows ? 1 : 0)
|
|
|
|
// 计算当前卷的良品和不良总数
|
|
let totalGoodQty = 0
|
|
let totalDefectQty = 0
|
|
const rollRows = []
|
|
|
|
// 获取当前卷的备注(第一排的备注)
|
|
let rollRemark = ''
|
|
let rollPackingList = ''
|
|
|
|
for (let i = 0; i < currentRollRows; i++) {
|
|
const row = this.rowDataList[currentRowIndex + i]
|
|
totalGoodQty += row.goodQty || 0
|
|
totalDefectQty += row.defectQty || 0
|
|
|
|
// 第一排的备注作为整卷的备注
|
|
if (i === 0) {
|
|
rollRemark = row.remark || ''
|
|
rollPackingList = row.packingList || ''
|
|
}
|
|
|
|
rollRows.push({
|
|
rowNumber: row.rowNumber,
|
|
goodQty: row.goodQty || 0,
|
|
surfaceLossQty: row.surfaceLossQty || 0,
|
|
poorPerformanceQty: row.poorPerformanceQty || 0,
|
|
defectQty: row.defectQty || 0,
|
|
totalQty: row.totalQty || 0,
|
|
packingList: row.packingList || '',
|
|
remark: row.remark || ''
|
|
})
|
|
}
|
|
|
|
// 构建请求数据(每次调用rollNums固定为1)
|
|
const requestData = {
|
|
...this.pageData,
|
|
buNo: this.scheduleData.buNo,
|
|
rollQty: totalGoodQty, // 该卷的良品总数
|
|
rollNums: 1, // 固定为1
|
|
defectQty: totalDefectQty, // 该卷的不良总数
|
|
rollRows: rollRows, // 该卷包含的排数据
|
|
rollDefectDetails: this.getRollDefectDetails(rollIndex + 1),
|
|
packingList: rollPackingList, // 该卷的Packing List
|
|
remark: rollRemark // 该卷的备注
|
|
}
|
|
|
|
// 调用后端创建单个卷
|
|
const {data} = await createSplitSfdcRoll(requestData)
|
|
|
|
if (data.code === 500) {
|
|
throw new Error(data.msg)
|
|
}
|
|
|
|
// 收集打印数据
|
|
if (data.printList && data.printList.length > 0) {
|
|
allPrintList.push(...data.printList)
|
|
}
|
|
|
|
// 进度提示
|
|
this.$message.success(`第${rollIndex + 1}/${this.pageData.rollCount}卷创建成功`)
|
|
|
|
currentRowIndex += currentRollRows
|
|
}
|
|
|
|
// 所有卷创建完成
|
|
this.$message.success('创建分卷完成!')
|
|
|
|
// 如果有缓存数据,标记为已处理
|
|
if (this.hasCachedData) {
|
|
try {
|
|
await markShiftChangeAsProcessed({
|
|
site: this.scheduleData.site,
|
|
orderNo: this.scheduleData.orderNo,
|
|
seqNo: parseInt(this.scheduleData.seqNo) || 0,
|
|
processedBy: this.pageData.operatorId
|
|
});
|
|
// 重置缓存状态
|
|
this.hasCachedData = false;
|
|
this.cachedDataCount = 0;
|
|
} catch (err) {
|
|
console.error('标记换班结转数据为已处理失败:', err);
|
|
}
|
|
}
|
|
|
|
// 打印标签(优化版:使用模板打印)
|
|
if (allPrintList.length > 0) {
|
|
await this.printLabelsWithTemplate(allPrintList)
|
|
}
|
|
|
|
// 放行执行完成后:回写operate_flag=Y并清理对应缓存
|
|
try {
|
|
await this.completeSplitRuleTodoOperationIfNeeded()
|
|
await this.refreshSplitRuleLockStatus(false)
|
|
} catch (completeError) {
|
|
console.error('回写代办操作状态失败:', completeError)
|
|
this.$message.warning(completeError.message || '回写代办操作状态失败,请联系管理员检查')
|
|
}
|
|
|
|
if (this.triggerPendingRuleFollowupIfNeeded()) {
|
|
this.yieldReportLoading = false
|
|
return true
|
|
}
|
|
|
|
// 延时关闭弹窗
|
|
setTimeout(() =>{
|
|
//关闭当前的页面
|
|
this.closeDialog()
|
|
this.yieldReportLoading = false;
|
|
}, 1000)
|
|
|
|
} catch (error) {
|
|
this.$message.error('创建分卷失败: ' + error.message)
|
|
this.yieldReportLoading = false;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 使用模板打印标签(优化版)
|
|
* @param {Array} printList - 打印数据列表
|
|
*/
|
|
async printLabelsWithTemplate(printList) {
|
|
try {
|
|
// 1. 查询物料的标签模板设置(找默认模板)
|
|
const templateParams = {
|
|
site: this.scheduleData.site,
|
|
buNo: this.scheduleData.buNo,
|
|
partNo: this.scheduleData.partNo
|
|
};
|
|
|
|
const {data: templateData} = await getPartLabelTemplateList(templateParams);
|
|
|
|
if (!templateData || templateData.code !== 0 || !templateData.list || templateData.list.length === 0) {
|
|
// 如果没有模板配置,使用原有的流转标签打印方式
|
|
console.warn('未找到物料标签模板配置!');
|
|
return;
|
|
}
|
|
|
|
// 找到默认模板(default_flag = '1')
|
|
let defaultTemplate = templateData.list.find(item => item.defaultFlag === '1');
|
|
if (!defaultTemplate && templateData.list.length > 0) {
|
|
// 如果没有设置默认模板,使用第一个
|
|
defaultTemplate = templateData.list[0];
|
|
}
|
|
|
|
if (!defaultTemplate) {
|
|
console.warn('未找到默认模板!');
|
|
return;
|
|
}
|
|
|
|
const labelNo = defaultTemplate.labelNo; // A001、A002、A003
|
|
|
|
// 2. 获取打印机(使用默认打印机)
|
|
const LODOP = getLodop();
|
|
if (!LODOP) {
|
|
this.$message.warning('无法连接到打印控件,跳过打印');
|
|
return;
|
|
}
|
|
|
|
// 获取默认打印机
|
|
const printerCount = LODOP.GET_PRINTER_COUNT();
|
|
if (printerCount === 0) {
|
|
this.$message.warning('未检测到打印机,跳过打印');
|
|
return;
|
|
}
|
|
|
|
const defaultPrinterName = LODOP.GET_PRINTER_NAME(0); // 使用第一个打印机作为默认打印机
|
|
|
|
// 3. 调用存储过程获取打印参数
|
|
const printDataList = [];
|
|
|
|
for (let i = 0; i < printList.length; i++) {
|
|
const item = printList[i];
|
|
const params = {
|
|
site: this.scheduleData.site,
|
|
buNo: this.scheduleData.buNo,
|
|
menuID: '104003006', // 过站采集功能ID
|
|
relatedOrderNo: this.scheduleData.orderNo,
|
|
relatedOrderLineNo: this.scheduleData.seqNo,
|
|
documentNo: '',
|
|
partNo: this.scheduleData.partNo,
|
|
labelNo: labelNo,
|
|
rollNo: item.rollNo
|
|
};
|
|
|
|
const {data: printData} = await callUspPartLabelTemplate(params);
|
|
if (printData && printData.code === 0 && printData.row) {
|
|
printDataList.push({
|
|
...printData.row,
|
|
labelNo: labelNo
|
|
});
|
|
} else {
|
|
console.warn(`获取卷号 ${item.rollNo} 的打印参数失败,使用原始数据`);
|
|
// 如果获取打印参数失败,使用原始数据
|
|
printDataList.push({
|
|
...item,
|
|
labelNo: labelNo
|
|
});
|
|
}
|
|
}
|
|
|
|
// 4. 执行打印
|
|
await this.executePrintWithTemplate(LODOP, printDataList, labelNo, defaultPrinterName);
|
|
|
|
this.$message.success('标签打印任务已发送!');
|
|
|
|
} catch (error) {
|
|
console.error('模板打印失败:', error);
|
|
// this.$message.warning('模板打印失败,使用默认流转标签打印');
|
|
// // 降级处理:使用原有的流转标签打印方式
|
|
// printSfdcLabel(printList);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 执行模板打印
|
|
* @param {Object} LODOP - 打印控件对象
|
|
* @param {Array} printDataList - 打印数据列表
|
|
* @param {String} labelNo - 标签模板编号 (A001/A002/A003)
|
|
* @param {String} printerName - 打印机名称
|
|
*/
|
|
async executePrintWithTemplate(LODOP, printDataList, labelNo, printerName) {
|
|
console.log('开始打印,标签数量:', printDataList.length, '标签数据:', printDataList);
|
|
|
|
// 循环打印每个标签(每个标签单独打印一次)
|
|
for (let i = 0; i < printDataList.length; i++) {
|
|
const printData = printDataList[i];
|
|
|
|
// 每个标签单独初始化一个打印任务
|
|
LODOP.PRINT_INIT('分卷标签打印_' + (i + 1));
|
|
|
|
// 设置打印模式
|
|
LODOP.SET_PRINT_MODE("PRINT_NOCOLLATE", true);
|
|
|
|
// 根据 labelNo 调用不同的打印方法(来自 labelPrintTemplates mixin)
|
|
// 注意:不需要 NEWPAGE,因为每个标签是独立的打印任务
|
|
if (labelNo === 'A001') {
|
|
await this.printLabelA001(LODOP, printData, false);
|
|
} else if (labelNo === 'A002') {
|
|
this.printLabelA002(LODOP, printData, false);
|
|
} else if (labelNo === 'A003') {
|
|
this.printLabelA003(LODOP, printData, false);
|
|
} else if (labelNo === 'A004') {
|
|
this.printLabelA004(LODOP, printData, false);
|
|
} else {
|
|
console.warn(`未知的标签模板:${labelNo},跳过该标签`);
|
|
continue;
|
|
}
|
|
|
|
// 执行打印
|
|
LODOP.PRINT();
|
|
|
|
console.log(`第${i + 1}张标签已发送打印, 卷号: ${printData.rollNo}`);
|
|
}
|
|
},
|
|
|
|
// 保存 默认配置 列
|
|
async saveMultiLanguage() {
|
|
// 保存页面 button label title 属性
|
|
let buttons = this.buttonList;
|
|
let labels = this.labelsList;
|
|
await saveButtonList(buttons)
|
|
await saveButtonList(labels)
|
|
},
|
|
|
|
getMultiLanguageList() {
|
|
//首先查询当前按钮的多语言
|
|
searchFunctionButtonList(this.queryButton).then(({data}) => {
|
|
if (data && data.code == 0 ) {
|
|
this.buttons = data.data
|
|
} else {
|
|
// saveButtonList(this.buttonList).then(({data}) => {
|
|
// })
|
|
}
|
|
});
|
|
//其次查询当前标签的多语言
|
|
searchFunctionButtonList(this.queryLabel).then(({data}) => {
|
|
if (data && data.code == 0 ) {
|
|
this.labels = data.data
|
|
} else {
|
|
// saveButtonList(this.buttonList).then(({data}) => {
|
|
// })
|
|
}
|
|
});
|
|
},
|
|
|
|
},
|
|
created() {
|
|
// this.factoryList()
|
|
// this.getLanguageList()
|
|
},
|
|
beforeDestroy() {
|
|
this.stopSplitRuleLockPolling()
|
|
this.stopExceptionRuleClock()
|
|
}
|
|
}
|
|
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
@import url('https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&display=swap');
|
|
|
|
/*调节页面button和input的上下间距*/
|
|
.customer-css .customer-button{
|
|
margin-top: 25px;
|
|
}
|
|
|
|
/* 固定载具清除按钮 */
|
|
.fixture-clear-btn {
|
|
cursor: pointer;
|
|
color: #C0C4CC;
|
|
&:hover {
|
|
color: #909399;
|
|
}
|
|
}
|
|
|
|
/*调节fieldset下的样式*/
|
|
.customer-fieldset .customer-item{
|
|
margin-top: -15px;
|
|
}
|
|
|
|
/*fieldset下table的样式*/
|
|
.customer-fieldset /deep/ .el-table__header th.is-leaf{
|
|
line-height: 16px;
|
|
}
|
|
|
|
/deep/ .customer-tab .el-tabs__content{
|
|
padding: 0px !important;
|
|
}
|
|
|
|
/* 良品数输入框样式 - 绿色加粗 */
|
|
.good-qty-input >>> .el-input__inner {
|
|
font-weight: bold !important;
|
|
color: #67c23a !important;
|
|
font-size: 12px !important;
|
|
}
|
|
|
|
::v-deep .good-qty-input .el-input__inner {
|
|
font-weight: bold !important;
|
|
color: #67c23a !important;
|
|
font-size: 12px !important;
|
|
}
|
|
|
|
/deep/ .good-qty-input .el-input__inner {
|
|
font-weight: bold !important;
|
|
color: #67c23a !important;
|
|
font-size: 12px !important;
|
|
}
|
|
|
|
/* 不良数输入框样式 - 红色加粗 */
|
|
.defect-qty-input >>> .el-input__inner {
|
|
font-weight: bold !important;
|
|
color: #f56c6c !important;
|
|
font-size: 12px !important;
|
|
}
|
|
|
|
::v-deep .defect-qty-input .el-input__inner {
|
|
font-weight: bold !important;
|
|
color: #f56c6c !important;
|
|
font-size: 12px !important;
|
|
}
|
|
|
|
/deep/ .defect-qty-input .el-input__inner {
|
|
font-weight: bold !important;
|
|
color: #f56c6c !important;
|
|
font-size: 12px !important;
|
|
}
|
|
|
|
/* 总数显示样式 */
|
|
.total-display {
|
|
font-weight: bold;
|
|
color: #303133;
|
|
font-size: 12px;
|
|
}
|
|
|
|
/* 备注列单元格样式 - 使用绝对定位填满 */
|
|
.customer-dialog >>> .el-table td.remark-column,
|
|
.customer-dialog >>> .el-table td.packing-list-column {
|
|
padding: 0 !important;
|
|
position: relative !important;
|
|
overflow: hidden !important;
|
|
}
|
|
|
|
.customer-dialog /deep/ .el-table td.remark-column,
|
|
.customer-dialog /deep/ .el-table td.packing-list-column {
|
|
padding: 0 !important;
|
|
position: relative !important;
|
|
overflow: hidden !important;
|
|
}
|
|
|
|
/* 备注包装器 - 绝对定位填满单元格 */
|
|
.customer-dialog >>> .remark-wrapper {
|
|
position: absolute !important;
|
|
top: 1px !important;
|
|
left: 1px !important;
|
|
right: 1px !important;
|
|
bottom: 1px !important;
|
|
width: 100% !important;
|
|
height: 100% !important;
|
|
box-sizing: border-box !important;
|
|
}
|
|
|
|
.customer-dialog /deep/ .remark-wrapper {
|
|
position: absolute !important;
|
|
top: 1px !important;
|
|
left: 1px !important;
|
|
right: 1px !important;
|
|
bottom: 1px !important;
|
|
width: 100% !important;
|
|
height: 100% !important;
|
|
box-sizing: border-box !important;
|
|
}
|
|
|
|
/* 备注textarea */
|
|
.customer-dialog >>> .remark-textarea {
|
|
width: 100% !important;
|
|
height: 100% !important;
|
|
}
|
|
|
|
.customer-dialog >>> .remark-textarea .el-textarea__inner {
|
|
border: none !important;
|
|
padding: 8px 10px !important;
|
|
border-radius: 0 !important;
|
|
resize: none !important;
|
|
width: 100% !important;
|
|
height: 100% !important;
|
|
box-sizing: border-box !important;
|
|
overflow-y: auto !important;
|
|
}
|
|
|
|
.customer-dialog /deep/ .remark-textarea {
|
|
width: 100% !important;
|
|
height: 100% !important;
|
|
}
|
|
|
|
.customer-dialog /deep/ .remark-textarea .el-textarea__inner {
|
|
border: none !important;
|
|
padding: 8px 10px !important;
|
|
border-radius: 0 !important;
|
|
resize: none !important;
|
|
width: 100% !important;
|
|
height: 100% !important;
|
|
box-sizing: border-box !important;
|
|
overflow-y: auto !important;
|
|
}
|
|
|
|
.customer-dialog /deep/ .el-dialog {
|
|
position: relative;
|
|
}
|
|
|
|
.exception-rule-mask-screen {
|
|
--mask-accent: #e01020;
|
|
--mask-accent-rgb: 224, 16, 32;
|
|
--mask-subtitle: #7a1020;
|
|
--mask-accent-dark: #b3001b;
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 4000;
|
|
background: #0d1317;
|
|
overflow: hidden;
|
|
pointer-events: all;
|
|
display: flex;
|
|
flex-direction: column;
|
|
font-family: 'Barlow Condensed', sans-serif;
|
|
}
|
|
|
|
.exception-rule-mask-screen.warning {
|
|
--mask-accent: #e6a817;
|
|
--mask-accent-rgb: 230, 168, 23;
|
|
--mask-subtitle: #7a5c10;
|
|
--mask-accent-dark: #a3780f;
|
|
}
|
|
|
|
.exception-rule-mask-screen.serious {
|
|
--mask-accent: #e01020;
|
|
--mask-accent-rgb: 224, 16, 32;
|
|
--mask-subtitle: #7a1020;
|
|
}
|
|
|
|
.exception-rule-bg-grid {
|
|
position: absolute;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
background-image:
|
|
linear-gradient(rgba(var(--mask-accent-rgb), 0.03) 1px, transparent 1px),
|
|
linear-gradient(90deg, rgba(var(--mask-accent-rgb), 0.03) 1px, transparent 1px);
|
|
background-size: 48px 48px;
|
|
z-index: 0;
|
|
}
|
|
|
|
.exception-rule-vignette {
|
|
position: absolute;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
background: radial-gradient(ellipse at center, transparent 40%, rgba(0, 0, 0, 0.75) 100%);
|
|
z-index: 0;
|
|
}
|
|
|
|
.exception-rule-scanline {
|
|
position: absolute;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
overflow: hidden;
|
|
z-index: 1;
|
|
}
|
|
|
|
.exception-rule-scanline .lines {
|
|
position: absolute;
|
|
inset: 0;
|
|
background-image: repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0, 0, 0, 0.18) 2px, rgba(0, 0, 0, 0.18) 4px);
|
|
background-size: 100% 4px;
|
|
}
|
|
|
|
.exception-rule-scanline .beam {
|
|
position: absolute;
|
|
left: 0;
|
|
right: 0;
|
|
height: 128px;
|
|
background: linear-gradient(
|
|
180deg,
|
|
transparent 0%,
|
|
rgba(var(--mask-accent-rgb), 0.035) 40%,
|
|
rgba(var(--mask-accent-rgb), 0.055) 50%,
|
|
rgba(var(--mask-accent-rgb), 0.035) 60%,
|
|
transparent 100%
|
|
);
|
|
animation: exception-rule-scan-beam 6s linear infinite;
|
|
}
|
|
|
|
.exception-rule-corners {
|
|
position: absolute;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
z-index: 2;
|
|
color: var(--mask-accent);
|
|
}
|
|
|
|
.exception-rule-corners .corner {
|
|
position: absolute;
|
|
width: 80px;
|
|
height: 80px;
|
|
}
|
|
|
|
.exception-rule-corners .corner.tl { top: 0; left: 0; }
|
|
.exception-rule-corners .corner.tr { top: 0; right: 0; }
|
|
.exception-rule-corners .corner.bl { bottom: 0; left: 0; }
|
|
.exception-rule-corners .corner.br { bottom: 0; right: 0; }
|
|
|
|
.exception-rule-corners .corner svg {
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: visible;
|
|
}
|
|
|
|
.exception-rule-corners .corner.tr svg { transform: rotate(90deg); }
|
|
.exception-rule-corners .corner.bl svg { transform: rotate(-90deg); }
|
|
.exception-rule-corners .corner.br svg { transform: rotate(180deg); }
|
|
|
|
.exception-rule-corners .corner .arc {
|
|
transform-origin: 4px 4px;
|
|
animation: exception-rule-spin-cw 4s linear infinite;
|
|
}
|
|
|
|
.exception-rule-statusbar {
|
|
position: relative;
|
|
z-index: 10;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 8px 24px;
|
|
font-size: 12px;
|
|
line-height: 1.2;
|
|
border-bottom: 1px solid rgba(var(--mask-accent-rgb), 0.2);
|
|
color: #8aafc8;
|
|
background: rgba(8, 11, 13, 0.65);
|
|
font-family: 'JetBrains Mono', monospace;
|
|
}
|
|
|
|
.exception-rule-statusbar .left,
|
|
.exception-rule-statusbar .right {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
}
|
|
|
|
.exception-rule-statusbar .mes {
|
|
color: var(--mask-accent);
|
|
font-weight: 700;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
|
|
.exception-rule-statusbar .lockdown {
|
|
color: var(--mask-accent);
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.exception-rule-statusbar .dot {
|
|
display: inline-block;
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
background: var(--mask-accent);
|
|
margin-right: 6px;
|
|
animation: exception-rule-blink 1s step-end infinite;
|
|
}
|
|
|
|
.exception-rule-statusbar .clock {
|
|
color: #dde3e8;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.exception-rule-main {
|
|
position: relative;
|
|
z-index: 10;
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: flex-start;
|
|
padding: 148px 32px 0;
|
|
gap: 32px;
|
|
min-height: 0;
|
|
}
|
|
|
|
.exception-rule-alert-icon {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 180px;
|
|
height: 180px;
|
|
margin-bottom: 30px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.exception-rule-alert-icon .pulse-ring {
|
|
position: absolute;
|
|
inset: 0;
|
|
border-radius: 50%;
|
|
border: 2px solid var(--mask-accent);
|
|
opacity: 0;
|
|
animation: exception-rule-pulse-ring 2s ease-out infinite;
|
|
}
|
|
|
|
.exception-rule-alert-icon .pulse-ring.d0 { animation-delay: 0s; }
|
|
.exception-rule-alert-icon .pulse-ring.d1 { animation-delay: 0.67s; }
|
|
.exception-rule-alert-icon .pulse-ring.d2 { animation-delay: 1.33s; }
|
|
|
|
.exception-rule-alert-icon .alert-glow {
|
|
position: absolute;
|
|
inset: 0;
|
|
border-radius: 50%;
|
|
box-shadow: 0 0 40px 8px rgba(var(--mask-accent-rgb), 0.35), 0 0 80px 20px rgba(var(--mask-accent-rgb), 0.12);
|
|
animation: exception-rule-glow-breathe 1.5s ease-in-out infinite;
|
|
}
|
|
|
|
.exception-rule-alert-icon .alert-core {
|
|
position: relative;
|
|
z-index: 10;
|
|
width: 110px;
|
|
height: 110px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: radial-gradient(circle at 35% 35%, rgba(var(--mask-accent-rgb), 0.18), rgba(26, 22, 4, 0.95));
|
|
border: 2px solid rgba(var(--mask-accent-rgb), 0.8);
|
|
box-shadow: inset 0 0 20px rgba(var(--mask-accent-rgb), 0.3);
|
|
color: var(--mask-accent);
|
|
animation: exception-rule-glow-breathe 1.5s ease-in-out infinite;
|
|
}
|
|
|
|
.exception-rule-title-block {
|
|
text-align: center;
|
|
margin-top: 30px;
|
|
}
|
|
|
|
.exception-rule-title-cn {
|
|
font-family: 'Barlow Condensed', sans-serif;
|
|
font-size: 72px;
|
|
font-weight: 800;
|
|
letter-spacing: 0.12em;
|
|
line-height: 1;
|
|
color: var(--mask-accent);
|
|
text-shadow: 0 0 40px rgba(var(--mask-accent-rgb), 0.55), 0 0 80px rgba(var(--mask-accent-rgb), 0.22);
|
|
}
|
|
|
|
.exception-rule-title-en {
|
|
font-family: 'Barlow Condensed', sans-serif;
|
|
margin-top: 4px;
|
|
font-size: 30px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.35em;
|
|
color: var(--mask-subtitle);
|
|
}
|
|
|
|
.exception-rule-title-rule {
|
|
margin: 12px auto 0;
|
|
height: 1px;
|
|
background: linear-gradient(90deg, transparent, rgba(var(--mask-accent-rgb), 0.6), transparent);
|
|
max-width: 360px;
|
|
}
|
|
|
|
.exception-rule-title-lock {
|
|
margin-top: 12px;
|
|
font-family: 'Barlow Condensed', sans-serif;
|
|
font-size: 20px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.3em;
|
|
text-transform: uppercase;
|
|
color: #dde3e8;
|
|
}
|
|
|
|
.exception-rule-info-cards {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 16px;
|
|
width: 100%;
|
|
max-width: 768px;
|
|
}
|
|
|
|
.exception-rule-info-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
padding: 12px 16px;
|
|
background: rgba(10, 16, 22, 0.7);
|
|
border: 1px solid rgba(var(--mask-accent-rgb), 0.18);
|
|
}
|
|
|
|
.exception-rule-info-card .label {
|
|
font-size: 12px;
|
|
line-height: 1.2;
|
|
letter-spacing: 0.05em;
|
|
text-transform: uppercase;
|
|
color: #8aafc8;
|
|
font-family: 'JetBrains Mono', monospace;
|
|
}
|
|
|
|
.exception-rule-info-card .value {
|
|
font-family: 'Barlow Condensed', sans-serif;
|
|
font-size: 20px;
|
|
line-height: 1.15;
|
|
font-weight: 700;
|
|
letter-spacing: 0.02em;
|
|
color: #dde3e8;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.exception-rule-info-card .value.hl {
|
|
color: var(--mask-accent);
|
|
text-shadow: 0 0 12px rgba(var(--mask-accent-rgb), 0.5);
|
|
animation: exception-rule-glow-breathe 1.5s ease-in-out infinite;
|
|
}
|
|
|
|
.exception-rule-info-card.work-order-card .work-order-value-line {
|
|
display: flex;
|
|
align-items: flex-end;
|
|
justify-content: space-between;
|
|
gap: 10px;
|
|
width: 100%;
|
|
}
|
|
|
|
.exception-rule-info-card.work-order-card .work-order-value-line .value {
|
|
flex: 1;
|
|
min-width: 0;
|
|
text-align: left;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.exception-rule-info-card.work-order-card .work-order-seq {
|
|
display: inline-block;
|
|
min-width: 5ch;
|
|
text-align: right;
|
|
white-space: nowrap;
|
|
font-family: 'Barlow Condensed', sans-serif;
|
|
font-size: 20px;
|
|
line-height: 1.15;
|
|
font-weight: 700;
|
|
letter-spacing: 0.02em;
|
|
color: #dde3e8;
|
|
}
|
|
|
|
.exception-rule-flow-steps {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0;
|
|
width: 100%;
|
|
max-width: 768px;
|
|
}
|
|
|
|
.exception-rule-flow-steps .flow-step {
|
|
flex: 1;
|
|
text-align: center;
|
|
position: relative;
|
|
padding: 10px 0;
|
|
}
|
|
|
|
.exception-rule-flow-steps .flow-step .dot {
|
|
width: 16px;
|
|
height: 16px;
|
|
border-radius: 50%;
|
|
margin: 0 auto 8px;
|
|
border: 2px solid rgba(var(--mask-accent-rgb), 0.5);
|
|
}
|
|
|
|
.exception-rule-flow-steps .flow-step.done .dot {
|
|
background: var(--mask-accent);
|
|
border-color: var(--mask-accent);
|
|
}
|
|
|
|
.exception-rule-flow-steps .flow-step.active .dot {
|
|
background: transparent;
|
|
border-color: var(--mask-accent);
|
|
box-shadow: 0 0 8px rgba(var(--mask-accent-rgb), 0.5);
|
|
animation: exception-rule-blink 1.2s ease-in-out infinite;
|
|
}
|
|
|
|
.exception-rule-flow-steps .flow-step .label {
|
|
font-size: 12px;
|
|
color: #5a7080;
|
|
letter-spacing: 0.05em;
|
|
font-family: 'JetBrains Mono', monospace;
|
|
}
|
|
|
|
.exception-rule-flow-steps .flow-step.done .label { color: #8aafc8; }
|
|
.exception-rule-flow-steps .flow-step.active .label {
|
|
color: var(--mask-accent);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.exception-rule-flow-steps .flow-line {
|
|
width: 40px;
|
|
height: 1px;
|
|
background: rgba(var(--mask-accent-rgb), 0.2);
|
|
margin: 0 -4px;
|
|
align-self: flex-start;
|
|
margin-top: 6px;
|
|
}
|
|
|
|
.exception-rule-flow-steps .flow-line.done {
|
|
background: rgba(var(--mask-accent-rgb), 0.5);
|
|
}
|
|
|
|
.exception-rule-bottombar {
|
|
position: relative;
|
|
z-index: 10;
|
|
padding: 16px 32px;
|
|
border-top: 1px solid rgba(var(--mask-accent-rgb), 0.2);
|
|
background: rgba(8, 11, 13, 0.85);
|
|
}
|
|
|
|
.exception-rule-bottombar .inner {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
max-width: 1024px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.exception-rule-bottombar .left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
.exception-rule-bottombar .left svg {
|
|
color: var(--mask-accent-dark);
|
|
}
|
|
|
|
.exception-rule-bottombar .left .ttl {
|
|
font-size: 12px;
|
|
letter-spacing: 0.2em;
|
|
text-transform: uppercase;
|
|
color: #8aafc8;
|
|
font-family: 'JetBrains Mono', monospace;
|
|
}
|
|
|
|
.exception-rule-quality-cards {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
}
|
|
|
|
.exception-rule-quality-cards .quality-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 4px;
|
|
padding: 12px 24px;
|
|
min-width: 140px;
|
|
border: 1px solid rgba(var(--mask-accent-rgb), 0.3);
|
|
background: rgba(var(--mask-accent-rgb), 0.04);
|
|
}
|
|
|
|
.exception-rule-quality-cards .quality-card .qlabel {
|
|
font-size: 11px;
|
|
line-height: 1.2;
|
|
font-weight: 600;
|
|
letter-spacing: 0.15em;
|
|
text-transform: uppercase;
|
|
color: #8aafc8;
|
|
font-family: 'JetBrains Mono', monospace;
|
|
}
|
|
|
|
.exception-rule-quality-cards .quality-card .qvalue {
|
|
font-size: 22px;
|
|
line-height: 1;
|
|
font-weight: 700;
|
|
color: #dde3e8;
|
|
letter-spacing: 0.04em;
|
|
}
|
|
|
|
.exception-rule-quality-cards .quality-card .qvalue.hl {
|
|
color: var(--mask-accent);
|
|
text-shadow: 0 0 12px rgba(var(--mask-accent-rgb), 0.4);
|
|
}
|
|
|
|
.exception-rule-bottombar .right {
|
|
text-align: right;
|
|
font-size: 12px;
|
|
line-height: 1.25;
|
|
color: #8aafc8;
|
|
font-family: 'JetBrains Mono', monospace;
|
|
}
|
|
|
|
.exception-rule-bottombar .right .auth {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
gap: 4px;
|
|
margin-top: 2px;
|
|
color: #1e3040;
|
|
}
|
|
|
|
@keyframes exception-rule-spin-cw {
|
|
from { transform: rotate(0deg); }
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
@keyframes exception-rule-scan-beam {
|
|
from { top: -128px; }
|
|
to { top: 100%; }
|
|
}
|
|
|
|
@keyframes exception-rule-pulse-ring {
|
|
0% {
|
|
transform: scale(0.7);
|
|
opacity: 0.7;
|
|
}
|
|
100% {
|
|
transform: scale(2.2);
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
@keyframes exception-rule-glow-breathe {
|
|
0%, 100% { opacity: 0.8; }
|
|
50% { opacity: 1; }
|
|
}
|
|
|
|
@keyframes exception-rule-blink {
|
|
0%,
|
|
100% {
|
|
opacity: 1;
|
|
}
|
|
50% {
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
/* 分卷不良原因对话框 */
|
|
.roll-defect-summary {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 12px;
|
|
padding: 8px 12px;
|
|
background: #f5f7fa;
|
|
border-radius: 4px;
|
|
color: #606266;
|
|
}
|
|
|
|
.roll-defect-form /deep/ .el-form-item {
|
|
margin-right: 12px;
|
|
}
|
|
|
|
.roll-defect-form .defect-code-label a {
|
|
color: #409EFF;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.roll-defect-form /deep/ .defect-code-input .el-input__inner {
|
|
color: #409EFF;
|
|
font-weight: 600;
|
|
}
|
|
|
|
</style>
|
|
|
|
<style lang="scss">
|
|
/* 全局样式 - 良品数和不良数输入框颜色 */
|
|
.good-qty-input .el-input__inner {
|
|
font-weight: bold !important;
|
|
color: #67c23a !important;
|
|
font-size: 16px !important;
|
|
}
|
|
|
|
.defect-qty-input .el-input__inner {
|
|
font-weight: bold !important;
|
|
color: #f56c6c !important;
|
|
font-size: 16px !important;
|
|
}
|
|
|
|
/* 备注列样式 - 只针对创建分卷对话框 */
|
|
.customer-css .el-table td.remark-column,
|
|
.customer-css .el-table td.packing-list-column {
|
|
padding: 0 !important;
|
|
position: relative !important;
|
|
overflow: hidden !important;
|
|
}
|
|
|
|
/* 备注包装器 - 填满整个单元格 */
|
|
.customer-css .remark-wrapper {
|
|
position: absolute !important;
|
|
top: 1px !important;
|
|
left: 1px !important;
|
|
right: 1px !important;
|
|
bottom: 1px !important;
|
|
width: 100% !important;
|
|
height: 100% !important;
|
|
box-sizing: border-box !important;
|
|
}
|
|
|
|
/* 备注textarea容器 */
|
|
.customer-css .remark-textarea {
|
|
width: 100% !important;
|
|
height: 100% !important;
|
|
}
|
|
|
|
.customer-css .remark-textarea .el-textarea__inner {
|
|
border: none !important;
|
|
padding: 8px 10px !important;
|
|
border-radius: 0 !important;
|
|
resize: none !important;
|
|
width: 100% !important;
|
|
height: 100% !important;
|
|
box-sizing: border-box !important;
|
|
overflow-y: auto !important;
|
|
}
|
|
|
|
</style>
|